New JSON dump format

Unified across VMA and D3D12MA.
Updated Python script for visualization - now called GpuMemDumpVis.py.

Also a fix for bug in EXTENSIVE defragmentation algorithm - see #232

Code by @medranSolus
This commit is contained in:
Adam Sawicki 2022-03-24 11:50:34 +01:00
parent 6fbb28e3a8
commit 3c6470cf45
22 changed files with 1205 additions and 712 deletions

View File

@ -59,7 +59,7 @@ Additional features:
- Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated blocks, number of allocations etc. - globally, per memory heap, and per memory type.
- Debug annotations: Associate custom `void* pUserData` and debug `char* pName` with each allocation.
- JSON dump: Obtain a string in JSON format with detailed map of internal state, including list of allocations, their string names, and gaps between them.
- Convert this JSON dump into a picture to visualize your memory. See [tools/VmaDumpVis](tools/VmaDumpVis/README.md).
- Convert this JSON dump into a picture to visualize your memory. See [tools/GpuMemDumpVis](tools/GpuMemDumpVis/README.md).
- Debugging incorrect memory usage: Enable initialization of all allocated memory with a bit pattern to detect usage of uninitialized or freed memory. Enable validation of a magic number after every allocation to detect out-of-bounds memory corruption.
- Support for interoperability with OpenGL.
- Virtual allocator: Interface for using core allocation algorithm to allocate any custom data, e.g. pieces of one large buffer.

View File

