New JSON dump format

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

Code by @medranSolus
diff --git a/README.md b/README.md
index 65a9ff5..28b17a1 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,7 @@
 - Statistics: Obtain brief or detailed statistics about the amount of memory used, unused, number of allocated heaps, number of allocations etc. - globally and per memory heap type. Current memory usage and budget as reported by the system can also be queried.
 - Debug annotations: Associate custom `void* pPrivateData` and debug `LPCWSTR 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 using attached Python script.
+- Convert this JSON dump into a picture to visualize your memory. See [tools/GpuMemDumpVis](tools/GpuMemDumpVis/README.md).
 - Virtual allocator - an API that exposes the core allocation algorithm to be used without allocating real GPU memory, to allocate your own stuff, e.g. sub-allocate pieces of one large buffer.
 
 # Prerequisites
diff --git a/include/D3D12MemAlloc.h b/include/D3D12MemAlloc.h
index 46fa934..6e3ecf7 100644
--- a/include/D3D12MemAlloc.h
+++ b/include/D3D12MemAlloc.h
@@ -237,7 +237,6 @@
     */

     ALLOCATION_FLAG_UPPER_ADDRESS = 0x8,

 

-

     /** Set this flag if the allocated memory will have aliasing resources.

     

     Use this when calling D3D12MA::Allocator::CreateResource() and similar to

@@ -570,7 +569,6 @@
     UINT64 m_Size;

     UINT64 m_Alignment;

     ID3D12Resource* m_Resource;

-    UINT m_CreationFrameIndex;

     void* m_pPrivateData;

     wchar_t* m_Name;

 

@@ -1246,8 +1244,9 @@
     */

     void CalculateStatistics(TotalStatistics* pStats);

 

-    /// Builds and returns statistics as a string in JSON format.

-    /** @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.

+    /** \brief Builds and returns statistics as a string in JSON format.

+    * 

+    @param[out] ppStatsString Must be freed using Allocator::FreeStatsString.

     @param DetailedMap `TRUE` to include full list of allocations (can make the string quite long), `FALSE` to only return statistics.

     */

     void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap) const;

diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index 64aa1b3..6939696 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -1511,61 +1511,61 @@
         break;

     default: D3D12MA_ASSERT(0); break;

     }

+

     WriteString(L"Size");

     WriteNumber(alloc.GetSize());

+    WriteString(L"Usage");

+    WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());

+

+    void* privateData = alloc.GetPrivateData();

+    if (privateData)

+    {

+        WriteString(L"CustomData");

+        WriteNumber((uintptr_t)privateData);

+    }

+

     LPCWSTR name = alloc.GetName();

     if (name != NULL)

     {

         WriteString(L"Name");

         WriteString(name);

     }

-    if (alloc.m_PackedData.GetResourceFlags())

-    {

-        WriteString(L"Flags");

-        WriteNumber((UINT)alloc.m_PackedData.GetResourceFlags());

-    }

     if (alloc.m_PackedData.GetTextureLayout())

     {

         WriteString(L"Layout");

         WriteNumber((UINT)alloc.m_PackedData.GetTextureLayout());

     }

-    if (alloc.m_CreationFrameIndex)

-    {

-        WriteString(L"CreationFrameIndex");

-        WriteNumber(alloc.m_CreationFrameIndex);

-    }

 }

 

 void JsonWriter::AddDetailedStatisticsInfoObject(const DetailedStatistics& stats)

 {

     BeginObject();

+

     WriteString(L"BlockCount");

     WriteNumber(stats.Stats.BlockCount);

-    WriteString(L"AllocationCount");

-    WriteNumber(stats.Stats.AllocationCount);

-    WriteString(L"UnusedRangeCount");

-    WriteNumber(stats.UnusedRangeCount);

     WriteString(L"BlockBytes");

     WriteNumber(stats.Stats.BlockBytes);

+    WriteString(L"AllocationCount");

+    WriteNumber(stats.Stats.AllocationCount);

     WriteString(L"AllocationBytes");

     WriteNumber(stats.Stats.AllocationBytes);

+    WriteString(L"UnusedRangeCount");

+    WriteNumber(stats.UnusedRangeCount);

 

-    WriteString(L"AllocationSize");

-    BeginObject(true);

-    WriteString(L"Min");

-    WriteNumber(stats.AllocationSizeMin);

-    WriteString(L"Max");

-    WriteNumber(stats.AllocationSizeMax);

-    EndObject();

-

-    WriteString(L"UnusedRangeSize");

-    BeginObject(true);

-    WriteString(L"Min");

-    WriteNumber(stats.UnusedRangeSizeMin);

-    WriteString(L"Max");

-    WriteNumber(stats.UnusedRangeSizeMax);

-    EndObject();

-

+    if (stats.Stats.AllocationCount > 1)

+    {

+        WriteString(L"AllocationSizeMin");

+        WriteNumber(stats.AllocationSizeMin);

+        WriteString(L"AllocationSizeMax");

+        WriteNumber(stats.AllocationSizeMax);

+    }

+    if (stats.UnusedRangeCount > 1)

+    {

+        WriteString(L"UnusedRangeSizeMin");

+        WriteNumber(stats.UnusedRangeSizeMin);

+        WriteString(L"UnusedRangeSizeMax");

+        WriteNumber(stats.UnusedRangeSizeMax);

+    }

     EndObject();

 }

 

@@ -2965,8 +2965,6 @@
 void BlockMetadata::PrintDetailedMap_Begin(JsonWriter& json,

     UINT64 unusedBytes, size_t allocationCount, size_t unusedRangeCount) const

 {

-    json.BeginObject();

-

     json.WriteString(L"TotalBytes");

     json.WriteNumber(GetSize());

 

@@ -2993,13 +2991,11 @@
 

     if (IsVirtual())

     {

-        json.WriteString(L"Type");

-        json.WriteString(L"ALLOCATION");

         json.WriteString(L"Size");

         json.WriteNumber(size);

         if (privateData)

         {

-            json.WriteString(L"PrivateData");

+            json.WriteString(L"CustomData");

             json.WriteNumber((uintptr_t)privateData);

         }

     }

@@ -3032,7 +3028,6 @@
 void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const

 {

     json.EndArray();

-    json.EndObject();

 }

 #endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS

 #endif // _D3D12MA_BLOCK_METADATA

@@ -5942,6 +5937,7 @@
     ~BlockVector();

 

     const D3D12_HEAP_PROPERTIES& GetHeapProperties() const { return m_HeapProps; }

+    D3D12_HEAP_FLAGS GetHeapFlags() const { return m_HeapFlags; }

     UINT64 GetPreferredBlockSize() const { return m_PreferredBlockSize; }

     UINT32 GetAlgorithm() const { return m_Algorithm; }

     // To be used only while the m_Mutex is locked. Used during defragmentation.

@@ -6409,13 +6405,13 @@
     void FreeHeapMemory(Allocation* allocation);

 

     void SetCurrentFrameIndex(UINT frameIndex);

-

-    void CalculateStatistics(TotalStatistics& outStats);

+    // For more deailed stats use outCutomHeaps to access statistics divided into L0 and L1 group

+    void CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2] = NULL);

 

     void GetBudget(Budget* outLocalBudget, Budget* outNonLocalBudget);

     void GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE heapType);

 

-    void BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap);

+    void BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap);

     void FreeStatsString(WCHAR* pStatsString);

 

 private:

@@ -6945,7 +6941,7 @@
 #endif

 }

 

-void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats)

+void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats, DetailedStatistics outCutomHeaps[2])

 {

     // Init stats

     for (size_t i = 0; i < HEAP_TYPE_COUNT; i++)

@@ -6953,6 +6949,11 @@
     for (size_t i = 0; i < DXGI_MEMORY_SEGMENT_GROUP_COUNT; i++)

         ClearDetailedStatistics(outStats.MemorySegmentGroup[i]);

     ClearDetailedStatistics(outStats.Total);

+    if (outCutomHeaps)

+    {

+        ClearDetailedStatistics(outCutomHeaps[0]);

+        ClearDetailedStatistics(outCutomHeaps[1]);

+    }

 

     // Process default pools. 3 standard heap types only. Add them to outStats.HeapType[i].

     if (SupportsResourceHeapTier2())

@@ -7003,8 +7004,13 @@
             pool->AddDetailedStatistics(tmpStats);

             AddDetailedStatistics(

                 outStats.HeapType[heapTypeIndex], tmpStats);

+

+            UINT memorySegment = HeapPropertiesToMemorySegmentGroup(poolHeapProps);

             AddDetailedStatistics(

-                outStats.MemorySegmentGroup[HeapPropertiesToMemorySegmentGroup(poolHeapProps)], tmpStats);

+                outStats.MemorySegmentGroup[memorySegment], tmpStats);

+

+            if (outCutomHeaps)

+                AddDetailedStatistics(outCutomHeaps[memorySegment], tmpStats);

         }

     }

 

@@ -7106,155 +7112,354 @@
     }

 }

 

-void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL DetailedMap)

+void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap)

 {

     StringBuilder sb(GetAllocs());

     {

-        JsonWriter json(GetAllocs(), sb);

-

         Budget localBudget = {}, nonLocalBudget = {};

         GetBudget(&localBudget, &nonLocalBudget);

 

         TotalStatistics stats;

-        CalculateStatistics(stats);

+        DetailedStatistics customHeaps[2];

+        CalculateStatistics(stats, customHeaps);

 

-        json.BeginObject();

-

-        json.WriteString(L"Total");

-        json.AddDetailedStatisticsInfoObject(stats.Total);

-        for (size_t heapType = 0; heapType < HEAP_TYPE_COUNT; ++heapType)

-        {

-            json.WriteString(HeapTypeNames[heapType]);

-            json.AddDetailedStatisticsInfoObject(stats.HeapType[heapType]);

-        }

-

-        json.WriteString(L"Budget");

+        JsonWriter json(GetAllocs(), sb);

         json.BeginObject();

         {

-            json.WriteString(L"Local");

-            WriteBudgetToJson(json, localBudget);

-            json.WriteString(L"NonLocal");

-            WriteBudgetToJson(json, nonLocalBudget);

-        }

-        json.EndObject();

-

-        if (DetailedMap)

-        {

-            json.WriteString(L"DetailedMap");

+            json.WriteString(L"General");

             json.BeginObject();

+            {

+                json.WriteString(L"API");

+                json.WriteString(L"Direct3D 12");

+

+                json.WriteString(L"GPU");

+                json.WriteString(m_AdapterDesc.Description);

+

+                json.WriteString(L"DedicatedVideoMemory");

+                json.WriteNumber(m_AdapterDesc.DedicatedVideoMemory);

+                json.WriteString(L"DedicatedSystemMemory");

+                json.WriteNumber(m_AdapterDesc.DedicatedSystemMemory);

+                json.WriteString(L"SharedSystemMemory");

+                json.WriteNumber(m_AdapterDesc.SharedSystemMemory);

+                

+                json.WriteString(L"ResourceHeapTier");

+                json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceHeapTier));

+

+                json.WriteString(L"ResourceBindingTier");

+                json.WriteNumber(static_cast<UINT>(m_D3D12Options.ResourceBindingTier));

+

+                json.WriteString(L"TiledResourcesTier");

+                json.WriteNumber(static_cast<UINT>(m_D3D12Options.TiledResourcesTier));

+

+                json.WriteString(L"TileBasedRenderer");

+                json.WriteBool(m_D3D12Architecture.TileBasedRenderer);

+

+                json.WriteString(L"UMA");

+                json.WriteBool(m_D3D12Architecture.UMA);

+                json.WriteString(L"CacheCoherentUMA");

+                json.WriteBool(m_D3D12Architecture.CacheCoherentUMA);

+            }

+            json.EndObject();

+        }

+        {

+            json.WriteString(L"Total");

+            json.AddDetailedStatisticsInfoObject(stats.Total);

+        }

+        {

+            json.WriteString(L"MemoryInfo");

+            json.BeginObject();

+            {

+                json.WriteString(L"L0");

+                json.BeginObject();

+                {

+                    json.WriteString(L"Budget");

+                    WriteBudgetToJson(json, IsUMA() ? localBudget : nonLocalBudget); // When UMA device only L0 present as local

+

+                    json.WriteString(L"Stats");

+                    json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[!IsUMA()]);

+

+                    json.WriteString(L"MemoryPools");

+                    json.BeginObject();

+                    {

+                        if (IsUMA())

+                        {

+                            json.WriteString(L"DEFAULT");

+                            json.BeginObject();

+                            {

+                                json.WriteString(L"Flags");

+                                json.BeginArray(true);

+                                json.EndArray();

+                                json.WriteString(L"Stats");

+                                json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);

+                            }

+                            json.EndObject();

+                        }

+                        json.WriteString(L"UPLOAD");

+                        json.BeginObject();

+                        {

+                            json.WriteString(L"Flags");

+                            json.BeginArray(true);

+                            json.EndArray();

+                            json.WriteString(L"Stats");

+                            json.AddDetailedStatisticsInfoObject(stats.HeapType[1]);

+                        }

+                        json.EndObject();

+

+                        json.WriteString(L"READBACK");

+                        json.BeginObject();

+                        {

+                            json.WriteString(L"Flags");

+                            json.BeginArray(true);

+                            json.EndArray();

+                            json.WriteString(L"Stats");

+                            json.AddDetailedStatisticsInfoObject(stats.HeapType[2]);

+                        }

+                        json.EndObject();

+

+                        json.WriteString(L"CUSTOM");

+                        json.BeginObject();

+                        {

+                            json.WriteString(L"Flags");

+                            json.BeginArray(true);

+                            json.EndArray();

+                            json.WriteString(L"Stats");

+                            json.AddDetailedStatisticsInfoObject(customHeaps[!IsUMA()]);

+                        }

+                        json.EndObject();

+                    }

+                    json.EndObject();

+                }

+                json.EndObject();

+                if (!IsUMA())

+                {

+                    json.WriteString(L"L1");

+                    json.BeginObject();

+                    {

+                        json.WriteString(L"Flags");

+                        json.BeginArray(true);

+                        json.EndArray();

+

+                        json.WriteString(L"Size");

+                        json.WriteNumber(0U);

+

+                        json.WriteString(L"Budget");

+                        WriteBudgetToJson(json, localBudget);

+

+                        json.WriteString(L"Stats");

+                        json.AddDetailedStatisticsInfoObject(stats.MemorySegmentGroup[0]);

+

+                        json.WriteString(L"MemoryPools");

+                        json.BeginObject();

+                        {

+                            json.WriteString(L"DEFAULT");

+                            json.BeginObject();

+                            {

+                                json.WriteString(L"Flags");

+                                json.BeginArray(true);

+                                json.EndArray();

+                                json.WriteString(L"Stats");

+                                json.AddDetailedStatisticsInfoObject(stats.HeapType[0]);

+                            }

+                            json.EndObject();

+

+                            json.WriteString(L"CUSTOM");

+                            json.BeginObject();

+                            {

+                                json.WriteString(L"Flags");

+                                json.BeginArray(true);

+                                json.EndArray();

+                                json.WriteString(L"Stats");

+                                json.AddDetailedStatisticsInfoObject(customHeaps[0]);

+                            }

+                            json.EndObject();

+                        }

+                        json.EndObject();

+                    }

+                    json.EndObject();

+                }

+            }

+            json.EndObject();

+        }

+

+        if (detailedMap)

+        {

+            const auto writeHeapInfo = [&](BlockVector* blockVector, CommittedAllocationList* committedAllocs, bool customHeap)

+            {

+                D3D12MA_ASSERT(blockVector);

+

+                D3D12_HEAP_FLAGS flags = blockVector->GetHeapFlags();

+                json.WriteString(L"Flags");

+                json.BeginArray(true);

+                {

+                    if (flags & D3D12_HEAP_FLAG_SHARED)

+                        json.WriteString(L"HEAP_FLAG_SHARED");

+                    if (flags & D3D12_HEAP_FLAG_ALLOW_DISPLAY)

+                        json.WriteString(L"HEAP_FLAG_ALLOW_DISPLAY");

+                    if (flags & D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER)

+                        json.WriteString(L"HEAP_FLAG_CROSS_ADAPTER");

+                    if (flags & D3D12_HEAP_FLAG_HARDWARE_PROTECTED)

+                        json.WriteString(L"HEAP_FLAG_HARDWARE_PROTECTED");

+                    if (flags & D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH)

+                        json.WriteString(L"HEAP_FLAG_ALLOW_WRITE_WATCH");

+                    if (flags & D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS)

+                        json.WriteString(L"HEAP_FLAG_ALLOW_SHADER_ATOMICS");

+#ifdef __ID3D12Device8_INTERFACE_DEFINED__

+                    if (flags & D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT)

+                        json.WriteString(L"HEAP_FLAG_CREATE_NOT_RESIDENT");

+                    if (flags & D3D12_HEAP_FLAG_CREATE_NOT_ZEROED)

+                        json.WriteString(L"HEAP_FLAG_CREATE_NOT_ZEROED");

+#endif

+

+                    if (flags & D3D12_HEAP_FLAG_DENY_BUFFERS)

+                        json.WriteString(L"HEAP_FLAG_DENY_BUFFERS");

+                    if (flags & D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES)

+                        json.WriteString(L"HEAP_FLAG_DENY_RT_DS_TEXTURES");

+                    if (flags & D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES)

+                        json.WriteString(L"HEAP_FLAG_DENY_NON_RT_DS_TEXTURES");

+

+                    flags &= ~(D3D12_HEAP_FLAG_SHARED

+                        | D3D12_HEAP_FLAG_DENY_BUFFERS

+                        | D3D12_HEAP_FLAG_ALLOW_DISPLAY

+                        | D3D12_HEAP_FLAG_SHARED_CROSS_ADAPTER

+                        | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES

+                        | D3D12_HEAP_FLAG_DENY_NON_RT_DS_TEXTURES

+                        | D3D12_HEAP_FLAG_HARDWARE_PROTECTED

+                        | D3D12_HEAP_FLAG_ALLOW_WRITE_WATCH

+                        | D3D12_HEAP_FLAG_ALLOW_SHADER_ATOMICS);

+#ifdef __ID3D12Device8_INTERFACE_DEFINED__

+                    flags &= ~(D3D12_HEAP_FLAG_CREATE_NOT_RESIDENT

+                        | D3D12_HEAP_FLAG_CREATE_NOT_ZEROED);

+#endif

+                    if (flags != 0)

+                        json.WriteNumber((UINT)flags);

+

+                    if (customHeap)

+                    {

+                        const D3D12_HEAP_PROPERTIES& properties = blockVector->GetHeapProperties();

+                        switch (properties.MemoryPoolPreference)

+                        {

+                        default:

+                            D3D12MA_ASSERT(0);

+                        case D3D12_MEMORY_POOL_UNKNOWN:

+                            json.WriteString(L"MEMORY_POOL_UNKNOWN");

+                            break;

+                        case D3D12_MEMORY_POOL_L0:

+                            json.WriteString(L"MEMORY_POOL_L0");

+                            break;

+                        case D3D12_MEMORY_POOL_L1:

+                            json.WriteString(L"MEMORY_POOL_L1");

+                            break;

+                        }

+                        switch (properties.CPUPageProperty)

+                        {

+                        default:

+                            D3D12MA_ASSERT(0);

+                        case D3D12_CPU_PAGE_PROPERTY_UNKNOWN:

+                            json.WriteString(L"CPU_PAGE_PROPERTY_UNKNOWN");

+                            break;

+                        case D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE:

+                            json.WriteString(L"CPU_PAGE_PROPERTY_NOT_AVAILABLE");

+                            break;

+                        case D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE:

+                            json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_COMBINE");

+                            break;

+                        case D3D12_CPU_PAGE_PROPERTY_WRITE_BACK:

+                            json.WriteString(L"CPU_PAGE_PROPERTY_WRITE_BACK");

+                            break;

+                        }

+                    }

+                }

+                json.EndArray();

+

+                json.WriteString(L"PreferredBlockSize");

+                json.WriteNumber(blockVector->GetPreferredBlockSize());

+

+                json.WriteString(L"Blocks");

+                blockVector->WriteBlockInfoToJson(json);

+

+                json.WriteString(L"DedicatedAllocations");

+                json.BeginArray();

+                if (committedAllocs)

+                    committedAllocs->BuildStatsString(json);

+                json.EndArray();

+            };

 

             json.WriteString(L"DefaultPools");

             json.BeginObject();

-

-            if (SupportsResourceHeapTier2())

             {

-                for (size_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)

+                if (SupportsResourceHeapTier2())

                 {

-                    json.WriteString(HeapTypeNames[heapType]);

-                    json.BeginObject();

-

-                    json.WriteString(L"Blocks");

-

-                    BlockVector* blockVector = m_BlockVectors[heapType];

-                    D3D12MA_ASSERT(blockVector);

-                    blockVector->WriteBlockInfoToJson(json);

-

-                    json.EndObject(); // heap name

-                }

-            }

-            else

-            {

-                for (size_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)

-                {

-                    for (size_t heapSubType = 0; heapSubType < 3; ++heapSubType)

+                    for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)

                     {

-                        static const WCHAR* const heapSubTypeName[] = {

-                            L" + buffer",

-                            L" + texture",

-                            L" + texture RT or DS",

-                        };

-                        json.BeginString();

-                        json.ContinueString(HeapTypeNames[heapType]);

-                        json.ContinueString(heapSubTypeName[heapSubType]);

-                        json.EndString();

+                        json.WriteString(HeapTypeNames[heapType]);

                         json.BeginObject();

-

-                        json.WriteString(L"Blocks");

-

-                        BlockVector* blockVector = m_BlockVectors[heapType * 3 + heapSubType];

-                        D3D12MA_ASSERT(blockVector);

-                        blockVector->WriteBlockInfoToJson(json);

-

-                        json.EndObject(); // heap name

+                        writeHeapInfo(m_BlockVectors[heapType], m_CommittedAllocations + heapType, false);

+                        json.EndObject();

                     }

                 }

-            }

-

-            json.EndObject(); // DefaultPools

-

-            json.WriteString(L"CommittedAllocations");

-            json.BeginObject();

-

-            for (size_t heapTypeIndex = 0; heapTypeIndex < STANDARD_HEAP_TYPE_COUNT; ++heapTypeIndex)

-            {

-                json.WriteString(HeapTypeNames[heapTypeIndex]);

-                json.BeginArray();

-                m_CommittedAllocations[heapTypeIndex].BuildStatsString(json);

-                json.EndArray();

-            }

-

-            json.EndObject(); // CommittedAllocations

-

-            json.WriteString(L"Pools");

-            json.BeginObject();

-

-            for (size_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)

-            {

-                json.WriteString(HeapTypeNames[heapTypeIndex]);

-                json.BeginArray();

-                MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);

-                size_t index = 0;

-                for (auto* item = m_Pools[heapTypeIndex].Front(); item != nullptr; item = PoolList::GetNext(item))

+                else

                 {

-                    json.BeginObject();

-                    json.WriteString(L"Name");

-                    if (item->GetName() != nullptr)

+                    for (uint8_t heapType = 0; heapType < STANDARD_HEAP_TYPE_COUNT; ++heapType)

                     {

-                        json.WriteString(item->GetName());

+                        for (uint8_t heapSubType = 0; heapSubType < 3; ++heapSubType)

+                        {

+                            static const WCHAR* const heapSubTypeName[] = {

+                                L" - Buffers",

+                                L" - Textures",

+                                L" - Textures RT/DS",

+                            };

+                            json.BeginString(HeapTypeNames[heapType]);

+                            json.EndString(heapSubTypeName[heapSubType]);

+

+                            json.BeginObject();

+                            writeHeapInfo(m_BlockVectors[heapType + heapSubType], m_CommittedAllocations + heapType, false);

+                            json.EndObject();

+                        }

                     }

-                    else

-                    {

-                        json.BeginString();

-                        json.ContinueString(index);

-                        json.EndString();

-                    }

-                    ++index;

-

-                    json.WriteString(L"Blocks");

-                    item->GetBlockVector()->WriteBlockInfoToJson(json);

-

-                    json.WriteString(L"CommittedAllocations");

-                    json.BeginArray();

-                    if (item->SupportsCommittedAllocations())

-                        item->GetCommittedAllocationList()->BuildStatsString(json);

-                    json.EndArray();

-

-                    json.EndObject();

                 }

-                json.EndArray();

             }

+            json.EndObject();

 

-            json.EndObject(); // Pools

+            json.WriteString(L"CustomPools");

+            json.BeginObject();

+            for (uint8_t heapTypeIndex = 0; heapTypeIndex < HEAP_TYPE_COUNT; ++heapTypeIndex)

+            {

+                MutexLockRead mutex(m_PoolsMutex[heapTypeIndex], m_UseMutex);

+                auto* item = m_Pools[heapTypeIndex].Front();

+                if (item != NULL)

+                {

+                    size_t index = 0;

+                    json.WriteString(HeapTypeNames[heapTypeIndex]);

+                    json.BeginArray();

+                    do

+                    {

+                        json.BeginObject();

+                        json.WriteString(L"Name");

+                        json.BeginString();

+                        json.ContinueString(index++);

+                        if (item->GetName())

+                        {

+                            json.WriteString(L" - ");

+                            json.WriteString(item->GetName());

+                        }

+                        json.EndString();

 

-            json.EndObject(); // DetailedMap

+                        writeHeapInfo(item->GetBlockVector(), item->GetCommittedAllocationList(), heapTypeIndex == 3);

+                        json.EndObject();

+                    } while ((item = PoolList::GetNext(item)) != NULL);

+                    json.EndArray();

+                }

+            }

+            json.EndObject();

         }

         json.EndObject();

     }

 

     const size_t length = sb.GetLength();

-    WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 1);

-    memcpy(result, sb.GetData(), length * sizeof(WCHAR));

-    result[length] = L'\0';

+    WCHAR* result = AllocateArray<WCHAR>(GetAllocs(), length + 2);

+    result[0] = 0xFEFF;

+    memcpy(result + 1, sb.GetData(), length * sizeof(WCHAR));

+    result[length + 1] = L'\0';

     *ppStatsString = result;

 }

 

@@ -7710,18 +7915,10 @@
 {

     json.BeginObject();

     {

-        json.WriteString(L"BlockCount");

-        json.WriteNumber(budget.Stats.BlockCount);

-        json.WriteString(L"AllocationCount");

-        json.WriteNumber(budget.Stats.AllocationCount);

-        json.WriteString(L"BlockBytes");

-        json.WriteNumber(budget.Stats.BlockBytes);

-        json.WriteString(L"AllocationBytes");

-        json.WriteNumber(budget.Stats.AllocationBytes);

-        json.WriteString(L"UsageBytes");

-        json.WriteNumber(budget.UsageBytes);

         json.WriteString(L"BudgetBytes");

         json.WriteNumber(budget.BudgetBytes);

+        json.WriteString(L"UsageBytes");

+        json.WriteNumber(budget.UsageBytes);

     }

     json.EndObject();

 }

@@ -8254,7 +8451,9 @@
         json.ContinueString(pBlock->GetId());

         json.EndString();

 

+        json.BeginObject();

         pBlock->m_pMetadata->WriteAllocationInfoToJson(json);

+        json.EndObject();

     }

 

     json.EndObject();

@@ -9423,7 +9622,6 @@
     m_Size{ size },

     m_Alignment{ alignment },

     m_Resource{ NULL },

-    m_CreationFrameIndex{ allocator->GetCurrentFrameIndex() },

     m_Name{ NULL }

 {

     D3D12MA_ASSERT(allocator);

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 33790cc..9d8755a 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -439,51 +439,194 @@
     }

 }

 

-static void TestFrameIndexAndJson(const TestContext& ctx)

+static void TestJson(const TestContext& ctx)

 {

-    const UINT64 bufSize = 32ull * 1024;

+    wprintf(L"Test JSON\n");

+

+    std::vector<ComPtr<D3D12MA::Pool>> pools;

+    std::vector<ComPtr<D3D12MA::Allocation>> allocs;

 

     D3D12MA::ALLOCATION_DESC allocDesc = {};

-    allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;

-    allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;

+    D3D12_RESOURCE_DESC resDesc = {};

+    resDesc.Alignment = 0;

+    resDesc.MipLevels = 1;

+    resDesc.SampleDesc.Count = 1;

+    resDesc.SampleDesc.Quality = 0;

 

-    D3D12_RESOURCE_DESC resourceDesc;

-    FillResourceDescForBuffer(resourceDesc, bufSize);

+    D3D12_RESOURCE_ALLOCATION_INFO allocInfo = {};

+    allocInfo.Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

+    allocInfo.SizeInBytes = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT;

 

-    const UINT BEGIN_INDEX = 10;

-    const UINT END_INDEX = 20;

-    for (UINT frameIndex = BEGIN_INDEX; frameIndex < END_INDEX; ++frameIndex)

+    // Select if using custom pool or default

+    for (UINT8 poolType = 0; poolType < 2; ++poolType)

     {

-        ctx.allocator->SetCurrentFrameIndex(frameIndex);

-        D3D12MA::Allocation* alloc = nullptr;

-        CHECK_HR(ctx.allocator->CreateResource(

-            &allocDesc,

-            &resourceDesc,

-            D3D12_RESOURCE_STATE_GENERIC_READ,

-            NULL,

-            &alloc,

-            IID_NULL,

-            NULL));

-

-        WCHAR* statsString;

-        ctx.allocator->BuildStatsString(&statsString, TRUE);

-        const UINT BUFFER_SIZE = 1024;

-        WCHAR buffer[BUFFER_SIZE];

-        for (UINT testIndex = BEGIN_INDEX; testIndex < END_INDEX; ++testIndex)

+        // Select different heaps

+        for (UINT8 heapType = 0; heapType < 5; ++heapType)

         {

-            swprintf(buffer, BUFFER_SIZE, L"\"CreationFrameIndex\": %u", testIndex);

-            if (testIndex == frameIndex)

+            D3D12_RESOURCE_STATES state;

+            D3D12_CPU_PAGE_PROPERTY cpuPageType = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;

+            D3D12_MEMORY_POOL memoryPool = D3D12_MEMORY_POOL_UNKNOWN;

+            switch (heapType)

             {

-                CHECK_BOOL(wcsstr(statsString, buffer) != NULL);

+            case 0:

+                allocDesc.HeapType = D3D12_HEAP_TYPE_DEFAULT;

+                state = D3D12_RESOURCE_STATE_COMMON;

+                break;

+            case 1:

+                allocDesc.HeapType = D3D12_HEAP_TYPE_UPLOAD;

+                state = D3D12_RESOURCE_STATE_GENERIC_READ;

+                break;

+            case 2:

+                allocDesc.HeapType = D3D12_HEAP_TYPE_READBACK;

+                state = D3D12_RESOURCE_STATE_COPY_DEST;

+                break;

+            case 3:

+                allocDesc.HeapType = D3D12_HEAP_TYPE_CUSTOM;

+                state = D3D12_RESOURCE_STATE_COMMON;

+                cpuPageType = D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE;

+                memoryPool = ctx.allocator->IsUMA() ? D3D12_MEMORY_POOL_L0 : D3D12_MEMORY_POOL_L1;

+                break;

+            case 4:

+                allocDesc.HeapType = D3D12_HEAP_TYPE_CUSTOM;

+                state = D3D12_RESOURCE_STATE_GENERIC_READ;

+                cpuPageType = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE;

+                memoryPool = D3D12_MEMORY_POOL_L0;

+                break;

             }

-            else

+            // Skip custom heaps for default pools

+            if (poolType == 0 && heapType > 2)

+                continue;

+            const bool texturesPossible = heapType == 0 || heapType == 3;

+

+            // Select different resource region types

+            for (UINT8 resType = 0; resType < 3; ++resType)

             {

-                CHECK_BOOL(wcsstr(statsString, buffer) == NULL);

+                allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS;

+                D3D12_RESOURCE_FLAGS resFlags = D3D12_RESOURCE_FLAG_NONE;

+                if (texturesPossible)

+                {

+                    switch (resType)

+                    {

+                    case 1:

+                        allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES;

+                        break;

+                    case 2:

+                        allocDesc.ExtraHeapFlags = D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES;

+                        resFlags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;

+                        break;

+                    }

+                }

+

+                switch (poolType)

+                {

+                case 0:

+                    allocDesc.CustomPool = nullptr;

+                    break;

+                case 1:

+                {

+                    ComPtr<D3D12MA::Pool> pool;

+                    D3D12MA::POOL_DESC poolDesc = {};

+                    poolDesc.HeapFlags = allocDesc.ExtraHeapFlags;

+                    poolDesc.HeapProperties.Type = allocDesc.HeapType;

+                    poolDesc.HeapProperties.CPUPageProperty = cpuPageType;

+                    poolDesc.HeapProperties.MemoryPoolPreference = memoryPool;

+                    CHECK_HR(ctx.allocator->CreatePool(&poolDesc, &pool));

+

+                    allocDesc.CustomPool = pool.Get();

+                    pools.emplace_back(std::move(pool));

+                    break;

+                }

+                }

+

+                // Select different allocation flags

+                for (UINT8 allocFlag = 0; allocFlag < 2; ++allocFlag)

+                {

+                    switch (allocFlag)

+                    {

+                    case 0:

+                        allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_NONE;

+                        break;

+                    case 1:

+                        allocDesc.Flags = D3D12MA::ALLOCATION_FLAG_COMMITTED;

+                        break;

+                    }

+

+                    // Select different alloc types (block, buffer, texture, etc.)

+                    for (UINT8 allocType = 0; allocType < 5; ++allocType)

+                    {

+                        // Select different data stored in the allocation

+                        for (UINT8 data = 0; data < 4; ++data)

+                        {

+                            ComPtr<D3D12MA::Allocation> alloc;

+

+                            if (texturesPossible && resType != 0)

+                            {

+                                resDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;

+                                resDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;

+                                switch (allocType % 3)

+                                {

+                                case 0:

+                                    resDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE1D;

+                                    resDesc.Width = 512;

+                                    resDesc.Height = 1;

+                                    resDesc.DepthOrArraySize = 1;

+                                    resDesc.Flags = resFlags;

+                                    CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, state, nullptr, &alloc, IID_NULL, nullptr));

+                                    break;

+                                case 1:

+                                    resDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

+                                    resDesc.Width = 1024;

+                                    resDesc.Height = 512;

+                                    resDesc.DepthOrArraySize = 1;

+                                    resDesc.Flags = resFlags;

+                                    CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, state, nullptr, &alloc, IID_NULL, nullptr));

+                                    break;

+                                case 2:

+                                    resDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE3D;

+                                    resDesc.Width = 512;

+                                    resDesc.Height = 256;

+                                    resDesc.DepthOrArraySize = 128;

+                                    resDesc.Flags = resFlags;

+                                    CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, state, nullptr, &alloc, IID_NULL, nullptr));

+                                    break;

+                                }

+                            }

+                            else

+                            {

+                                switch (allocType % 2)

+                                {

+                                case 0:

+                                    CHECK_HR(ctx.allocator->AllocateMemory(&allocDesc, &allocInfo, &alloc));

+                                    break;

+                                case 1:

+                                    FillResourceDescForBuffer(resDesc, 1024);

+                                    CHECK_HR(ctx.allocator->CreateResource(&allocDesc, &resDesc, state, nullptr, &alloc, IID_NULL, nullptr));

+                                    break;

+                                }

+                            }

+

+                            switch (data)

+                            {

+                            case 1:

+                                alloc->SetPrivateData((void*)16112007);

+                                break;

+                            case 2:

+                                alloc->SetName(L"SHEPURD");

+                                break;

+                            case 3:

+                                alloc->SetPrivateData((void*)26012010);

+                                alloc->SetName(L"JOKER");

+                                break;

+                            }

+                            allocs.emplace_back(std::move(alloc));

+                        }

+                    }

+

+                }

             }

         }

-        ctx.allocator->FreeStatsString(statsString);

-        alloc->Release();

     }

+    SaveStatsStringToFile(ctx, L"JSON_D3D12.json");

 }

 

 static void TestCommittedResourcesAndJson(const TestContext& ctx)

@@ -2689,8 +2832,8 @@
     block->BuildStatsString(&json);

     {

         std::wstring str(json);

-        CHECK_BOOL(str.find(L"\"PrivateData\": 1") != std::wstring::npos);

-        CHECK_BOOL(str.find(L"\"PrivateData\": 2") != std::wstring::npos);

+        CHECK_BOOL(str.find(L"\"CustomData\": 1") != std::wstring::npos);

+        CHECK_BOOL(str.find(L"\"CustomData\": 2") != std::wstring::npos);

     }

     block->FreeStatsString(json);

 

@@ -3839,7 +3982,7 @@
     TestDebugMargin(ctx);

     TestDebugMarginNotInVirtualAllocator(ctx);

 #else

-    TestFrameIndexAndJson(ctx);

+    TestJson(ctx);

     TestCommittedResourcesAndJson(ctx);

     TestCustomHeapFlags(ctx);

     TestPlacedResources(ctx);

diff --git a/tools/D3d12maDumpVis/D3d12maDumpVis.py b/tools/D3d12maDumpVis/D3d12maDumpVis.py
deleted file mode 100644
index cbd5a3c..0000000
--- a/tools/D3d12maDumpVis/D3d12maDumpVis.py
+++ /dev/null
@@ -1,282 +0,0 @@
-#

-# Copyright (c) 2019-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 = 'D3D12MA Dump Visualization 1.0.0'

-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 D3D12 Memory Allocator JSON dump.')

-argParser.add_argument('DumpFile', type=argparse.FileType(mode='r', encoding='utf_16_le'), help='Path to source JSON file with memory dump created by 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)')

-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.get('Flags', 0)), int(objSuballoc.get('Layout', 0))))

-    dstBlockList.append(dstBlockObj)

-

-

-def GetDataForHeapType(sHeapType):

-    global data

-    if sHeapType in data:

-        return data[sHeapType]

-    else:

-        newHeapTypeData = {'CommittedAllocations':[], 'DefaultPoolBlocks':[], 'CustomPools':{}}

-        data[sHeapType] = newHeapTypeData

-        return newHeapTypeData

-

-

-# 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['CommittedAllocations']

-        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'])

-        for poolData in dictMemType['CustomPools'].values():

-            iImgSizeY += len(poolData) * (IMG_MARGIN * 2 + FONT_SIZE + MAP_SIZE)

-            for objBlock in poolData['Blocks']:

-                iMaxBlockSize = max(iMaxBlockSize, objBlock['Size'])

-            for tDedicatedAlloc in poolData['CommittedAllocations']:

-                iMaxBlockSize = max(iMaxBlockSize, tDedicatedAlloc[1])

-    fPixelsPerByte = (IMG_SIZE_X - IMG_MARGIN * 2) / float(iMaxBlockSize)

-    return iImgSizeY, fPixelsPerByte

-

-

-def TypeToColor(sType, iFlags, iLayout):

-    if sType == 'FREE':

-        return 220, 220, 220, 255

-    elif sType == 'BUFFER':

-        return 255, 255, 0, 255 # Yellow

-    elif sType == 'TEXTURE2D' or sType == 'TEXTURE1D' or sType == 'TEXTURE3D':

-        if iLayout != 0: # D3D12_TEXTURE_LAYOUT_UNKNOWN

-            return 0, 255, 0, 255 # Green

-        else:

-            if (iFlags & 0x2) != 0: # D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL

-                return 246, 128, 255, 255 # Pink

-            elif (iFlags & 0x5) != 0: # D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS

-                return 179, 179, 255, 255 # Blue

-            elif (iFlags & 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:

-        return 175, 175, 175, 255 # Gray

-    assert False

-    return 0, 0, 0, 255

-

-

-def DrawCommittedAllocationBlock(draw, y, tAlloc): 

-    global fPixelsPerByte

-    iSizeBytes = tAlloc[1]

-    iSizePixels = int(iSizeBytes * fPixelsPerByte)

-    draw.rectangle([IMG_MARGIN, y, IMG_MARGIN + iSizePixels, y + MAP_SIZE], fill=TypeToColor(tAlloc[0], tAlloc[2], tAlloc[3]), 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, 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:

-                iFlags = tSuballoc[2]

-                iLayout = tSuballoc[3]

-                draw.rectangle([IMG_MARGIN + iX, y, IMG_MARGIN + iXEnd, y + MAP_SIZE], fill=TypeToColor(sType, iFlags, iLayout), 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 KB" % iBytes

-    iBytes /= 1024

-    if iBytes < 1024:

-        return "%d MB" % iBytes

-    iBytes /= 1024

-    return "%d GB" % iBytes

-

-

-jsonSrc = json.load(args.DumpFile)

-objDetailedMap = jsonSrc['DetailedMap']

-if 'CommittedAllocations' in objDetailedMap:

-    for tType in objDetailedMap['CommittedAllocations'].items():

-        sHeapType = tType[0]

-        typeData = GetDataForHeapType(sHeapType)

-        for objAlloc in tType[1]:

-            typeData['CommittedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc.get('Flags', 0)), int(objAlloc.get('Layout', 0))))

-if 'DefaultPools' in objDetailedMap:

-    for tType in objDetailedMap['DefaultPools'].items():

-        sHeapType = tType[0]

-        typeData = GetDataForHeapType(sHeapType)

-        for sBlockId, objBlock in tType[1]['Blocks'].items():

-            ProcessBlock(typeData['DefaultPoolBlocks'], int(sBlockId), objBlock, '')

-if 'Pools' in objDetailedMap:

-    for tType in objDetailedMap['Pools'].items():

-        sHeapType = tType[0]

-        typeData = GetDataForHeapType(sHeapType)

-        for pool in tType[1]:

-            typeData['CustomPools'][pool['Name']] = {'Blocks':[], 'CommittedAllocations':[]}

-            for sBlockId, objBlock in pool['Blocks'].items():

-                ProcessBlock(typeData['CustomPools'][pool['Name']]['Blocks'], int(sBlockId), objBlock, '')

-            for objAlloc in pool['CommittedAllocations']:

-                typeData['CustomPools'][pool['Name']]['CommittedAllocations'].append((objAlloc['Type'], int(objAlloc['Size']), int(objAlloc.get('Flags', 0)), int(objAlloc.get('Layout', 0))))

-

-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 sHeapType in data.keys():

-    dictMemType = data[sHeapType]

-    draw.text((IMG_MARGIN, y), sHeapType, fill=COLOR_TEXT_H1, font=font)

-    y += FONT_SIZE + IMG_MARGIN

-    index = 0

-    for tCommittedAlloc in dictMemType['CommittedAllocations']:

-        draw.text((IMG_MARGIN, y), "Committed allocation %d" % index, fill=COLOR_TEXT_H2, font=font)

-        y += FONT_SIZE + IMG_MARGIN

-        DrawCommittedAllocationBlock(draw, y, tCommittedAlloc)

-        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

-    for sPoolName, pool in dictMemType['CustomPools'].items():

-        index = 0

-        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

-        index = 0

-        for objAlloc in pool['CommittedAllocations']:

-            draw.text((IMG_MARGIN, y), "Custom pool %s%s committed allocation %d" % (sPoolName, sAlgorithm, index), fill=COLOR_TEXT_H2, font=font)

-            y += FONT_SIZE + IMG_MARGIN

-            DrawCommittedAllocationBlock(draw, y, objAlloc)

-            y += MAP_SIZE + IMG_MARGIN

-            index += 1

-del draw

-img.save(args.output)

-

-"""

-Main data structure - variable `data` - is a dictionary. Key is string - heap type ('DEFAULT', 'UPLOAD', or 'READBACK'). Value is dictionary of:

-- Fixed key 'CommittedAllocations'. Value is list of tuples, each containing:

-    - [0]: Type : string

-    - [1]: Size : integer

-    - [2]: Flags : integer (0 if unknown)

-    - [3]: Layout : 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 'CommittedAllocations'. Value is list of tuples as above.

-"""

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..0213f52
--- /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.
+* ![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 or 1D/2D/3D texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL.
+* ![Image 2](README_files/Legend_Image_2.png "Image 2") Image with OPTIMAL tiling and usage containing INPUT_ATTACHMENT, TRANSIENT_ATTACHMENT or COLOR_ATTACHMENT, or 1D/2D/3D texture with usage containing D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS.
+* ![Image 3](README_files/Legend_Image_3.png "Image 3") Image with OPTIMAL tiling and usage containing SAMPLED or 1D/2D/3D texture with usage not containing D3D12_RESOURCE_FLAG_DENY_SHARED_RESOURCE.
+* ![Image 4](README_files/Legend_Image_4.png "Image 4") Other image with OPTIMAL tiling or 1D/2D/3D texture.
+* ![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/GpuMemDumpVis/README_files/ExampleOutput.png b/tools/GpuMemDumpVis/README_files/ExampleOutput.png
new file mode 100644
index 0000000..2646ed2
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/ExampleOutput.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Bkg.png b/tools/GpuMemDumpVis/README_files/Legend_Bkg.png
new file mode 100644
index 0000000..f8fd89f
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Bkg.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png
new file mode 100644
index 0000000..3805225
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png
new file mode 100644
index 0000000..bc3773f
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png
new file mode 100644
index 0000000..992d8b2
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png b/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png
new file mode 100644
index 0000000..d29f54e
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Details.png b/tools/GpuMemDumpVis/README_files/Legend_Details.png
new file mode 100644
index 0000000..a9c8535
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Details.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_1.png b/tools/GpuMemDumpVis/README_files/Legend_Image_1.png
new file mode 100644
index 0000000..dc180af
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_1.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_2.png b/tools/GpuMemDumpVis/README_files/Legend_Image_2.png
new file mode 100644
index 0000000..fc35c7c
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_2.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_3.png b/tools/GpuMemDumpVis/README_files/Legend_Image_3.png
new file mode 100644
index 0000000..b69849d
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_3.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_4.png b/tools/GpuMemDumpVis/README_files/Legend_Image_4.png
new file mode 100644
index 0000000..7f3980e
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_4.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png b/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png
new file mode 100644
index 0000000..36d8be5
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png b/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png
new file mode 100644
index 0000000..f3f40ec
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Texture_1.png b/tools/GpuMemDumpVis/README_files/Legend_Texture_1.png
new file mode 100644
index 0000000..dc180af
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Texture_1.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Texture_2.png b/tools/GpuMemDumpVis/README_files/Legend_Texture_2.png
new file mode 100644
index 0000000..fc35c7c
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Texture_2.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Texture_3.png b/tools/GpuMemDumpVis/README_files/Legend_Texture_3.png
new file mode 100644
index 0000000..b69849d
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Texture_3.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Texture_4.png b/tools/GpuMemDumpVis/README_files/Legend_Texture_4.png
new file mode 100644
index 0000000..7f3980e
--- /dev/null
+++ b/tools/GpuMemDumpVis/README_files/Legend_Texture_4.png
Binary files differ
diff --git a/tools/GpuMemDumpVis/README_files/Legend_Unknown.png b/tools/GpuMemDumpVis/README_files/Legend_Unknown.png
new file mode 100644
index 0000000..3053726
--- /dev/null
+++ b/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