From 3c6470cf45816999f906c2dec18ec15a57652000 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Thu, 24 Mar 2022 11:50:34 +0100 Subject: [PATCH] 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 --- README.md | 2 +- include/vk_mem_alloc.h | 537 +++++++++--------- src/Tests.cpp | 167 +++++- tools/GpuMemDumpVis/GpuMemDumpVis.py | 334 +++++++++++ tools/{VmaDumpVis => GpuMemDumpVis}/README.md | 29 +- .../README_files/ExampleOutput.png | Bin .../README_files/Legend_Bkg.png | Bin .../README_files/Legend_Buffer_1.png | Bin .../README_files/Legend_Buffer_2.png | Bin .../README_files/Legend_Buffer_3.png | Bin .../README_files/Legend_Buffer_4.png | Bin .../README_files/Legend_Details.png | Bin .../README_files/Legend_Image_1.png | Bin .../README_files/Legend_Image_2.png | Bin .../README_files/Legend_Image_3.png | Bin .../README_files/Legend_Image_4.png | Bin .../README_files/Legend_Image_Linear.png | Bin .../README_files/Legend_Image_Unknown.png | Bin .../README_files/Legend_Unknown.png | Bin tools/GpuMemDumpVis/Sample.json | 426 ++++++++++++++ tools/VmaDumpVis/Sample.json | 102 ---- tools/VmaDumpVis/VmaDumpVis.py | 320 ----------- 22 files changed, 1205 insertions(+), 712 deletions(-) create mode 100644 tools/GpuMemDumpVis/GpuMemDumpVis.py rename tools/{VmaDumpVis => GpuMemDumpVis}/README.md (56%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/ExampleOutput.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Bkg.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Buffer_1.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Buffer_2.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Buffer_3.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Buffer_4.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Details.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_1.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_2.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_3.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_4.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_Linear.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Image_Unknown.png (100%) rename tools/{VmaDumpVis => GpuMemDumpVis}/README_files/Legend_Unknown.png (100%) create mode 100644 tools/GpuMemDumpVis/Sample.json delete mode 100644 tools/VmaDumpVis/Sample.json delete mode 100644 tools/VmaDumpVis/VmaDumpVis.py diff --git a/README.md b/README.md index 7959d74..0096ab6 100644 --- a/README.md +++ b/README.md @@ -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. 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 @@ 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 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(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(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(); } 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 ValidateAllocationsData(const AllocInfo* allocs, size_t allocCount) }); } + +static void TestJson() +{ + wprintf(L"Test JSON\n"); + + std::vector pools; + std::vector 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(); 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/VmaDumpVis/README.md b/tools/GpuMemDumpVis/README.md similarity index 56% rename from tools/VmaDumpVis/README.md rename to tools/GpuMemDumpVis/README.md index f238001..2bc8249 100644 --- a/tools/VmaDumpVis/README.md +++ b/tools/GpuMemDumpVis/README.md @@ -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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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/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. -"""