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 #include #include +#include #include // for _aligned_malloc, _aligned_free #ifndef _WIN32 #include @@ -377,7 +378,15 @@ template static T RoundDiv(T x, T y) { return (x + (y / (T)2)) / y; } template 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 @@ public: void AddNewLine() { Add(L'\n'); } void AddNumber(UINT num); void AddNumber(UINT64 num); + void AddPointer(const void* ptr); private: Vector m_Data; @@ -1224,6 +1234,22 @@ void StringBuilder::AddNumber(UINT64 num) 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 @@ public: // 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 @@ void JsonWriter::ContinueString(UINT64 num) 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 @@ void JsonWriter::AddAllocationToObject(const Allocation& alloc) if (privateData) { WriteString(L"CustomData"); - WriteNumber((uintptr_t)privateData); + BeginString(); + ContinueString_Pointer(privateData); + EndString(); } LPCWSTR name = alloc.GetName(); @@ -7196,9 +7231,6 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 @@ void AllocatorPimpl::BuildStatsString(WCHAR** ppStatsString, BOOL detailedMap) 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 ValidateAllocationsDataGPU(const TestContext& ctx, const ComPtrBuildStatsString(&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 @@ def CalcParams(): 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 @@ def CalcParams(): 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 @@ if __name__ == '__main__': 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)