From dc6f5db3f6a2a102bcf25df807ef137e90cb4547 Mon Sep 17 00:00:00 2001 From: Bryan Bernhart Date: Fri, 23 Sep 2022 11:55:07 -0700 Subject: [PATCH] Improve the D3D12 test harness for playback. * Adds a basic residency description. * Fixes resource heap tier checks in capture-replay. * Adds allocator fields (algorithm) to playback. --- src/tests/D3D12Test.cpp | 18 ++ src/tests/D3D12Test.h | 3 + .../D3D12EventTraceReplay.cpp | 162 ++++++++++-------- .../end2end/D3D12ResidencyManagerTests.cpp | 15 +- 4 files changed, 108 insertions(+), 90 deletions(-) diff --git a/src/tests/D3D12Test.cpp b/src/tests/D3D12Test.cpp index 593a8297b..61ef0c446 100644 --- a/src/tests/D3D12Test.cpp +++ b/src/tests/D3D12Test.cpp @@ -124,6 +124,24 @@ namespace gpgmm::d3d12 { return desc; } + RESIDENCY_DESC D3D12TestBase::CreateBasicResidencyDesc() const { + RESIDENCY_DESC desc = {}; + // Required + desc.Adapter = mAdapter; + desc.Device = mDevice; + desc.IsUMA = mCaps->IsAdapterUMA(); + + desc.MinLogLevel = GetMessageSeverity(GetLogLevel()); + + if (IsDumpEventsEnabled()) { + desc.RecordOptions.Flags |= EVENT_RECORD_FLAG_ALL_EVENTS; + desc.RecordOptions.MinMessageLevel = desc.MinLogLevel; + desc.RecordOptions.UseDetailedTimingEvents = true; + } + + return desc; + } + // static D3D12_RESOURCE_DESC D3D12TestBase::CreateBasicBufferDesc(uint64_t width, uint64_t alignment) { D3D12_RESOURCE_DESC resourceDesc; diff --git a/src/tests/D3D12Test.h b/src/tests/D3D12Test.h index 7fc3d3f48..8fa194d94 100644 --- a/src/tests/D3D12Test.h +++ b/src/tests/D3D12Test.h @@ -32,6 +32,8 @@ namespace gpgmm::d3d12 { struct ALLOCATOR_DESC; + struct RESIDENCY_DESC; + class Caps; class ResourceAllocator; class ResourceAllocation; @@ -43,6 +45,7 @@ namespace gpgmm::d3d12 { void SetUp(); void TearDown(); + RESIDENCY_DESC CreateBasicResidencyDesc() const; ALLOCATOR_DESC CreateBasicAllocatorDesc() const; static D3D12_RESOURCE_DESC CreateBasicBufferDesc(uint64_t width, uint64_t alignment = 0); diff --git a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp index ec8db713d..d7e19ac76 100644 --- a/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp +++ b/src/tests/capture_replay_tests/D3D12EventTraceReplay.cpp @@ -90,6 +90,54 @@ namespace { return resourceDescriptor; } + ALLOCATOR_DESC ConvertAndApplyToAllocatorDesc(const Json::Value& allocatorDescJson, + const ALLOCATOR_DESC& allocatorDesc) { + ALLOCATOR_DESC newAllocatorDesc = allocatorDesc; + newAllocatorDesc.Flags |= static_cast(allocatorDescJson["Flags"].asInt()); + newAllocatorDesc.ResourceHeapTier = + static_cast(allocatorDescJson["ResourceHeapTier"].asInt()); + newAllocatorDesc.SubAllocationAlgorithm = + static_cast(allocatorDescJson["SubAllocationAlgorithm"].asInt()); + newAllocatorDesc.PoolAlgorithm = + static_cast(allocatorDescJson["PoolAlgorithm"].asInt()); + newAllocatorDesc.PreferredResourceHeapSize = + allocatorDescJson["PreferredResourceHeapSize"].asUInt64(); + newAllocatorDesc.MaxResourceHeapSize = allocatorDescJson["MaxResourceHeapSize"].asUInt64(); + newAllocatorDesc.MemoryFragmentationLimit = + allocatorDescJson["MemoryFragmentationLimit"].asDouble(); + newAllocatorDesc.MemoryGrowthFactor = allocatorDescJson["MemoryGrowthFactor"].asDouble(); + return newAllocatorDesc; + } + + RESIDENCY_DESC ConvertAndApplyToResidencyDesc(const Json::Value& residencyDescJson, + const RESIDENCY_DESC& residencyDesc) { + RESIDENCY_DESC newResidencyDesc = residencyDesc; + newResidencyDesc.MaxPctOfVideoMemoryToBudget = + residencyDescJson["MaxPctOfVideoMemoryToBudget"].asFloat(); + newResidencyDesc.MaxBudgetInBytes = residencyDescJson["MaxBudgetInBytes"].asUInt64(); + newResidencyDesc.EvictSizeInBytes = residencyDescJson["EvictSizeInBytes"].asUInt64(); + newResidencyDesc.InitialFenceValue = residencyDescJson["InitialFenceValue"].asUInt64(); + return newResidencyDesc; + } + + D3D12_HEAP_PROPERTIES ConvertToD3D12HeapProperties(const Json::Value& heapPropertiesJson) { + D3D12_HEAP_PROPERTIES heapProperties = {}; + heapProperties.Type = static_cast(heapPropertiesJson["Type"].asInt()); + heapProperties.CPUPageProperty = + static_cast(heapPropertiesJson["CPUPageProperty"].asInt()); + heapProperties.MemoryPoolPreference = + static_cast(heapPropertiesJson["MemoryPoolPreference"].asInt()); + return heapProperties; + } + + HEAP_DESC ConvertAndApplyToHeapDesc(const Json::Value& heapJson, const HEAP_DESC& heapDesc) { + HEAP_DESC newHeapDesc = heapDesc; + newHeapDesc.SizeInBytes = heapJson["SizeInBytes"].asUInt64(); + newHeapDesc.Alignment = heapJson["Alignment"].asUInt64(); + newHeapDesc.Flags = static_cast(heapJson["Flags"].asInt()); + return newHeapDesc; + } + bool IsErrorEvent(const Json::Value& args) { return (args.isMember("Description") && args.isMember("ID")); } @@ -131,6 +179,18 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith const Json::Value& traceEvents = root["traceEvents"]; ASSERT_TRUE(!traceEvents.empty()); + // Captures never store recording options, they must be always specified. + EVENT_RECORD_OPTIONS eventRecordOptions = {}; + eventRecordOptions.Flags |= static_cast(envParams.CaptureEventMask); + eventRecordOptions.TraceFile = traceFile.path.c_str(); + eventRecordOptions.MinMessageLevel = GetMessageSeverity(GetLogLevel()); + + // Keep recording across multiple playback iterations to ensure all + // events will be captured instead of overwritten per iteration. + if (envParams.Iterations == 1) { + eventRecordOptions.EventScope = EVENT_RECORD_SCOPE_PER_INSTANCE; + } + for (Json::Value::ArrayIndex eventIndex = 0; eventIndex < traceEvents.size(); eventIndex++) { const Json::Value& event = traceEvents[eventIndex]; @@ -310,32 +370,9 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith GPGMM_SKIP_TEST_IF(!envParams.IsIgnoreCapsMismatchEnabled); } - RESIDENCY_DESC residencyDesc = {}; - residencyDesc.Device = mDevice; - residencyDesc.Adapter = mAdapter; - residencyDesc.IsUMA = mCaps->IsAdapterUMA(); - residencyDesc.MaxPctOfVideoMemoryToBudget = - snapshot["MaxPctOfVideoMemoryToBudget"].asFloat(); - residencyDesc.MaxBudgetInBytes = snapshot["MaxBudgetInBytes"].asUInt64(); - residencyDesc.EvictSizeInBytes = snapshot["EvictSizeInBytes"].asUInt64(); - residencyDesc.InitialFenceValue = snapshot["InitialFenceValue"].asUInt64(); - - if (envParams.CaptureEventMask != 0) { - residencyDesc.RecordOptions.Flags |= - static_cast(envParams.CaptureEventMask); - residencyDesc.RecordOptions.TraceFile = traceFile.path.c_str(); - residencyDesc.RecordOptions.MinMessageLevel = - GetMessageSeverity(GetLogLevel()); - - // Keep recording across multiple playback iterations to ensure all - // events will be captured instead of overwritten per iteration. - if (envParams.Iterations == 1) { - residencyDesc.RecordOptions.EventScope = - EVENT_RECORD_SCOPE_PER_INSTANCE; - } - } - - residencyDesc.MinLogLevel = GetMessageSeverity(GetLogLevel()); + RESIDENCY_DESC residencyDesc = CreateBasicResidencyDesc(); + residencyDesc.RecordOptions = eventRecordOptions; + residencyDesc = ConvertAndApplyToResidencyDesc(snapshot, residencyDesc); ComPtr residencyManager; ASSERT_SUCCEEDED(ResidencyManager::CreateResidencyManager( @@ -377,24 +414,27 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith const Json::Value& snapshot = event["args"]["snapshot"]; ASSERT_FALSE(snapshot.empty()); - ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); - if (!envParams.IsPrefetchAllowed) { - allocatorDesc.Flags |= ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH; + if (GetLogLevel() <= gpgmm::LogSeverity::Warning && + mCaps->GetMaxResourceHeapTierSupported() < + snapshot["ResourceHeapTier"].asInt() && + iterationIndex == 0) { + gpgmm::WarningLog() + << "Captured device exceeds capabilities of playback device " + "(ResourceHeapTier: " + + std::to_string(snapshot["ResourceHeapTier"].asInt()) + + " vs " + + std::to_string(mCaps->GetMaxResourceHeapTierSupported()) + + ")."; + GPGMM_SKIP_TEST_IF(!envParams.IsIgnoreCapsMismatchEnabled); } + ALLOCATOR_DESC allocatorDesc = CreateBasicAllocatorDesc(); + allocatorDesc.RecordOptions = eventRecordOptions; + // Apply profile (if specified). if (envParams.AllocatorProfile == AllocatorProfile::ALLOCATOR_PROFILE_CAPTURED) { - allocatorDesc.Flags |= - static_cast(snapshot["Flags"].asInt()); - allocatorDesc.PreferredResourceHeapSize = - snapshot["PreferredResourceHeapSize"].asUInt64(); - allocatorDesc.MaxResourceHeapSize = - snapshot["MaxResourceHeapSize"].asUInt64(); - allocatorDesc.MemoryFragmentationLimit = - snapshot["MemoryFragmentationLimit"].asDouble(); - allocatorDesc.MemoryGrowthFactor = - snapshot["MemoryGrowthFactor"].asDouble(); + allocatorDesc = ConvertAndApplyToAllocatorDesc(snapshot, allocatorDesc); } else if (envParams.AllocatorProfile == AllocatorProfile::ALLOCATOR_PROFILE_MAX_PERFORMANCE) { // Any amount of (internal) fragmentation is acceptable. @@ -405,34 +445,9 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith allocatorDesc.MemoryFragmentationLimit = 0.125; // 1/8th of 4MB } - if (envParams.CaptureEventMask != 0) { - allocatorDesc.RecordOptions.Flags |= - static_cast(envParams.CaptureEventMask); - allocatorDesc.RecordOptions.TraceFile = traceFile.path.c_str(); - allocatorDesc.RecordOptions.MinMessageLevel = - GetMessageSeverity(GetLogLevel()); - - // Keep recording across multiple playback iterations to ensure all - // events will be captured instead of overwritten per iteration. - if (envParams.Iterations == 1) { - allocatorDesc.RecordOptions.EventScope = - EVENT_RECORD_SCOPE_PER_INSTANCE; - } - } - - allocatorDesc.MinLogLevel = GetMessageSeverity(GetLogLevel()); - - if (GetLogLevel() <= gpgmm::LogSeverity::Warning && - allocatorDesc.ResourceHeapTier != - snapshot["ResourceHeapTier"].asInt() && - iterationIndex == 0) { - gpgmm::WarningLog() - << "Capture device does not match playback device " - "(ResourceHeapTier: " + - std::to_string(snapshot["ResourceHeapTier"].asInt()) + - " vs " + std::to_string(allocatorDesc.ResourceHeapTier) + - ")."; - GPGMM_SKIP_TEST_IF(!envParams.IsIgnoreCapsMismatchEnabled); + // Apply flags by enviromental settings after the profile. + if (!envParams.IsPrefetchAllowed) { + allocatorDesc.Flags |= ALLOCATOR_FLAG_DISABLE_MEMORY_PREFETCH; } ComPtr residencyManager; @@ -485,19 +500,14 @@ class D3D12EventTraceReplay : public D3D12TestBase, public CaptureReplayTestWith continue; } - D3D12_HEAP_PROPERTIES heapProperties = {}; - heapProperties.Type = static_cast( - args["Heap"]["Properties"]["Type"].asInt()); - heapProperties.CPUPageProperty = static_cast( - args["Heap"]["Properties"]["CPUPageProperty"].asInt()); - heapProperties.MemoryPoolPreference = static_cast( - args["Heap"]["Properties"]["MemoryPoolPreference"].asInt()); + const D3D12_HEAP_PROPERTIES heapProperties = + ConvertToD3D12HeapProperties(args["Heap"]["Properties"]); HEAP_DESC resourceHeapDesc = {}; - resourceHeapDesc.SizeInBytes = args["Heap"]["SizeInBytes"].asUInt64(); - resourceHeapDesc.Alignment = args["Heap"]["Alignment"].asUInt64(); resourceHeapDesc.MemorySegmentGroup = GetMemorySegmentGroup( heapProperties.MemoryPoolPreference, mCaps->IsAdapterUMA()); + resourceHeapDesc = + ConvertAndApplyToHeapDesc(args["Heap"], resourceHeapDesc); ResidencyManager* residencyManager = createdResidencyManagerToID[currentResidencyID].Get(); diff --git a/src/tests/end2end/D3D12ResidencyManagerTests.cpp b/src/tests/end2end/D3D12ResidencyManagerTests.cpp index 89e0f6952..0908d2cbe 100644 --- a/src/tests/end2end/D3D12ResidencyManagerTests.cpp +++ b/src/tests/end2end/D3D12ResidencyManagerTests.cpp @@ -57,7 +57,7 @@ class D3D12ResidencyManagerTests : public D3D12TestBase, public ::testing::Test // Configures residency manager for testing residency in a controlled and predictable // fashion. RESIDENCY_DESC CreateBasicResidencyDesc(uint64_t budget) const { - RESIDENCY_DESC residencyDesc = {}; + RESIDENCY_DESC residencyDesc = D3D12TestBase::CreateBasicResidencyDesc(); // Disable automatic budget updates, since it occurs uncontrollably by the OS. residencyDesc.Flags |= RESIDENCY_FLAG_NEVER_UPDATE_BUDGET_ON_WORKER_THREAD; @@ -65,19 +65,6 @@ class D3D12ResidencyManagerTests : public D3D12TestBase, public ::testing::Test // Specify a restricted budget, the OS budget fluctuates unpredicatbly. residencyDesc.MaxBudgetInBytes = budget; - // Required - residencyDesc.IsUMA = mCaps->IsAdapterUMA(); - residencyDesc.Adapter = mAdapter; - residencyDesc.Device = mDevice; - - residencyDesc.MinLogLevel = GetMessageSeverity(GetLogLevel()); - - if (IsDumpEventsEnabled()) { - residencyDesc.RecordOptions.Flags |= EVENT_RECORD_FLAG_ALL_EVENTS; - residencyDesc.RecordOptions.MinMessageLevel = residencyDesc.MinLogLevel; - residencyDesc.RecordOptions.UseDetailedTimingEvents = true; - } - return residencyDesc; }