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
diff --git a/README.md b/README.md
index 7959d74..0096ab6 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@
- 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.
diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index f5da499..d6bd74b 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -5765,41 +5765,29 @@
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 @@
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 @@
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 @@
#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 @@
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 @@
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_End(class VmaJsonWriter& json) const
{
json.EndArray();
- json.EndObject();
}
#endif // VMA_STATS_STRING_ENABLED
#endif // _VMA_BLOCK_METADATA_FUNCTIONS
@@ -7631,7 +7604,7 @@
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 @@
}
#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 @@
}
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 @@
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 @@
}
#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 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
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 @@
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 @@
{
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 @@
#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 @@
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();
- if(stats.memoryType[typeIndex].statistics.blockCount > 0)
- {
+ json.WriteString("Size");
+ json.WriteNumber(heapInfo.size);
+
+ json.WriteString("Budget");
+ json.BeginObject();
+ {
+ json.WriteString("BudgetBytes");
+ json.WriteNumber(budgets[heapIndex].budget);
+ json.WriteString("UsageBytes");
+ json.WriteNumber(budgets[heapIndex].usage);
+ }
+ json.EndObject();
+
json.WriteString("Stats");
- VmaPrintDetailedStatistics(json, stats.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();
}
diff --git a/src/Tests.cpp b/src/Tests.cpp
index 37b1042..baee5c4 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -1653,6 +1653,168 @@
});
}
+
+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 @@
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 @@
TestDebugMargin();
TestDebugMarginNotInVirtualAllocator();
#else
+ TestJson();
TestBasics();
TestVirtualBlocks();
TestVirtualBlocksAlgorithms();
diff --git a/tools/GpuMemDumpVis/GpuMemDumpVis.py b/tools/GpuMemDumpVis/GpuMemDumpVis.py
new file mode 100644
index 0000000..b225306
--- /dev/null
+++ b/tools/GpuMemDumpVis/GpuMemDumpVis.py
@@ -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.
+"""
diff --git a/tools/GpuMemDumpVis/README.md b/tools/GpuMemDumpVis/README.md
new file mode 100644
index 0000000..2bc8249
--- /dev/null
+++ b/tools/GpuMemDumpVis/README.md
@@ -0,0 +1,45 @@
+# GpuMemDumpVis
+
+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
+
+- Python 3 installed
+- [Pillow](http://python-pillow.org/) - Python Imaging Library (Fork) installed
+
+## Usage
+
+```
+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/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:
+
+* `-h` - to see help on command line syntax
+* `-v` - to see program version number
+
+## Example output
+
+![Example output](README_files/ExampleOutput.png "Example output")
+
+## 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 (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 (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.
diff --git a/tools/VmaDumpVis/README_files/ExampleOutput.png b/tools/GpuMemDumpVis/README_files/ExampleOutput.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/ExampleOutput.png
rename to tools/GpuMemDumpVis/README_files/ExampleOutput.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Bkg.png b/tools/GpuMemDumpVis/README_files/Legend_Bkg.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Bkg.png
rename to tools/GpuMemDumpVis/README_files/Legend_Bkg.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Buffer_1.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Buffer_1.png
rename to tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Buffer_2.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Buffer_2.png
rename to tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Buffer_3.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Buffer_3.png
rename to tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Buffer_4.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Buffer_4.png
rename to tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Details.png b/tools/GpuMemDumpVis/README_files/Legend_Details.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Details.png
rename to tools/GpuMemDumpVis/README_files/Legend_Details.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_1.png b/tools/GpuMemDumpVis/README_files/Legend_Image_1.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_1.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_1.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_2.png b/tools/GpuMemDumpVis/README_files/Legend_Image_2.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_2.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_2.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_3.png b/tools/GpuMemDumpVis/README_files/Legend_Image_3.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_3.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_3.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_4.png b/tools/GpuMemDumpVis/README_files/Legend_Image_4.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_4.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_4.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_Linear.png b/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_Linear.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Image_Unknown.png b/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Image_Unknown.png
rename to tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png
Binary files differ
diff --git a/tools/VmaDumpVis/README_files/Legend_Unknown.png b/tools/GpuMemDumpVis/README_files/Legend_Unknown.png
similarity index 100%
rename from tools/VmaDumpVis/README_files/Legend_Unknown.png
rename to tools/GpuMemDumpVis/README_files/Legend_Unknown.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/Sample.json b/tools/GpuMemDumpVis/Sample.json
new file mode 100644
index 0000000..d12c8b3
--- /dev/null
+++ b/tools/GpuMemDumpVis/Sample.json
@@ -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"}
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/tools/VmaDumpVis/README.md b/tools/VmaDumpVis/README.md
deleted file mode 100644
index f238001..0000000
--- a/tools/VmaDumpVis/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# VMA Dump Vis
-
-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.
-
-## Requirements
-
-- Python 3 installed
-- [Pillow](http://python-pillow.org/) - Python Imaging Library (Fork) installed
-
-## Usage
-
-```
-python VmaDumpVis.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.
-* `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:
-
-* `-h` - to see help on command line syntax
-* `-v` - to see program version number
-
-## Example output
-
-![Example output](README_files/ExampleOutput.png "Example output")
-
-## 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 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.
-* ![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.
diff --git a/tools/VmaDumpVis/Sample.json b/tools/VmaDumpVis/Sample.json
deleted file mode 100644
index 34698a3..0000000
--- a/tools/VmaDumpVis/Sample.json
+++ /dev/null
@@ -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}
- ]
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tools/VmaDumpVis/VmaDumpVis.py b/tools/VmaDumpVis/VmaDumpVis.py
deleted file mode 100644
index 03d658c..0000000
--- a/tools/VmaDumpVis/VmaDumpVis.py
+++ /dev/null
@@ -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.
-"""