diff --git a/README.md b/README.md index 65a9ff5..28b17a1 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Additional features: - 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 @@ enum ALLOCATION_FLAGS */ 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 @@ private: UINT64 m_Size; UINT64 m_Alignment; ID3D12Resource* m_Resource; - UINT m_CreationFrameIndex; void* m_pPrivateData; wchar_t* m_Name; @@ -1246,8 +1244,9 @@ public: */ 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 @@ void JsonWriter::AddAllocationToObject(const Allocation& alloc) 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 @@ BlockMetadata::BlockMetadata(const ALLOCATION_CALLBACKS* allocationCallbacks, bo 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 @@ void BlockMetadata::PrintDetailedMap_Allocation(JsonWriter& json, 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_UnusedRange(JsonWriter& json, void BlockMetadata::PrintDetailedMap_End(JsonWriter& json) const { json.EndArray(); - json.EndObject(); } #endif // _D3D12MA_BLOCK_METADATA_FUNCTIONS #endif // _D3D12MA_BLOCK_METADATA @@ -5942,6 +5937,7 @@ public: ~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 @@ public: 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 @@ void AllocatorPimpl::SetCurrentFrameIndex(UINT frameIndex) #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 @@ void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) 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 @@ void AllocatorPimpl::CalculateStatistics(TotalStatistics& outStats) 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::GetBudgetForHeapType(Budget& outBudget, D3D12_HEAP_TYPE hea } } -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(m_D3D12Options.ResourceHeapTier)); + + json.WriteString(L"ResourceBindingTier"); + json.WriteNumber(static_cast(m_D3D12Options.ResourceBindingTier)); + + json.WriteString(L"TiledResourcesTier"); + json.WriteNumber(static_cast(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(GetAllocs(), length + 1); - memcpy(result, sb.GetData(), length * sizeof(WCHAR)); - result[length] = L'\0'; + WCHAR* result = AllocateArray(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 @@ void AllocatorPimpl::WriteBudgetToJson(JsonWriter& json, const Budget& budget) { 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 @@ void BlockVector::WriteBlockInfoToJson(JsonWriter& json) json.ContinueString(pBlock->GetId()); json.EndString(); + json.BeginObject(); pBlock->m_pMetadata->WriteAllocationInfoToJson(json); + json.EndObject(); } json.EndObject(); @@ -9423,7 +9622,6 @@ Allocation::Allocation(AllocatorPimpl* allocator, UINT64 size, UINT64 alignment, 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 TestDebugMarginNotInVirtualAllocator(const TestContext& ctx) } } -static void TestFrameIndexAndJson(const TestContext& ctx) +static void TestJson(const TestContext& ctx) { - const UINT64 bufSize = 32ull * 1024; + wprintf(L"Test JSON\n"); + + std::vector> pools; + std::vector> 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 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 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 @@ static void TestVirtualBlocks(const TestContext& ctx) 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 @@ static void TestGroupBasics(const TestContext& ctx) 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/ExampleOutput.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Bkg.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Buffer_1.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Buffer_2.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Buffer_3.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Buffer_4.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Details.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_1.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_2.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_3.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_4.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_Linear.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Image_Unknown.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Texture_1.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Texture_2.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Texture_3.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Texture_4.png 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 Binary files /dev/null and b/tools/GpuMemDumpVis/README_files/Legend_Unknown.png 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