@ -5765,41 +5765,29 @@ static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedSta
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);
json.WriteString("BlockBytes");
json.WriteNumber(stat.statistics.blockBytes);
json.WriteString("AllocationBytes");
json.WriteNumber(stat.statistics.allocationBytes);
if (stat.statistics.allocationCount > 1)
{
json.WriteString("AllocationSize");
json.BeginObject(true);
json.WriteString("Min");
json.WriteString("AllocationSizeMin");
json.WriteNumber(stat.allocationSizeMin);
json.WriteString("Max");
json.WriteString("AllocationSizeMax");
json.WriteNumber(stat.allocationSizeMax);
json.EndObject();
}
if (stat.unusedRangeCount > 1)
{
json.WriteString("UnusedRangeSize");
json.BeginObject(true);
json.WriteString("Min");
json.WriteString("UnusedRangeSizeMin");
json.WriteNumber(stat.unusedRangeSizeMin);
json.WriteString("Max");
json.WriteString("UnusedRangeSizeMax");
json.WriteNumber(stat.unusedRangeSizeMax);
json.EndObject();
}
json.EndObject();
}
#endif // _VMA_JSON_WRITER
@ -6348,8 +6336,7 @@ public:
virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;
#if VMA_STATS_STRING_ENABLED
// mapRefCount == UINT32_MAX means unspecified.
virtual void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const = 0;
virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
#endif
// Tries to find a place for suballocation with given parameters inside this block.
@ -6393,8 +6380,7 @@ protected:
void PrintDetailedMap_Begin(class VmaJsonWriter& json,
VkDeviceSize unusedBytes,
size_t allocationCount,
size_t unusedRangeCount,
uint32_t mapRefCount) const;
size_t unusedRangeCount) const;
void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
VkDeviceSize offset, VkDeviceSize size, void* userData) const;
void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
@ -6448,10 +6434,8 @@ void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount, uint32_t mapRefCount) const
VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const
{
json.BeginObject();
json.WriteString("TotalBytes");
json.WriteNumber(GetSize());
@ -6459,16 +6443,10 @@ void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
json.WriteNumber(unusedBytes);
json.WriteString("Allocations");
json.WriteNumber((uint64_t)allocationCount);
json.WriteNumber(allocationCount);
json.WriteString("UnusedRanges");
json.WriteNumber((uint64_t)unusedRangeCount);
if(mapRefCount != UINT32_MAX)
{
json.WriteString("MapRefCount");
json.WriteNumber(mapRefCount);
}
json.WriteNumber(unusedRangeCount);
json.WriteString("Suballocations");
json.BeginArray();
@ -6484,15 +6462,11 @@ void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
if (IsVirtual())
{
json.WriteString("Type");
json.WriteString("VirtualAllocation");
json.WriteString("Size");
json.WriteNumber(size);
if (userData != VMA_NULL)
if (userData)
{
json.WriteString("UserData");
json.WriteString("CustomData");
json.BeginString();
json.ContinueString_Pointer(userData);
json.EndString();
@ -6526,7 +6500,6 @@ void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
{
json.EndArray();
json.EndObject();
}
#endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_BLOCK_METADATA_FUNCTIONS
@ -7631,7 +7604,7 @@ public:
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
void PrintDetailedMap(class VmaJsonWriter& json) const override;
#endif
bool CreateAllocationRequest(
@ -8218,7 +8191,7 @@ void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
{
const VkDeviceSize size = GetSize();
const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
@ -8380,7 +8353,7 @@ void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json, uint32
}
const VkDeviceSize unusedBytes = size - usedBytes;
PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount, mapRefCount);
PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
// SECOND PASS
lastOffset = 0;
@ -9988,7 +9961,7 @@ public:
void AddStatistics(VmaStatistics& inoutStats) const override;
#if VMA_STATS_STRING_ENABLED
void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;
void PrintDetailedMap(class VmaJsonWriter& json) const override;
#endif
bool CreateAllocationRequest(
@ -10261,7 +10234,7 @@ void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const
}
#if VMA_STATS_STRING_ENABLED
void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const
void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const
{
size_t blockCount = m_AllocCount + m_BlocksFreeCount;
VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());
@ -10278,12 +10251,10 @@ void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json, uint32_t
VmaClearDetailedStatistics(stats);
AddDetailedStatistics(stats);
PrintDetailedMap_Begin(
json,
PrintDetailedMap_Begin(json,
stats.statistics.blockBytes - stats.statistics.allocationBytes,
stats.statistics.allocationCount,
stats.unusedRangeCount,
mapRefCount);
stats.unusedRangeCount);
for (; i < blockCount; ++i)
{
@ -11380,8 +11351,9 @@ void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb)
if (detailedMap)
{
json.WriteString("Details");
m_Metadata->PrintDetailedMap(json,
UINT32_MAX); // mapRefCount
json.BeginObject();
m_Metadata->PrintDetailedMap(json);
json.EndObject();
}
json.EndObject();
@ -12280,10 +12252,12 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString("Size");
json.WriteNumber(m_Size);
json.WriteString("Usage");
json.WriteNumber(m_BufferImageUsage);
if (m_pUserData != VMA_NULL)
{
json.WriteString("UserData");
json.WriteString("CustomData");
json.BeginString();
json.ContinueString_Pointer(m_pUserData);
json.EndString();
@ -12293,12 +12267,6 @@ void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
json.WriteString("Name");
json.WriteString(m_pName);
}
if (m_BufferImageUsage != 0)
{
json.WriteString("Usage");
json.WriteNumber(m_BufferImageUsage);
}
}
#endif // VMA_STATS_STRING_ENABLED
@ -12951,50 +12919,7 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
{
VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
if (IsCustomPool())
{
const char* poolName = m_hParentPool->GetName();
if (poolName != VMA_NULL && poolName[0] != '\0')
{
json.WriteString("Name");
json.WriteString(poolName);
}
json.WriteString("MemoryTypeIndex");
json.WriteNumber(m_MemoryTypeIndex);
json.WriteString("BlockSize");
json.WriteNumber(m_PreferredBlockSize);
json.WriteString("BlockCount");
json.BeginObject(true);
if (m_MinBlockCount > 0)
{
json.WriteString("Min");
json.WriteNumber((uint64_t)m_MinBlockCount);
}
if (m_MaxBlockCount < SIZE_MAX)
{
json.WriteString("Max");
json.WriteNumber((uint64_t)m_MaxBlockCount);
}
json.WriteString("Cur");
json.WriteNumber((uint64_t)m_Blocks.size());
json.EndObject();
if (m_Algorithm != 0)
{
json.WriteString("Algorithm");
json.WriteString(VmaAlgorithmToStr(m_Algorithm));
}
}
else
{
json.WriteString("PreferredBlockSize");
json.WriteNumber(m_PreferredBlockSize);
}
json.WriteString("Blocks");
json.BeginObject();
for (size_t i = 0; i < m_Blocks.size(); ++i)
{
@ -13002,7 +12927,12 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
json.ContinueString(m_Blocks[i]->GetId());
json.EndString();
m_Blocks[i]->m_pMetadata->PrintDetailedMap(json, m_Blocks[i]->GetMapRefCount());
json.BeginObject();
json.WriteString("MapRefCount");
json.WriteNumber(m_Blocks[i]->GetMapRefCount());
m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
json.EndObject();
}
json.EndObject();
}
@ -13303,9 +13233,15 @@ VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMo
StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];
if (state.firstFreeBlock != SIZE_MAX)
{
state.firstFreeBlock -= prevCount - currentCount;
if (state.firstFreeBlock != 0)
state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();
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;
}
}
}
@ -13350,7 +13286,10 @@ VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMo
{
if (i < state.firstFreeBlock - 1)
{
VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
if (state.firstFreeBlock > 1)
VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);
else
--state.firstFreeBlock;
}
}
swapped = true;
@ -15968,89 +15907,90 @@ uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()
#if VMA_STATS_STRING_ENABLED
void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
{
bool dedicatedAllocationsStarted = false;
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
json.WriteString("DefaultPools");
json.BeginObject();
{
VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
if(!dedicatedAllocList.IsEmpty())
{
if(dedicatedAllocationsStarted == false)
{
dedicatedAllocationsStarted = true;
json.WriteString("DedicatedAllocations");
json.BeginObject();
}
json.BeginString("Type ");
json.ContinueString(memTypeIndex);
json.EndString();
dedicatedAllocList.BuildStatsString(json);
}
}
if(dedicatedAllocationsStarted)
{
json.EndObject();
}
{
bool allocationsStarted = false;
for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
{
VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];
if(pBlockVector != VMA_NULL)
VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];
if (pBlockVector != VMA_NULL)
{
if (pBlockVector->IsEmpty() == false)
{
if (allocationsStarted == false)
{
allocationsStarted = true;
json.WriteString("DefaultPools");
json.BeginObject();
}
json.BeginString("Type ");
json.ContinueString(memTypeIndex);
json.EndString();
json.BeginObject();
pBlockVector->PrintDetailedMap(json);
json.EndObject();
}
}
}
if(allocationsStarted)
{
json.EndObject();
}
}
// Custom pools
{
VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
if(!m_Pools.IsEmpty())
{
json.WriteString("Pools");
json.BeginObject();
for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
{
json.BeginString();
json.ContinueString(pool->GetId());
json.BeginString("Type ");
json.ContinueString(memTypeIndex);
json.EndString();
json.BeginObject();
pool->m_BlockVector.PrintDetailedMap(json);
if (!pool->m_DedicatedAllocations.IsEmpty())
{
json.WriteString("PreferredBlockSize");
json.WriteNumber(pBlockVector->GetPreferredBlockSize());
json.WriteString("Blocks");
pBlockVector->PrintDetailedMap(json);
json.WriteString("DedicatedAllocations");
pool->m_DedicatedAllocations.BuildStatsString(json);
dedicatedAllocList.BuildStatsString(json);
}
json.EndObject();
}
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(index++);
if (pool->GetName())
{
json.WriteString(" - ");
json.WriteString(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 // VMA_STATS_STRING_ENABLED
#endif // _VMA_ALLOCATOR_T_FUNCTIONS
@ -16161,127 +16101,176 @@ VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(
VmaStringBuilder sb(allocator->GetAllocationCallbacks());
{
VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
json.BeginObject();
VmaBudget budgets[VK_MAX_MEMORY_HEAPS];
allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());
VmaTotalStatistics stats;
allocator->CalculateStatistics(&stats);
json.WriteString("Total");
VmaPrintDetailedStatistics(json, stats.total);
for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
json.BeginObject();
{
json.BeginString("Heap ");
json.ContinueString(heapIndex);
json.EndString();
json.BeginObject();
json.WriteString("Size");
json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
json.WriteString("Flags");
json.BeginArray(true);
if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
{
json.WriteString("DEVICE_LOCAL");
}
json.EndArray();
json.WriteString("Budget");
json.WriteString("General");
json.BeginObject();
{
json.WriteString("BlockBytes");
json.WriteNumber(budgets[heapIndex].statistics.blockBytes);
json.WriteString("AllocationBytes");
json.WriteNumber(budgets[heapIndex].statistics.allocationBytes);
json.WriteString("BlockCount");
json.WriteNumber(budgets[heapIndex].statistics.blockCount);
json.WriteString("AllocationCount");
json.WriteNumber(budgets[heapIndex].statistics.allocationCount);
json.WriteString("Usage");
json.WriteNumber(budgets[heapIndex].usage);
json.WriteString("Budget");
json.WriteNumber(budgets[heapIndex].budget);
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_API_VERSION_MAJOR(deviceProperties.apiVersion));
json.ContinueString(".");
json.ContinueString(VK_API_VERSION_MINOR(deviceProperties.apiVersion));
json.ContinueString(".");
json.ContinueString(VK_API_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();
if(stats.memoryHeap[heapIndex].statistics.blockCount > 0)
}
{
json.WriteString("Total");
VmaPrintDetailedStatistics(json, stats.total);
}
{
json.WriteString("MemoryInfo");
json.BeginObject();
{
json.WriteString("Stats");
VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);
}
for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
{
if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
{
json.BeginString("Type ");
json.ContinueString(typeIndex);
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
json.WriteString("Flags");
json.BeginArray(true);
VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
{
json.WriteString("DEVICE_LOCAL");
}
if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
{
json.WriteString("HOST_VISIBLE");
}
if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
{
json.WriteString("HOST_COHERENT");
}
if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
{
json.WriteString("HOST_CACHED");
}
if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
{
json.WriteString("LAZILY_ALLOCATED");
}
#if VMA_VULKAN_VERSION >= 1001000
if((flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0)
{
json.WriteString("PROTECTED");
}
#endif // #if VMA_VULKAN_VERSION >= 1001000
#if VK_AMD_device_coherent_memory
if((flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)
{
json.WriteString("DEVICE_COHERENT");
}
if((flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) != 0)
{
json.WriteString("DEVICE_UNCACHED");
}
#endif // #if VK_AMD_device_coherent_memory
json.EndArray();
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();
if(stats.memoryType[typeIndex].statistics.blockCount > 0)
{
json.WriteString("Stats");
VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);
}
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)
{
if (detailedMap == VK_TRUE)
allocator->PrintDetailedMap(json);
}
json.EndObject();
}

View File

@ -1653,6 +1653,168 @@ static void ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount)
});
}
static void TestJson()
{
wprintf(L"Test JSON\n");
std::vector<VmaPool> pools;
std::vector<VmaAllocation> allocs;
VmaAllocationCreateInfo allocCreateInfo = {};
VkBufferCreateInfo buffCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
buffCreateInfo.size = 1024;
buffCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imgCreateInfo.extent.depth = 1;
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
imgCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
VkMemoryRequirements memReq = {};
{
VkBuffer dummyBuffer = VK_NULL_HANDLE;
TEST(vkCreateBuffer(g_hDevice, &buffCreateInfo, g_Allocs, &dummyBuffer) == VK_SUCCESS && dummyBuffer);
vkGetBufferMemoryRequirements(g_hDevice, dummyBuffer, &memReq);
vkDestroyBuffer(g_hDevice, dummyBuffer, g_Allocs);
}
// Select if using custom pool or default
for (uint8_t poolType = 0; poolType < 2; ++poolType)
{
// Select different memoryTypes
for (uint8_t memType = 0; memType < 2; ++memType)
{
switch (memType)
{
case 0:
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DONT_BIND_BIT;
break;
case 1:
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DONT_BIND_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
break;
}
switch (poolType)
{
case 0:
allocCreateInfo.pool = nullptr;
break;
case 1:
{
VmaPoolCreateInfo poolCreateInfo = {};
TEST(vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &buffCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex) == VK_SUCCESS);
VmaPool pool;
TEST(vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool) == VK_SUCCESS);
allocCreateInfo.pool = pool;
pools.emplace_back(pool);
break;
}
}
// Select different allocation flags
for (uint8_t allocFlag = 0; allocFlag < 2; ++allocFlag)
{
switch (allocFlag)
{
case 1:
allocCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
break;
}
// Select different alloc types (block, buffer, texture, etc.)
for (uint8_t allocType = 0; allocType < 4; ++allocType)
{
// Select different data stored in the allocation
for (uint8_t data = 0; data < 4; ++data)
{
VmaAllocation alloc = nullptr;
switch (allocType)
{
case 0:
{
VmaAllocationCreateInfo localCreateInfo = allocCreateInfo;
switch (memType)
{
case 0:
localCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
break;
case 1:
localCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
break;
}
TEST(vmaAllocateMemory(g_hAllocator, &memReq, &localCreateInfo, &alloc, nullptr) == VK_SUCCESS);
break;
}
case 1:
{
VkBuffer buffer;
TEST(vmaCreateBuffer(g_hAllocator, &buffCreateInfo, &allocCreateInfo, &buffer, &alloc, nullptr) == VK_SUCCESS);
vkDestroyBuffer(g_hDevice, buffer, g_Allocs);
break;
}
case 2:
{
imgCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
imgCreateInfo.extent.width = 512;
imgCreateInfo.extent.height = 1;
VkImage image;
TEST(vmaCreateImage(g_hAllocator, &imgCreateInfo, &allocCreateInfo, &image, &alloc, nullptr) == VK_SUCCESS);
vkDestroyImage(g_hDevice, image, g_Allocs);
break;
}
case 3:
{
imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imgCreateInfo.extent.width = 1024;
imgCreateInfo.extent.height = 512;
VkImage image;
TEST(vmaCreateImage(g_hAllocator, &imgCreateInfo, &allocCreateInfo, &image, &alloc, nullptr) == VK_SUCCESS);
vkDestroyImage(g_hDevice, image, g_Allocs);
break;
}
}
switch (data)
{
case 1:
vmaSetAllocationUserData(g_hAllocator, alloc, (void*)16112007);
break;
case 2:
vmaSetAllocationName(g_hAllocator, alloc, "SHEPURD");
break;
case 3:
vmaSetAllocationUserData(g_hAllocator, alloc, (void*)26012010);
vmaSetAllocationName(g_hAllocator, alloc, "JOKER");
break;
}
allocs.emplace_back(alloc);
}
}
}
}
}
SaveAllocatorStatsToFile(L"JSON_VULKAN.json");
for (auto& alloc : allocs)
vmaFreeMemory(g_hAllocator, alloc);
for (auto& pool : pools)
vmaDestroyPool(g_hAllocator, pool);
}
void TestDefragmentationSimple()
{
wprintf(L"Test defragmentation simple\n");
@ -3154,8 +3316,8 @@ static void TestVirtualBlocks()
vmaBuildVirtualBlockStatsString(block, &json, VK_TRUE);
{
std::string str(json);
TEST( str.find("\"UserData\": \"0000000000000001\"") != std::string::npos );
TEST( str.find("\"UserData\": \"0000000000000002\"") != std::string::npos );
TEST( str.find("\"CustomData\": \"0000000000000001\"") != std::string::npos );
TEST( str.find("\"CustomData\": \"0000000000000002\"") != std::string::npos );
}
vmaFreeVirtualBlockStatsString(block, json);
#endif
@ -7821,6 +7983,7 @@ void Test()
TestDebugMargin();
TestDebugMarginNotInVirtualAllocator();
#else
TestJson();
TestBasics();
TestVirtualBlocks();
TestVirtualBlocksAlgorithms();

View File

@ -0,0 +1,334 @@
#
# Copyright (c) 2018-2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# 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
# THE SOFTWARE.
#
import argparse
import json
from PIL import Image, ImageDraw, ImageFont
PROGRAM_VERSION = 'Vulkan/D3D12 Memory Allocator Dump Visualization 3.0.0'
IMG_WIDTH = 1200
IMG_MARGIN = 8
TEXT_MARGIN = 4
FONT_SIZE = 10
MAP_SIZE = 24
COLOR_TEXT_H1 = (0, 0, 0, 255)
COLOR_TEXT_H2 = (150, 150, 150, 255)
COLOR_OUTLINE = (155, 155, 155, 255)
COLOR_OUTLINE_HARD = (0, 0, 0, 255)
COLOR_GRID_LINE = (224, 224, 224, 255)
currentApi = ""
data = {}
def ParseArgs():
argParser = argparse.ArgumentParser(description='Visualization of Vulkan/D3D12 Memory Allocator JSON dump.')
argParser.add_argument('DumpFile', help='Path to source JSON file with memory dump created by Vulkan/D3D12 Memory Allocator library')
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)')
return argParser.parse_args()
def GetDataForMemoryPool(poolTypeName):
global data
if poolTypeName in data:
return data[poolTypeName]
else:
newPoolData = {'DedicatedAllocations':[], 'Blocks':[], 'CustomPools':{}}
data[poolTypeName] = newPoolData
return newPoolData
def ProcessBlock(poolData, block):
blockInfo = {'ID': block[0], 'Size': int(block[1]['TotalBytes']), 'Suballocations':[]}
for alloc in block[1]['Suballocations']:
allocData = {'Type': alloc['Type'], 'Size': int(alloc['Size']), 'Usage': int(alloc['Usage']) if 'Usage' in alloc else 0 }
blockInfo['Suballocations'].append(allocData)
poolData['Blocks'].append(blockInfo)
def IsDataEmpty():
global data
for poolData in data.values():
if len(poolData['DedicatedAllocations']) > 0:
return False
if len(poolData['Blocks']) > 0:
return False
for customPool in poolData['CustomPools'].values():
if len(customPool['Blocks']) > 0:
return False
if len(customPool['DedicatedAllocations']) > 0:
return False
return True
def RemoveEmptyType():
global data
for poolType in list(data.keys()):
if len(data[poolType]['DedicatedAllocations']) > 0:
continue
if len(data[poolType]['Blocks']) > 0:
continue
empty = True
for customPool in data[poolType]['CustomPools'].values():
if len(data[poolType]['Blocks']) > 0:
empty = False
break
if len(data[poolType]['DedicatedAllocations']) > 0:
empty = False
break
if empty:
del data[poolType]
# Returns tuple:
# [0] image height : integer
# [1] pixels per byte : float
def CalcParams():
global data
height = IMG_MARGIN
height += FONT_SIZE + IMG_MARGIN # Grid lines legend - sizes
maxBlockSize = 0
# Get height occupied by every memory pool
for poolData in data.values():
height += IMG_MARGIN + FONT_SIZE
height += len(poolData['DedicatedAllocations']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
height += len(poolData['Blocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
# Get longest block size
for dedicatedAlloc in poolData['DedicatedAllocations']:
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
for block in poolData['Blocks']:
maxBlockSize = max(maxBlockSize, block['Size'])
# Same for custom pools
for customPoolData in poolData['CustomPools'].values():
height += len(customPoolData['DedicatedAllocations']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
height += len(customPoolData['Blocks']) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)
height += FONT_SIZE * 2 + IMG_MARGIN if len(customPoolData['DedicatedAllocations']) == 0 else 0
# Get longest block size
for dedicatedAlloc in customPoolData['DedicatedAllocations']:
maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
for block in customPoolData['Blocks']:
maxBlockSize = max(maxBlockSize, block['Size'])
return height + FONT_SIZE, (IMG_WIDTH - IMG_MARGIN * 2) / float(maxBlockSize)
def BytesToStr(bytes):
if bytes < 1024:
return "%d B" % bytes
bytes /= 1024
if bytes < 1024:
return "%d KiB" % bytes
bytes /= 1024
if bytes < 1024:
return "%d MiB" % bytes
bytes /= 1024
return "%d GiB" % bytes
def TypeToColor(type, usage):
global currentApi
if type == 'FREE':
return 220, 220, 220, 255
elif type == 'UNKNOWN':
return 175, 175, 175, 255 # Gray
if currentApi == 'Vulkan':
if type == 'BUFFER':
if (usage & 0x1C0) != 0: # INDIRECT_BUFFER | VERTEX_BUFFER | INDEX_BUFFER
return 255, 148, 148, 255 # Red
elif (usage & 0x28) != 0: # STORAGE_BUFFER | STORAGE_TEXEL_BUFFER
return 255, 187, 121, 255 # Orange
elif (usage & 0x14) != 0: # UNIFORM_BUFFER | UNIFORM_TEXEL_BUFFER
return 255, 255, 0, 255 # Yellow
else:
return 255, 255, 165, 255 # Light yellow
elif type == 'IMAGE_OPTIMAL':
if (usage & 0x20) != 0: # DEPTH_STENCIL_ATTACHMENT
return 246, 128, 255, 255 # Pink
elif (usage & 0xD0) != 0: # INPUT_ATTACHMENT | TRANSIENT_ATTACHMENT | COLOR_ATTACHMENT
return 179, 179, 255, 255 # Blue
elif (usage & 0x4) != 0: # SAMPLED
return 0, 255, 255, 255 # Aqua
else:
return 183, 255, 255, 255 # Light aqua
elif type == 'IMAGE_LINEAR' :
return 0, 255, 0, 255 # Green
elif type == 'IMAGE_UNKNOWN':
return 0, 255, 164, 255 # Green/aqua
elif currentApi == 'Direct3D 12':
if type == 'BUFFER':
return 255, 255, 165, 255 # Light yellow
elif type == 'TEXTURE1D' or type == 'TEXTURE2D' or type == 'TEXTURE3D':
if (usage & 0x2) != 0: # D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
return 246, 128, 255, 255 # Pink
elif (usage & 0x5) != 0: # D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS
return 179, 179, 255, 255 # Blue
elif (usage & 0x8) == 0: # Not having D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE
return 0, 255, 255, 255 # Aqua
else:
return 183, 255, 255, 255 # Light aqua
else:
print("Unknown graphics API!")
exit(1)
assert False
return 0, 0, 0, 255
def DrawBlock(draw, y, block, pixelsPerByte):
sizePixels = int(block['Size'] * pixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + sizePixels, y + MAP_SIZE], fill=TypeToColor('FREE', 0), outline=None)
byte = 0
x = 0
lastHardLineX = -1
for alloc in block['Suballocations']:
byteEnd = byte + alloc['Size']
xEnd = int(byteEnd * pixelsPerByte)
if alloc['Type'] != 'FREE':
if xEnd > x + 1:
draw.rectangle([IMG_MARGIN + x, y, IMG_MARGIN + xEnd, y + MAP_SIZE], fill=TypeToColor(alloc['Type'], alloc['Usage']), outline=COLOR_OUTLINE)
# Hard line was been overwritten by rectangle outline: redraw it.
if lastHardLineX == x:
draw.line([IMG_MARGIN + x, y, IMG_MARGIN + x, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
else:
draw.line([IMG_MARGIN + x, y, IMG_MARGIN + x, y + MAP_SIZE], fill=COLOR_OUTLINE_HARD)
lastHardLineX = x
byte = byteEnd
x = xEnd
def DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte):
sizePixels = int(dedicatedAlloc['Size'] * pixelsPerByte)
draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + sizePixels, y + MAP_SIZE], fill=TypeToColor(dedicatedAlloc['Type'], dedicatedAlloc['Usage']), outline=COLOR_OUTLINE)
if __name__ == '__main__':
args = ParseArgs()
jsonSrc = json.load(open(args.DumpFile, 'rb'))
if 'General' in jsonSrc:
currentApi = jsonSrc['General']['API']
else:
print("Wrong JSON format, cannot determine graphics API!")
exit(1)
# Process default pools
if 'DefaultPools' in jsonSrc:
for memoryPool in jsonSrc['DefaultPools'].items():
poolData = GetDataForMemoryPool(memoryPool[0])
# Get dedicated allocations
for dedicatedAlloc in memoryPool[1]['DedicatedAllocations']:
allocData = {'Type': dedicatedAlloc['Type'], 'Size': int(dedicatedAlloc['Size']), 'Usage': int(dedicatedAlloc['Usage'])}
poolData['DedicatedAllocations'].append(allocData)
# Get allocations in block vectors
for block in memoryPool[1]['Blocks'].items():
ProcessBlock(poolData, block)
# Process custom pools
if 'CustomPools' in jsonSrc:
for memoryPool in jsonSrc['CustomPools'].items():
poolData = GetDataForMemoryPool(memoryPool[0])
for pool in memoryPool[1]:
poolName = pool['Name']
poolData['CustomPools'][poolName] = {'DedicatedAllocations':[], 'Blocks':[]}
# Get dedicated allocations
for dedicatedAlloc in pool['DedicatedAllocations']:
allocData = {'Type': dedicatedAlloc['Type'], 'Size': int(dedicatedAlloc['Size']), 'Usage': int(dedicatedAlloc['Usage'])}
poolData['CustomPools'][poolName]['DedicatedAllocations'].append(allocData)
# Get allocations in block vectors
for block in pool['Blocks'].items():
ProcessBlock(poolData['CustomPools'][poolName], block)
if IsDataEmpty():
print("There is nothing to put on the image. Please make sure you generated the stats string with detailed map enabled.")
exit(1)
RemoveEmptyType()
# Calculate dimmensions and create data image
imgHeight, pixelsPerByte = CalcParams()
img = Image.new('RGB', (IMG_WIDTH, imgHeight), 'white')
draw = ImageDraw.Draw(img)
try:
font = ImageFont.truetype('segoeuib.ttf')
except:
font = ImageFont.load_default()
# Draw grid lines
bytesBetweenGridLines = 32
while bytesBetweenGridLines * pixelsPerByte < 64:
bytesBetweenGridLines *= 2
byte = 0
y = IMG_MARGIN
while True:
x = int(byte * pixelsPerByte)
if x > IMG_WIDTH - 2 * IMG_MARGIN:
break
draw.line([x + IMG_MARGIN, 0, x + IMG_MARGIN, imgHeight], fill=COLOR_GRID_LINE)
if byte == 0:
draw.text((x + IMG_MARGIN + TEXT_MARGIN, y), "0", fill=COLOR_TEXT_H2, font=font)
else:
text = BytesToStr(byte)
textSize = draw.textsize(text, font=font)
draw.text((x + IMG_MARGIN - textSize[0] - TEXT_MARGIN, y), text, fill=COLOR_TEXT_H2, font=font)
byte += bytesBetweenGridLines
y += FONT_SIZE + IMG_MARGIN
# Draw main content
for memType in sorted(data.keys()):
memPoolData = data[memType]
draw.text((IMG_MARGIN, y), "Memory pool %s" % memType, fill=COLOR_TEXT_H1, font=font)
y += FONT_SIZE + IMG_MARGIN
# Draw block vectors
for block in memPoolData['Blocks']:
draw.text((IMG_MARGIN, y), "Default pool block %s" % block['ID'], fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawBlock(draw, y, block, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index = 0
# Draw dedicated allocations
for dedicatedAlloc in memPoolData['DedicatedAllocations']:
draw.text((IMG_MARGIN, y), "Dedicated allocation %d" % index, fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index += 1
for poolName, pool in memPoolData['CustomPools'].items():
for block in pool['Blocks']:
draw.text((IMG_MARGIN, y), "Custom pool %s block %s" % (poolName, block['ID']), fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawBlock(draw, y, block, pixelsPerByte)
y += 2 * (FONT_SIZE + IMG_MARGIN)
index = 0
for dedicatedAlloc in pool['DedicatedAllocations']:
draw.text((IMG_MARGIN, y), "Custom pool %s dedicated allocation %d" % (poolName, index), fill=COLOR_TEXT_H2, font=font)
y += FONT_SIZE + IMG_MARGIN
DrawDedicatedAllocationBlock(draw, y, dedicatedAlloc, pixelsPerByte)
y += MAP_SIZE + IMG_MARGIN
index += 1
del draw
img.save(args.output)
"""
Main data structure - variable `data` - is a dictionary. Key is string - memory type name. Value is dictionary of:
- Fixed key 'DedicatedAllocations'. Value is list of objects, each containing dictionary with:
- Fixed key 'Type'. Value is string.
- Fixed key 'Size'. Value is int.
- Fixed key 'Usage'. Value is int.
- Fixed key 'Blocks'. 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 objects as above.
- Fixed key 'CustomPools'. Value is dictionary.
- Key is string with pool ID/name. Value is a dictionary with:
- Fixed key 'DedicatedAllocations'. Value is list of objects as above.
- Fixed key 'Blocks'. Value is a list of objects representing memory blocks as above.
"""

View File

@ -1,6 +1,9 @@
# VMA Dump Vis
# GpuMemDumpVis
Vulkan Memory Allocator Dump Visualization. It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](../../README.md) library on a picture. It is a Python script that must be launched from command line with appropriate parameters.
Vulkan/D3D12 Memory Allocator Dump Visualization.
It is an auxiliary tool that can visualize internal state of [Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) and
[D3D12 Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator) libraries on a picture.
It is a Python script that must be launched from command line with appropriate parameters.
## Requirements
@ -10,10 +13,10 @@ Vulkan Memory Allocator Dump Visualization. It is an auxiliary tool that can vis
## Usage
```
python VmaDumpVis.py -o OUTPUT_FILE INPUT_FILE
python GpuMemDumpVis.py -o OUTPUT_FILE INPUT_FILE
```
* `INPUT_FILE` - path to source file to be read, containing dump of internal state of the VMA library in JSON format (encoding: UTF-8), generated using `vmaBuildStatsString()` function.
* `INPUT_FILE` - path to source file to be read, containing dump of internal state of the VMA/D3D12MA library in JSON format (encoding: UTF-8/UTF-16), generated using `vmaBuildStatsString()` and `D3D12MA::Allocator::BuildStatsString()` functions.
* `OUTPUT_FILE` - path to destination file to be written that will contain generated image. Image format is automatically recognized based on file extension. List of supported formats can be found [here](http://pillow.readthedocs.io/en/latest/handbook/image-file-formats.html) and includes: BMP, GIF, JPEG, PNG, TGA.
You can also use typical options:
@ -28,15 +31,15 @@ You can also use typical options:
## Legend
* ![Free space](README_files/Legend_Bkg.png "Free space") Light gray without border - a space in Vulkan device memory block unused by any allocation.
* ![Buffer 1](README_files/Legend_Buffer_1.png "Buffer 1") Buffer with usage containing INDIRECT_BUFFER, VERTEX_BUFFER, or INDEX_BUFFER.
* ![Buffer 2](README_files/Legend_Buffer_2.png "Buffer 2") Buffer with usage containing STORAGE_BUFFER or STORAGE_TEXEL_BUFFER.
* ![Buffer 3](README_files/Legend_Buffer_3.png "Buffer 3") Buffer with usage containing UNIFORM_BUFFER or UNIFORM_TEXEL_BUFFER.
* ![Buffer 1](README_files/Legend_Buffer_1.png "Buffer 1") Buffer with usage containing INDIRECT_BUFFER, VERTEX_BUFFER, or INDEX_BUFFER (Vulkan).
* ![Buffer 2](README_files/Legend_Buffer_2.png "Buffer 2") Buffer with usage containing STORAGE_BUFFER or STORAGE_TEXEL_BUFFER (Vulkan).
* ![Buffer 3](README_files/Legend_Buffer_3.png "Buffer 3") Buffer with usage containing UNIFORM_BUFFER or UNIFORM_TEXEL_BUFFER (Vulkan).
* ![Buffer 4](README_files/Legend_Buffer_4.png "Buffer 4") Other buffer.
* ![Image 1](README_files/Legend_Image_1.png "Image 1") Image with OPTIMAL tiling and usage containing DEPTH_STENCIL_ATTACHMENT.
* ![Image 2](README_files/Legend_Image_2.png "Image 2") Image with OPTIMAL tiling and usage containing INPUT_ATTACHMENT, TRANSIENT_ATTACHMENT, or COLOR_ATTACHMENT.
* ![Image 3](README_files/Legend_Image_3.png "Image 3") Image with OPTIMAL tiling and usage containing SAMPLED.
* ![Image 4](README_files/Legend_Image_4.png "Image 4") Other image with OPTIMAL tiling.
* ![Image Linear](README_files/Legend_Image_Linear.png "Image Linear") Image with LINEAR tiling.
* ![Image Unknown](README_files/Legend_Image_Unknown.png "Image Unknown") Image with tiling unknown to the allocator.
* ![Image 1](README_files/Legend_Image_1.png "Image 1") Image with OPTIMAL tiling and usage containing DEPTH_STENCIL_ATTACHMENT (Vulkan) or a texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL (D3D12).
* ![Image 2](README_files/Legend_Image_2.png "Image 2") Image with OPTIMAL tiling and usage containing INPUT_ATTACHMENT, TRANSIENT_ATTACHMENT or COLOR_ATTACHMENT (Vulkan), or a texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS (D3D12).
* ![Image 3](README_files/Legend_Image_3.png "Image 3") Image with OPTIMAL tiling and usage containing SAMPLED (Vulkan) or a texture with usage not containing D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE (D3D12).
* ![Image 4](README_files/Legend_Image_4.png "Image 4") Other image with OPTIMAL tiling (Vulkan) or a texture (D3D12).
* ![Image Linear](README_files/Legend_Image_Linear.png "Image Linear") Image with LINEAR tiling (Vulkan).
* ![Image Unknown](README_files/Legend_Image_Unknown.png "Image Unknown") Image with tiling unknown to the allocator (Vulkan).
* ![Unknown](README_files/Legend_Unknown.png "Unknown") Allocation of unknown type.
* ![Details](README_files/Legend_Details.png "Details") Black bar - one or more allocations of any kind too small to be visualized as filled rectangles.

View File

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

Before

Width:  |  Height:  |  Size: 196 B

After

Width:  |  Height:  |  Size: 196 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 964 B

After

Width:  |  Height:  |  Size: 964 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,426 @@
{
"General": {
"API": "Vulkan",
"apiVersion": "1.3.203",
"GPU": "AMD Radeon RX 6600 XT",
"deviceType": 2,
"maxMemoryAllocationCount": 4096,
"bufferImageGranularity": 1,
"nonCoherentAtomSize": 128,
"memoryHeapCount": 2,
"memoryTypeCount": 8
},
"Total": {
"BlockCount": 69,
"BlockBytes": 201392128,
"AllocationCount": 132,
"AllocationBytes": 73401500,
"UnusedRangeCount": 11,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 33550336
},
"MemoryInfo": {
"Heap 0": {
"Flags": [],
"Size": 16862150656,
"Budget": {
"BudgetBytes": 16325847040,
"UsageBytes": 122392576
},
"Stats": {
"BlockCount": 35,
"BlockBytes": 117473280,
"AllocationCount": 64,
"AllocationBytes": 33619968,
"UnusedRangeCount": 5,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 2097152,
"UnusedRangeSizeMin": 49152,
"UnusedRangeSizeMax": 33550336
},
"MemoryPools": {
"Type 1": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT"],
"Stats": {
"BlockCount": 5,
"BlockBytes": 33558528,
"AllocationCount": 8,
"AllocationBytes": 8192,
"UnusedRangeCount": 1,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 1024
}
},
"Type 3": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "HOST_CACHED"],
"Stats": {
"BlockCount": 30,
"BlockBytes": 83914752,
"AllocationCount": 56,
"AllocationBytes": 33611776,
"UnusedRangeCount": 4,
"AllocationSizeMin": 1024,
"AllocationSizeMax": 2097152,
"UnusedRangeSizeMin": 49152,
"UnusedRangeSizeMax": 25100288
}
},
"Type 5": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 7": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "HOST_CACHED", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
}
}
},
"Heap 1": {
"Flags": ["DEVICE_LOCAL", "MULTI_INSTANCE"],
"Size": 8573157376,
"Budget": {
"BudgetBytes": 7737008128,
"UsageBytes": 155025408
},
"Stats": {
"BlockCount": 34,
"BlockBytes": 83918848,
"AllocationCount": 68,
"AllocationBytes": 39781532,
"UnusedRangeCount": 6,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 25100288
},
"MemoryPools": {
"Type 0": {
"Flags": ["DEVICE_LOCAL"],
"Stats": {
"BlockCount": 34,
"BlockBytes": 83918848,
"AllocationCount": 68,
"AllocationBytes": 39781532,
"UnusedRangeCount": 6,
"AllocationSizeMin": 60,
"AllocationSizeMax": 6095200,
"UnusedRangeSizeMin": 196,
"UnusedRangeSizeMax": 25100288
}
},
"Type 2": {
"Flags": ["DEVICE_LOCAL", "HOST_VISIBLE", "HOST_COHERENT"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 4": {
"Flags": ["DEVICE_LOCAL", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
},
"Type 6": {
"Flags": ["DEVICE_LOCAL", "HOST_VISIBLE", "HOST_COHERENT", "DEVICE_COHERENT_AMD", "DEVICE_UNCACHED_AMD"],
"Stats": {
"BlockCount": 0,
"BlockBytes": 0,
"AllocationCount": 0,
"AllocationBytes": 0,
"UnusedRangeCount": 0
}
}
}
}
},
"DefaultPools": {
"Type 0": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 18987876,
"Allocations": 20,
"UnusedRanges": 4,
"Suballocations": [
{"Offset": 0, "Type": "IMAGE_OPTIMAL", "Size": 65536, "Usage": 6},
{"Offset": 65536, "Type": "BUFFER", "Size": 768, "Usage": 130},
{"Offset": 66304, "Type": "BUFFER", "Size": 60, "Usage": 66},
{"Offset": 66364, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 67388, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 68412, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 69436, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 70460, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 71484, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 72508, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 73532, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 74556, "Type": "FREE", "Size": 196},
{"Offset": 74752, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 76800, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 78848, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 80896, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 82944, "Type": "FREE", "Size": 48128},
{"Offset": 131072, "Type": "IMAGE_OPTIMAL", "Size": 6095200, "Usage": 32},
{"Offset": 6226272, "Type": "FREE", "Size": 65184},
{"Offset": 6291456, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 8388608, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 10485760, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 12582912, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 14680064, "Type": "FREE", "Size": 18874368}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 1": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 33550336,
"Allocations": 4,
"UnusedRanges": 1,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "FREE", "Size": 33550336}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 2": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 3": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25153536,
"Allocations": 12,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 1024, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 6144, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 12288, "Type": "FREE", "Size": 53248},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
},
"Type 4": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 5": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 6": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
},
"Type 7": {
"PreferredBlockSize": 268435456,
"Blocks": {
},
"DedicatedAllocations": [
]
}
},
"CustomPools": {
"Type 0": [
{
"Name": "0",
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25149440,
"Allocations": 16,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 5120, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 6144, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 7168, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 12288, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 14336, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 16384, "Type": "FREE", "Size": 49152},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
}
],
"Type 3": [
{
"Name": "0",
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"MapRefCount": 0,
"TotalBytes": 33554432,
"UnusedBytes": 25149440,
"Allocations": 16,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Offset": 1024, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Offset": 2048, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Offset": 3072, "Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 4096, "Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Offset": 5120, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Offset": 6144, "Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Offset": 7168, "Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8192, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Offset": 10240, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 12288, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 14336, "Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 16384, "Type": "FREE", "Size": 49152},
{"Offset": 65536, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Offset": 2162688, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Offset": 4259840, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Offset": 6356992, "Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Offset": 8454144, "Type": "FREE", "Size": 25100288}
]
}
},
"DedicatedAllocations": [
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "0000000000F5D987"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "Name": "SHEPURD"},
{"Type": "UNKNOWN", "Size": 1024, "Usage": 0, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "0000000000F5D987"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "Name": "SHEPURD"},
{"Type": "BUFFER", "Size": 1024, "Usage": 3, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_LINEAR", "Size": 2048, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "0000000000F5D987"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "Name": "SHEPURD"},
{"Type": "IMAGE_OPTIMAL", "Size": 2097152, "Usage": 7, "CustomData": "00000000018CE96A", "Name": "JOKER"}
]
}
]
}
}

