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