Improved TestStats Based on TestStatistics function in VMA.
diff --git a/src/Tests.cpp b/src/Tests.cpp index 73c5bda..c704e1e 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp
@@ -1699,6 +1699,11 @@ return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; } +static inline bool StatisticsEqual(const D3D12MA::Statistics& lhs, const D3D12MA::Statistics& rhs) +{ + return memcmp(&lhs, &rhs, sizeof(lhs)) == 0; +} + static void CheckStatistics(const D3D12MA::DetailedStatistics& stats) { CHECK_BOOL(stats.Stats.AllocationBytes <= stats.Stats.BlockBytes); @@ -1714,86 +1719,304 @@ } } +static void CheckBudgetBasics(const TestContext& ctx, + const D3D12MA::Budget& localBudget, const D3D12MA::Budget& nonLocalBudget) +{ + CHECK_BOOL(localBudget.BudgetBytes > 0); + CHECK_BOOL(localBudget.BudgetBytes <= ctx.allocator->GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_LOCAL)); + CHECK_BOOL(localBudget.Stats.AllocationBytes <= localBudget.Stats.BlockBytes); + + // Discrete graphics card with separate video memory. + if (!ctx.allocator->IsUMA()) + { + CHECK_BOOL(nonLocalBudget.BudgetBytes > 0); + CHECK_BOOL(nonLocalBudget.BudgetBytes <= ctx.allocator->GetMemoryCapacity(DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL)); + CHECK_BOOL(nonLocalBudget.Stats.AllocationBytes <= nonLocalBudget.Stats.BlockBytes); + } +} + +static D3D12MA::DetailedStatistics GetEmptyDetailedStatistics() +{ + D3D12MA::DetailedStatistics out = {}; + out.AllocationSizeMin = UINT64_MAX; + out.UnusedRangeSizeMin = UINT64_MAX; + return out; +} + +static void AddDetailedStatistics(D3D12MA::DetailedStatistics& inoutSum, const D3D12MA::DetailedStatistics& stats) +{ + inoutSum.Stats.AllocationBytes += stats.Stats.AllocationBytes; + inoutSum.Stats.AllocationCount += stats.Stats.AllocationCount; + inoutSum.Stats.BlockBytes += stats.Stats.BlockBytes; + inoutSum.Stats.BlockCount += stats.Stats.BlockCount; + inoutSum.UnusedRangeCount += stats.UnusedRangeCount; + inoutSum.AllocationSizeMax = std::max(inoutSum.AllocationSizeMax, stats.AllocationSizeMax); + inoutSum.AllocationSizeMin = std::min(inoutSum.AllocationSizeMin, stats.AllocationSizeMin); + inoutSum.UnusedRangeSizeMax = std::max(inoutSum.UnusedRangeSizeMax, stats.UnusedRangeSizeMax); + inoutSum.UnusedRangeSizeMin = std::min(inoutSum.UnusedRangeSizeMin, stats.UnusedRangeSizeMin); +} + +static void CheckTotalStatistics(const D3D12MA::TotalStatistics& stats) +{ + D3D12MA::DetailedStatistics sum = GetEmptyDetailedStatistics(); + for (size_t i = 0; i < _countof(stats.HeapType); ++i) + { + AddDetailedStatistics(sum, stats.HeapType[i]); + } + CHECK_BOOL(StatisticsEqual(sum, stats.Total)); + + sum = GetEmptyDetailedStatistics(); + for (size_t i = 0; i < _countof(stats.MemorySegmentGroup); ++i) + { + AddDetailedStatistics(sum, stats.MemorySegmentGroup[i]); + } + CHECK_BOOL(StatisticsEqual(sum, stats.Total)); +} + static void TestStats(const TestContext& ctx) { + using namespace D3D12MA; + wprintf(L"Test stats\n"); - D3D12MA::TotalStatistics begStats = {}; - ctx.allocator->CalculateStatistics(&begStats); + constexpr UINT64 BUF_SIZE = 10 * MEGABYTE; + constexpr UINT32 BUF_COUNT = 4; + constexpr UINT64 PREALLOCATED_BLOCK_SIZE = BUF_SIZE * (BUF_COUNT + 1); - const UINT count = 10; - const UINT64 bufSize = 64ull * 1024; - ResourceWithAllocation resources[count]; - - D3D12MA::CALLOCATION_DESC allocDesc = D3D12MA::CALLOCATION_DESC{ D3D12_HEAP_TYPE_UPLOAD }; - - D3D12_RESOURCE_DESC resourceDesc; - FillResourceDescForBuffer(resourceDesc, bufSize); - - for(UINT i = 0; i < count; ++i) + /* + Test 0: ALLOCATION_FLAG_COMMITTED. + Test 1: normal allocations. + Test 2: allocations in a custom pool. + Test 3: allocations in a custom pool, COMMITTED. + Test 4: allocations in a custom pool with preallocated memory. + */ + for (uint32_t testIndex = 0; testIndex < 5; ++testIndex) { - if(i == count / 2) - allocDesc.Flags |= D3D12MA::ALLOCATION_FLAG_COMMITTED; - CHECK_HR( ctx.allocator->CreateResource( - &allocDesc, - &resourceDesc, - D3D12_RESOURCE_STATE_GENERIC_READ, - NULL, - &resources[i].allocation, - IID_PPV_ARGS(&resources[i].resource)) ); - } + const bool usePool = testIndex >= 2; + const bool useCommitted = testIndex == 0 || testIndex == 3; + const bool usePreallocated = testIndex == 4; - D3D12MA::TotalStatistics endStats = {}; - ctx.allocator->CalculateStatistics(&endStats); + // Get stats "Beg". + Budget localBudgetBeg = {}; + Budget nonLocalBudgetBeg = {}; + ctx.allocator->GetBudget(&localBudgetBeg, &nonLocalBudgetBeg); + CheckBudgetBasics(ctx, localBudgetBeg, nonLocalBudgetBeg); - CHECK_BOOL(endStats.Total.Stats.BlockCount >= begStats.Total.Stats.BlockCount); - CHECK_BOOL(endStats.Total.Stats.AllocationCount == begStats.Total.Stats.AllocationCount + count); - CHECK_BOOL(endStats.Total.Stats.AllocationBytes == begStats.Total.Stats.AllocationBytes + count * bufSize); - CHECK_BOOL(endStats.Total.AllocationSizeMin <= bufSize); - CHECK_BOOL(endStats.Total.AllocationSizeMax >= bufSize); + TotalStatistics statsBeg = {}; + ctx.allocator->CalculateStatistics(&statsBeg); + CheckTotalStatistics(statsBeg); - CHECK_BOOL(endStats.HeapType[1].Stats.BlockCount >= begStats.HeapType[1].Stats.BlockCount); - CHECK_BOOL(endStats.HeapType[1].Stats.AllocationCount >= begStats.HeapType[1].Stats.AllocationCount + count); - CHECK_BOOL(endStats.HeapType[1].Stats.AllocationBytes >= begStats.HeapType[1].Stats.AllocationBytes + count * bufSize); - CHECK_BOOL(endStats.HeapType[1].AllocationSizeMin <= bufSize); - CHECK_BOOL(endStats.HeapType[1].AllocationSizeMax >= bufSize); + // Create pool. + ComPtr<Pool> pool; + if (usePool) + { + POOL_DESC poolDesc = CPOOL_DESC(D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS); + if (usePreallocated) + { + poolDesc.BlockSize = PREALLOCATED_BLOCK_SIZE; + poolDesc.MinBlockCount = 1; + poolDesc.MaxBlockCount = 1; + } + CHECK_HR(ctx.allocator->CreatePool(&poolDesc, &pool)); + } - CHECK_BOOL(StatisticsEqual(begStats.HeapType[0], endStats.HeapType[0])); - CHECK_BOOL(StatisticsEqual(begStats.HeapType[2], endStats.HeapType[2])); + // Get pool stats "Beg". + Statistics poolStatsBeg = {}; + DetailedStatistics detailedPoolStatsBeg = {}; + if (usePool) + { + pool->GetStatistics(&poolStatsBeg); + pool->CalculateStatistics(&detailedPoolStatsBeg); + CheckStatistics(detailedPoolStatsBeg); + } - CheckStatistics(endStats.Total); - CheckStatistics(endStats.HeapType[0]); - CheckStatistics(endStats.HeapType[1]); - CheckStatistics(endStats.HeapType[2]); + // Create buffers. + D3D12_RESOURCE_DESC resDesc; + FillResourceDescForBuffer(resDesc, BUF_SIZE); - D3D12MA::Budget localBudget = {}, nonLocalBudget = {}; - ctx.allocator->GetBudget(&localBudget, &nonLocalBudget); + ALLOCATION_DESC allocDesc = {}; + if (usePool) + allocDesc = CALLOCATION_DESC(pool.Get()); + else + allocDesc = CALLOCATION_DESC(D3D12_HEAP_TYPE_DEFAULT); + if (useCommitted) + allocDesc.Flags |= ALLOCATION_FLAG_COMMITTED; - CHECK_BOOL(localBudget.Stats.AllocationBytes <= localBudget.Stats.BlockBytes); - CHECK_BOOL(endStats.HeapType[3].Stats.BlockCount == 0); // No allocation from D3D12_HEAP_TYPE_CUSTOM in this test. - if(!ctx.allocator->IsUMA()) - { - // Discrete GPU - CHECK_BOOL(localBudget.Stats.AllocationBytes == endStats.HeapType[0].Stats.AllocationBytes); - CHECK_BOOL(localBudget.Stats.BlockBytes == endStats.HeapType[0].Stats.BlockBytes); - - CHECK_BOOL(nonLocalBudget.Stats.AllocationBytes <= nonLocalBudget.Stats.BlockBytes); - CHECK_BOOL(nonLocalBudget.Stats.AllocationBytes == endStats.HeapType[1].Stats.AllocationBytes + endStats.HeapType[2].Stats.AllocationBytes); - CHECK_BOOL(nonLocalBudget.Stats.BlockBytes == - endStats.HeapType[1].Stats.BlockBytes + endStats.HeapType[2].Stats.BlockBytes); - } - else - { - // Integrated GPU - all memory is local - CHECK_BOOL(localBudget.Stats.AllocationBytes == endStats.HeapType[0].Stats.AllocationBytes + - endStats.HeapType[1].Stats.AllocationBytes + - endStats.HeapType[2].Stats.AllocationBytes); - CHECK_BOOL(localBudget.Stats.BlockBytes == endStats.HeapType[0].Stats.BlockBytes + - endStats.HeapType[1].Stats.BlockBytes + - endStats.HeapType[2].Stats.BlockBytes); + ComPtr<Allocation> allocs[BUF_COUNT]; + for (UINT i = 0; i < BUF_COUNT; ++i) + { + CHECK_HR(ctx.allocator->CreateResource( + &allocDesc, &resDesc, D3D12_RESOURCE_STATE_COMMON, + NULL, &allocs[i], IID_NULL, NULL)); + } - CHECK_BOOL(nonLocalBudget.Stats.AllocationBytes == 0); - CHECK_BOOL(nonLocalBudget.Stats.BlockBytes == 0); + // Get stats "WithBufs". + Budget localBudgetWithBufs = {}; + Budget nonLocalBudgetWithBufs = {}; + ctx.allocator->GetBudget(&localBudgetWithBufs, &nonLocalBudgetWithBufs); + CheckBudgetBasics(ctx, localBudgetWithBufs, nonLocalBudgetWithBufs); + + TotalStatistics statsWithBufs = {}; + ctx.allocator->CalculateStatistics(&statsWithBufs); + CheckTotalStatistics(statsWithBufs); + + Statistics poolStatsWithBufs = {}; + DetailedStatistics detailedPoolStatsWithBufs = {}; + if (usePool) + { + pool->GetStatistics(&poolStatsWithBufs); + pool->CalculateStatistics(&detailedPoolStatsWithBufs); + CheckStatistics(detailedPoolStatsWithBufs); + } + + // Destroy buffers. + for (size_t i = BUF_COUNT; i--; ) + { + allocs[i].Reset(); + } + + // Get pool stats "End". + Statistics poolStatsEnd = {}; + DetailedStatistics detailedPoolStatsEnd = {}; + if (usePool) + { + pool->GetStatistics(&poolStatsEnd); + pool->CalculateStatistics(&detailedPoolStatsEnd); + CheckStatistics(detailedPoolStatsEnd); + } + + // Destroy the pool. + pool.Reset(); + + // Get stats "End". + Budget localBudgetEnd = {}; + Budget nonLocalBudgetEnd = {}; + ctx.allocator->GetBudget(&localBudgetEnd, &nonLocalBudgetEnd); + CheckBudgetBasics(ctx, localBudgetEnd, nonLocalBudgetEnd); + + TotalStatistics statsEnd = {}; + ctx.allocator->CalculateStatistics(&statsEnd); + CheckTotalStatistics(statsEnd); + + // CHECK THE STATS: Local. + { + CHECK_BOOL(localBudgetBeg.Stats.AllocationBytes <= localBudgetEnd.Stats.AllocationBytes); + + // Budget::UsageBytes. + CHECK_BOOL(localBudgetWithBufs.UsageBytes >= localBudgetBeg.UsageBytes); + CHECK_BOOL(localBudgetEnd.UsageBytes <= localBudgetWithBufs.UsageBytes); + + // Budget - Statistics::AllocationBytes. + CHECK_BOOL(localBudgetEnd.Stats.AllocationBytes == localBudgetBeg.Stats.AllocationBytes); + CHECK_BOOL(localBudgetWithBufs.Stats.AllocationBytes == localBudgetBeg.Stats.AllocationBytes + BUF_SIZE * BUF_COUNT); + + // Budget - Statistics::BlockBytes. + if (usePool) + { + CHECK_BOOL(localBudgetEnd.Stats.BlockBytes == localBudgetBeg.Stats.BlockBytes); + CHECK_BOOL(localBudgetWithBufs.Stats.BlockBytes > localBudgetBeg.Stats.BlockBytes); + } + else + { + CHECK_BOOL(localBudgetWithBufs.Stats.BlockBytes >= localBudgetBeg.Stats.BlockBytes); + } + + // Budget - Statistics::AllocationCount. + CHECK_BOOL(localBudgetEnd.Stats.AllocationCount == localBudgetBeg.Stats.AllocationCount); + CHECK_BOOL(localBudgetWithBufs.Stats.AllocationCount == localBudgetBeg.Stats.AllocationCount + BUF_COUNT); + + // Budget - Statistics::BlockCount. + if (useCommitted) + { + CHECK_BOOL(localBudgetEnd.Stats.BlockCount == localBudgetBeg.Stats.BlockCount); + CHECK_BOOL(localBudgetWithBufs.Stats.BlockCount == localBudgetBeg.Stats.BlockCount + BUF_COUNT); + } + else if (usePool) + { + CHECK_BOOL(localBudgetEnd.Stats.BlockCount == localBudgetBeg.Stats.BlockCount); + if (usePreallocated) + { + CHECK_BOOL(localBudgetWithBufs.Stats.BlockCount == localBudgetBeg.Stats.BlockCount + 1); + } + else + { + CHECK_BOOL(localBudgetWithBufs.Stats.BlockCount > localBudgetBeg.Stats.BlockCount); + } + } + + // Compare CalculateStatistics per memory segment group with GetBudget. + CHECK_BOOL(StatisticsEqual(statsBeg.MemorySegmentGroup[0].Stats, localBudgetBeg.Stats)); + CHECK_BOOL(StatisticsEqual(statsWithBufs.MemorySegmentGroup[0].Stats, localBudgetWithBufs.Stats)); + CHECK_BOOL(StatisticsEqual(statsEnd.MemorySegmentGroup[0].Stats, localBudgetEnd.Stats)); + } + + // CHECK THE STATS: Non-local. + { + CHECK_BOOL(nonLocalBudgetEnd.Stats.AllocationBytes == nonLocalBudgetBeg.Stats.AllocationBytes && + nonLocalBudgetEnd.Stats.AllocationBytes == nonLocalBudgetWithBufs.Stats.AllocationBytes); + CHECK_BOOL(nonLocalBudgetEnd.Stats.BlockBytes == nonLocalBudgetBeg.Stats.BlockBytes && + nonLocalBudgetEnd.Stats.BlockBytes == nonLocalBudgetWithBufs.Stats.BlockBytes); + CHECK_BOOL(nonLocalBudgetEnd.Stats.AllocationCount == nonLocalBudgetBeg.Stats.AllocationCount && + nonLocalBudgetEnd.Stats.AllocationCount == nonLocalBudgetWithBufs.Stats.AllocationCount); + CHECK_BOOL(nonLocalBudgetEnd.Stats.BlockCount == nonLocalBudgetBeg.Stats.BlockCount && + nonLocalBudgetEnd.Stats.BlockCount == nonLocalBudgetWithBufs.Stats.BlockCount); + + // Compare CalculateStatistics per memory segment group with GetBudget. + CHECK_BOOL(StatisticsEqual(statsBeg.MemorySegmentGroup[1].Stats, nonLocalBudgetBeg.Stats)); + CHECK_BOOL(StatisticsEqual(statsWithBufs.MemorySegmentGroup[1].Stats, nonLocalBudgetWithBufs.Stats)); + CHECK_BOOL(StatisticsEqual(statsEnd.MemorySegmentGroup[1].Stats, nonLocalBudgetEnd.Stats)); + } + + if (usePool) + { + // Compare simple stats with calculated stats to make sure they are identical. + CHECK_BOOL(StatisticsEqual(poolStatsBeg, detailedPoolStatsBeg.Stats)); + CHECK_BOOL(StatisticsEqual(poolStatsWithBufs, detailedPoolStatsWithBufs.Stats)); + CHECK_BOOL(StatisticsEqual(poolStatsEnd, detailedPoolStatsEnd.Stats)); + + // Validate stats of an empty pool. + CHECK_BOOL(detailedPoolStatsBeg.AllocationSizeMax == 0); + CHECK_BOOL(detailedPoolStatsEnd.AllocationSizeMax == 0); + CHECK_BOOL(detailedPoolStatsBeg.AllocationSizeMin == UINT64_MAX); + CHECK_BOOL(detailedPoolStatsEnd.AllocationSizeMin == UINT64_MAX); + CHECK_BOOL(poolStatsBeg.AllocationCount == 0); + CHECK_BOOL(poolStatsBeg.AllocationBytes == 0); + CHECK_BOOL(poolStatsEnd.AllocationCount == 0); + CHECK_BOOL(poolStatsEnd.AllocationBytes == 0); + if (usePreallocated) + { + CHECK_BOOL(poolStatsBeg.BlockCount == 1); + CHECK_BOOL(poolStatsEnd.BlockCount == 1); + CHECK_BOOL(poolStatsBeg.BlockBytes == PREALLOCATED_BLOCK_SIZE); + CHECK_BOOL(poolStatsEnd.BlockBytes == PREALLOCATED_BLOCK_SIZE); + } + else + { + CHECK_BOOL(poolStatsBeg.BlockCount == 0); + CHECK_BOOL(poolStatsBeg.BlockBytes == 0); + // Not checking poolStatsEnd.blockCount, blockBytes, because an empty block may stay allocated. + } + + // Validate stats of a pool with buffers. + CHECK_BOOL(detailedPoolStatsWithBufs.AllocationSizeMin == BUF_SIZE); + CHECK_BOOL(detailedPoolStatsWithBufs.AllocationSizeMax == BUF_SIZE); + CHECK_BOOL(poolStatsWithBufs.AllocationCount == BUF_COUNT); + CHECK_BOOL(poolStatsWithBufs.AllocationBytes == BUF_COUNT * BUF_SIZE); + if (usePreallocated) + { + CHECK_BOOL(poolStatsWithBufs.BlockCount == 1); + CHECK_BOOL(poolStatsWithBufs.BlockBytes == PREALLOCATED_BLOCK_SIZE); + } + else + { + CHECK_BOOL(poolStatsWithBufs.BlockCount > 0); + CHECK_BOOL(poolStatsWithBufs.BlockBytes >= poolStatsWithBufs.AllocationBytes); + } + } + + // No allocation from D3D12_HEAP_TYPE_CUSTOM or GPU_UPLOAD in this test. + CHECK_BOOL(statsEnd.HeapType[3].Stats.BlockCount == 0); + CHECK_BOOL(statsEnd.HeapType[4].Stats.BlockCount == 0); } }