View File

@ -1,102 +0,0 @@
{
"Total": {
"Blocks": 2,
"Allocations": 4,
"UnusedRanges": 3,
"UsedBytes": 8062124,
"UnusedBytes": 59046740,
"AllocationSize": {"Min": 60, "Avg": 2015531, "Max": 7995760},
"UnusedRangeSize": {"Min": 64708, "Avg": 19682247, "Max": 33554432}
},
"Heap 0": {
"Size": 8304721920,
"Flags": ["DEVICE_LOCAL"],
"Stats": {
"Blocks": 1,
"Allocations": 4,
"UnusedRanges": 2,
"UsedBytes": 8062124,
"UnusedBytes": 25492308,
"AllocationSize": {"Min": 60, "Avg": 2015531, "Max": 7995760},
"UnusedRangeSize": {"Min": 64708, "Avg": 12746154, "Max": 25427600}
},
"Type 0": {
"Flags": ["DEVICE_LOCAL"],
"Stats": {
"Blocks": 1,
"Allocations": 4,
"UnusedRanges": 2,
"UsedBytes": 8062124,
"UnusedBytes": 25492308,
"AllocationSize": {"Min": 60, "Avg": 2015531, "Max": 7995760},
"UnusedRangeSize": {"Min": 64708, "Avg": 12746154, "Max": 25427600}
}
}
},
"Heap 1": {
"Size": 8285323264,
"Flags": [],
"Stats": {
"Blocks": 1,
"Allocations": 0,
"UnusedRanges": 1,
"UsedBytes": 0,
"UnusedBytes": 33554432
},
"Type 1": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT"],
"Stats": {
"Blocks": 1,
"Allocations": 0,
"UnusedRanges": 1,
"UsedBytes": 0,
"UnusedBytes": 33554432
}
},
"Type 3": {
"Flags": ["HOST_VISIBLE", "HOST_COHERENT", "HOST_CACHED"]
}
},
"Heap 2": {
"Size": 268435456,
"Flags": ["DEVICE_LOCAL"],
"Type 2": {
"Flags": ["DEVICE_LOCAL", "HOST_VISIBLE", "HOST_COHERENT"]
}
},
"DefaultPools": {
"Type 0": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"TotalBytes": 33554432,
"UnusedBytes": 25492308,
"Allocations": 4,
"UnusedRanges": 2,
"Suballocations": [
{"Offset": 0, "Type": "IMAGE_OPTIMAL", "Size": 65536, "CreationFrameIndex": 0, "LastUseFrameIndex": 0, "Usage": 6},
{"Offset": 65536, "Type": "BUFFER", "Size": 768, "CreationFrameIndex": 0, "LastUseFrameIndex": 0, "Usage": 130},
{"Offset": 66304, "Type": "BUFFER", "Size": 60, "CreationFrameIndex": 0, "LastUseFrameIndex": 0, "Usage": 66},
{"Offset": 66364, "Type": "FREE", "Size": 64708},
{"Offset": 131072, "Type": "IMAGE_OPTIMAL", "Size": 7995760, "CreationFrameIndex": 0, "LastUseFrameIndex": 0, "Usage": 32},
{"Offset": 8126832, "Type": "FREE", "Size": 25427600}
]
}
}
},
"Type 1": {
"PreferredBlockSize": 268435456,
"Blocks": {
"0": {
"TotalBytes": 33554432,
"UnusedBytes": 33554432,
"Allocations": 0,
"UnusedRanges": 1,
"Suballocations": [
{"Offset": 0, "Type": "FREE", "Size": 33554432}
]
}
}
}
}
}

View File

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