Fixes in JSON dump format. Fixes in GpuMemDumpVis.py regarding image height calculation.

Added GpuMemDump.schema.json.
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index d145c5c..2555ef6 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -27,6 +27,7 @@
 #include <algorithm>

 #include <utility>

 #include <cstdlib>

+#include <cstdint>

 #include <malloc.h> // for _aligned_malloc, _aligned_free

 #ifndef _WIN32

     #include <shared_mutex>

@@ -377,7 +378,15 @@
 static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; }

 template <typename T>

 static T DivideRoundingUp(T x, T y) { return (x + y - 1) / y; }

-    

+

+static WCHAR HexDigitToChar(UINT8 digit)

+{

+    if(digit < 10)

+        return L'0' + digit;

+    else

+        return L'A' + (digit - 10);

+}

+

 /*

 Performs binary search and returns iterator to first element that is greater or

 equal to `key`, according to comparison `cmp`.

@@ -1180,6 +1189,7 @@
     void AddNewLine() { Add(L'\n'); }

     void AddNumber(UINT num);

     void AddNumber(UINT64 num);

+    void AddPointer(const void* ptr);

 

 private:

     Vector<WCHAR> m_Data;

@@ -1224,6 +1234,22 @@
     while (num);

     Add(p);

 }

+

+void StringBuilder::AddPointer(const void* ptr)

+{

+    WCHAR buf[21];

+    uintptr_t num = (uintptr_t)ptr;

+    buf[20] = L'\0';

+    WCHAR *p = &buf[20];

+    do

+    {

+        *--p = HexDigitToChar((UINT8)(num & 0xF));

+        num >>= 4;

+    }

+    while (num);

+    Add(p);

+}

+

 #endif // _D3D12MA_STRING_BUILDER_FUNCTIONS

 #endif // _D3D12MA_STRING_BUILDER

 

@@ -1267,6 +1293,7 @@
     // Posts next part of an open string. The number is converted to decimal characters.

     void ContinueString(UINT num);

     void ContinueString(UINT64 num);

+    void ContinueString_Pointer(const void* ptr);

     // Posts next part of an open string. Pointer value is converted to characters

     // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00

     // void ContinueString_Pointer(const void* ptr);

@@ -1452,6 +1479,12 @@
     m_SB.AddNumber(num);

 }

 

+void JsonWriter::ContinueString_Pointer(const void* ptr)

+{

+    D3D12MA_ASSERT(m_InsideString);

+    m_SB.AddPointer(ptr);

+}

+

 void JsonWriter::EndString(LPCWSTR pStr)

 {

     D3D12MA_ASSERT(m_InsideString);

@@ -1524,7 +1557,9 @@
     if (privateData)

     {

         WriteString(L"CustomData");

-        WriteNumber((uintptr_t)privateData);

+        BeginString();

+        ContinueString_Pointer(privateData);

+        EndString();

     }

 

     LPCWSTR name = alloc.GetName();

@@ -7196,9 +7231,6 @@
                             json.WriteString(L"DEFAULT");

                             json.BeginObject();

                             {

-                                json.WriteString(L"Flags");

-                                json.BeginArray(true);

-                                json.EndArray();

                                 json.WriteString(L"Stats");

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

                             }

@@ -7207,9 +7239,6 @@
                         json.WriteString(L"UPLOAD");

                         json.BeginObject();

                         {

-                            json.WriteString(L"Flags");

-                            json.BeginArray(true);

-                            json.EndArray();

                             json.WriteString(L"Stats");

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

                         }

@@ -7218,9 +7247,6 @@
                         json.WriteString(L"READBACK");

                         json.BeginObject();

                         {

-                            json.WriteString(L"Flags");

-                            json.BeginArray(true);

-                            json.EndArray();

                             json.WriteString(L"Stats");

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

                         }

@@ -7229,9 +7255,6 @@
                         json.WriteString(L"CUSTOM");

                         json.BeginObject();

                         {

-                            json.WriteString(L"Flags");

-                            json.BeginArray(true);

-                            json.EndArray();

                             json.WriteString(L"Stats");

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

                         }

@@ -7245,13 +7268,6 @@
                     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);

 

@@ -7264,9 +7280,6 @@
                             json.WriteString(L"DEFAULT");

                             json.BeginObject();

                             {

-                                json.WriteString(L"Flags");

-                                json.BeginArray(true);

-                                json.EndArray();

                                 json.WriteString(L"Stats");

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

                             }

@@ -7275,9 +7288,6 @@
                             json.WriteString(L"CUSTOM");

                             json.BeginObject();

                             {

-                                json.WriteString(L"Flags");

-                                json.BeginArray(true);

-                                json.EndArray();

                                 json.WriteString(L"Stats");

                                 json.AddDetailedStatisticsInfoObject(customHeaps[0]);

                             }

diff --git a/src/Tests.cpp b/src/Tests.cpp
index 31804bb..1f77b87 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -322,10 +322,10 @@
     }

 }

 

-static void SaveStatsStringToFile(const TestContext& ctx, const wchar_t* dstFilePath)

+static void SaveStatsStringToFile(const TestContext& ctx, const wchar_t* dstFilePath, BOOL detailed = TRUE)

 {

     WCHAR* s = nullptr;

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

+    ctx.allocator->BuildStatsString(&s, detailed);

     SaveFile(dstFilePath, s, wcslen(s) * sizeof(WCHAR));

     ctx.allocator->FreeStatsString(s);

 }

diff --git a/tools/GpuMemDumpVis/GpuMemDump.schema.json b/tools/GpuMemDumpVis/GpuMemDump.schema.json
new file mode 100644
index 0000000..4d3331c
--- /dev/null
+++ b/tools/GpuMemDumpVis/GpuMemDump.schema.json
@@ -0,0 +1,163 @@
+{

+    "$id": "https://gpuopen.com/vulkan-memory-allocator/schemas/GpuMemDump",

+    "$schema": "https://json-schema.org/draft/2020-12/schema",

+    "type": "object",

+    "properties": {

+        "General": {

+            "type": "object",

+            "properties": {

+                "API": {"type": "string", "enum": ["Vulkan", "Direct3D 12"]},

+                "GPU": {"type": "string"}

+            },

+            "required": ["API", "GPU"]

+        },

+        "Total": {"$ref": "#/$defs/Stats"},

+        "MemoryInfo": {

+            "type": "object",

+            "additionalProperties": {

+                "type": "object",

+                "properties": {

+                    "Flags": {

+                        "type": "array",

+                        "items": {"type": "string"}

+                    },

+                    "Size": {"type": "integer"},

+                    "Budget": {

+                        "type": "object",

+                        "properties": {

+                            "BudgetBytes": {"type": "integer"},

+                            "UsageBytes": {"type": "integer"}

+                        },

+                        "additionalProperties": false

+                    },

+                    "Stats": {"$ref": "#/$defs/Stats"},

+                    "MemoryPools": {

+                        "type": "object",

+                        "additionalProperties": {

+                            "type": "object",

+                            "properties": {

+                                "Flags": {

+                                    "type": "array",

+                                    "items": {"type": "string"}

+                                },

+                                "Stats": {"$ref": "#/$defs/Stats"}

+                            },

+                            "additionalProperties": false

+                        }

+                    }

+                },

+                "required": ["Budget", "Stats"],

+                "additionalProperties": false

+            }

+        },

+        "DefaultPools": {

+            "type": "object",

+            "additionalProperties": {

+                "type": "object",

+                "properties": {

+                    "PreferredBlockSize": {"type": "integer"},

+                    "Blocks": {

+                        "type": "object",

+                        "propertyNames": {"pattern": "[0-9]+"},

+                        "additionalProperties": {"$ref": "#/$defs/Block"}

+                    },

+                    "DedicatedAllocations": {

+                        "type": "array",

+                        "items": {"$ref": "#/$defs/DedicatedAllocation"}

+                    }

+                }

+            }

+        },

+        "CustomPools": {

+            "type": "object",

+            "additionalProperties": {

+                "type": "array",

+                "items": {

+                    "type": "object",

+                    "properties": {

+                        "Name": {"type": "string"},

+                        "Flags": {"type": "array"},

+                        "PreferredBlockSize": {"type": "integer"},

+                        "Blocks": {

+                            "type": "object",

+                            "additionalProperties": {"$ref": "#/$defs/Block"}

+                        },

+                        "DedicatedAllocations": {

+                            "type": "array",

+                            "items": {"$ref": "#/$defs/DedicatedAllocation"}

+                        }

+                    },

+                    "required": ["PreferredBlockSize"],

+                    "additionalProperties": false

+                }

+            }

+        }

+    },

+    "required": ["General", "Total", "MemoryInfo"],

+    "additionalProperties": false,

+    "$defs": {

+        "CustomData": {

+            "type": "string",

+            "pattern": "^[0-9a-zA-Z]+$"

+        },

+        "Stats": {

+            "type": "object",

+            "properties": {

+                "BlockCount": {"type": "integer"},

+                "BlockBytes": {"type": "integer"},

+                "AllocationCount": {"type": "integer"},

+                "AllocationBytes": {"type": "integer"},

+                "UnusedRangeCount": {"type": "integer"},

+                "AllocationSizeMin": {"type": "integer"},

+                "AllocationSizeMax": {"type": "integer"},

+                "UnusedRangeSizeMin": {"type": "integer"},

+                "UnusedRangeSizeMax": {"type": "integer"}

+            },

+            "required": [

+                "BlockCount", "BlockBytes",

+                "AllocationCount", "AllocationBytes",

+                "UnusedRangeCount"

+            ],

+            "additionalProperties": false

+        },

+        "Block": {

+            "type": "object",

+            "properties": {

+                "MapRefCount": {"type": "integer"},

+                "TotalBytes": {"type": "integer"},

+                "UnusedBytes": {"type": "integer"},

+                "Allocations": {"type": "integer"},

+                "UnusedRanges": {"type": "integer"},

+                "Suballocations": {"type": "array", "items": {"$ref": "#/$defs/Suballocation"}}

+            },

+            "required": ["TotalBytes", "UnusedBytes", "Allocations", "UnusedRanges"]

+        },

+        "DedicatedAllocation": {

+            "type": "object",

+            "properties": {

+                "Type": {"type": "string"},

+                "Size": {"type": "integer"},

+                "Usage": {"type": "integer"},

+                "CustomData": {"$ref": "#/$defs/CustomData"},

+                "Name": {"type": "string"},

+                "Layout": {"type": "integer"}

+            },

+            "required": ["Type", "Size"],

+            "additionalProperties": false

+        },

+        "Suballocation": {

+            "type": "object",

+            "properties": {

+                "Offset": {"type": "integer"},

+                "Type": {"type": "string"},

+                "Size": {"type": "integer"},

+                "Usage": {"type": "integer"},

+                "CustomData": {"$ref": "#/$defs/CustomData"},

+                "Name": {"type": "string"},

+                "Layout": {"type": "integer"}

+            },

+            "required": ["Offset", "Type", "Size"],

+            "additionalProperties": false

+        }

+    }

+}

diff --git a/tools/GpuMemDumpVis/GpuMemDumpVis.py b/tools/GpuMemDumpVis/GpuMemDumpVis.py
index b225306..42800ea 100644
--- a/tools/GpuMemDumpVis/GpuMemDumpVis.py
+++ b/tools/GpuMemDumpVis/GpuMemDumpVis.py
@@ -106,9 +106,9 @@
     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)
+        height += FONT_SIZE + IMG_MARGIN # Memory pool title
+        height += len(poolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
+        height += len(poolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
         # Get longest block size
         for dedicatedAlloc in poolData['DedicatedAllocations']:
             maxBlockSize = max(maxBlockSize, dedicatedAlloc['Size'])
@@ -116,16 +116,15 @@
             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
+            height += len(customPoolData['Blocks']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
+            height += len(customPoolData['DedicatedAllocations']) * (FONT_SIZE + MAP_SIZE + IMG_MARGIN * 2)
             # 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)
+    return height, (IMG_WIDTH - IMG_MARGIN * 2) / float(maxBlockSize)
 
 def BytesToStr(bytes):
     if bytes < 1024:
@@ -306,7 +305,7 @@
                 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)
+                y += MAP_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)