From e93791f4a07f17d9b57a324ea61e2aee3641035b Mon Sep 17 00:00:00 2001 From: Michael Gharbharan Date: Wed, 1 Apr 2026 15:37:20 -0400 Subject: [PATCH] Add new "AMD FSR FG" frame type Signed-off-by: Michael Gharbharan --- .../source/metadata/EnumFrameType.h | 3 +- .../PresentMonAPI2/PresentMonAPI.h | 3 +- .../PresentMonAPI2Tests/CsvHelper.h | 5 ++ .../SampleClient/PacedFramePlayback.cpp | 2 + IntelPresentMon/UnitTests/MetricsCore.cpp | 69 ++++++++++++++++++ PresentData/Debug.cpp | 2 + PresentData/ETW/Intel_PresentMon.h | 1 + PresentData/PresentEventEnums.hpp | 1 + PresentData/PresentMonTraceConsumer.cpp | 8 +- PresentMon/CsvOutput.cpp | 1 + Provider/Intel-PresentMon.man | Bin 21586 -> 21944 bytes Provider/PresentMonProvider.cpp | 1 + Provider/PresentMonProvider.h | 1 + 13 files changed, 93 insertions(+), 4 deletions(-) diff --git a/IntelPresentMon/Interprocess/source/metadata/EnumFrameType.h b/IntelPresentMon/Interprocess/source/metadata/EnumFrameType.h index 837669994..153e8bbbf 100644 --- a/IntelPresentMon/Interprocess/source/metadata/EnumFrameType.h +++ b/IntelPresentMon/Interprocess/source/metadata/EnumFrameType.h @@ -8,4 +8,5 @@ X_(FRAME_TYPE, APPLICATION, "Application", "", "Frame rendered by the target application") \ X_(FRAME_TYPE, REPEATED, "Application", "", "Frame rendered by the taget application") \ X_(FRAME_TYPE, INTEL_XEFG, "Intel XeSS-FG", "", "Frame generated by Intel XeSS-FG") \ - X_(FRAME_TYPE, AMD_AFMF, "AMD AFMF", "", "Frame generated by AMD Fluid Motion Frames") + X_(FRAME_TYPE, AMD_AFMF, "AMD AFMF", "", "Frame generated by AMD Fluid Motion Frames") \ + X_(FRAME_TYPE, AMD_FSR_FG, "AMD FSR FG", "", "Frame generated by AMD FSR FG") diff --git a/IntelPresentMon/PresentMonAPI2/PresentMonAPI.h b/IntelPresentMon/PresentMonAPI2/PresentMonAPI.h index e60a71a12..5274fd112 100644 --- a/IntelPresentMon/PresentMonAPI2/PresentMonAPI.h +++ b/IntelPresentMon/PresentMonAPI2/PresentMonAPI.h @@ -256,7 +256,8 @@ extern "C" { PM_FRAME_TYPE_APPLICATION, PM_FRAME_TYPE_REPEATED, PM_FRAME_TYPE_INTEL_XEFG = 50, - PM_FRAME_TYPE_AMD_AFMF = 100, + PM_FRAME_TYPE_AMD_AFMF = 100, + PM_FRAME_TYPE_AMD_FSR_FG = 101, }; enum PM_DEVICE_TYPE diff --git a/IntelPresentMon/PresentMonAPI2Tests/CsvHelper.h b/IntelPresentMon/PresentMonAPI2Tests/CsvHelper.h index b3f2cd617..8515d8f98 100644 --- a/IntelPresentMon/PresentMonAPI2Tests/CsvHelper.h +++ b/IntelPresentMon/PresentMonAPI2Tests/CsvHelper.h @@ -362,6 +362,9 @@ void CharConvert::Convert(const std::string data, T& convertedData, Header co else if (data == "Intel XeSS-FG") { convertedData = PM_FRAME_TYPE_INTEL_XEFG; } + else if (data == "AMD_FSR_FG") { + convertedData = PM_FRAME_TYPE_AMD_FSR_FG; + } else { throw CsvConversionException(Header_FrameType, line, data); } @@ -629,6 +632,8 @@ std::string TranslateFrameType(PM_FRAME_TYPE frameType) { return "AMD_AFMF"; case PM_FRAME_TYPE_INTEL_XEFG: return "Intel XeSS-FG"; + case PM_FRAME_TYPE_AMD_FSR_FG: + return "AMD_FSR_FG"; default: return ""; } diff --git a/IntelPresentMon/SampleClient/PacedFramePlayback.cpp b/IntelPresentMon/SampleClient/PacedFramePlayback.cpp index e2e566a49..68d599463 100644 --- a/IntelPresentMon/SampleClient/PacedFramePlayback.cpp +++ b/IntelPresentMon/SampleClient/PacedFramePlayback.cpp @@ -101,6 +101,8 @@ namespace return "AMD_AFMF"; case PM_FRAME_TYPE_INTEL_XEFG: return "Intel XeSS-FG"; + case PM_FRAME_TYPE_AMD_FSR_FG: + return "AMD_FSR_FG"; default: return "Other"; } diff --git a/IntelPresentMon/UnitTests/MetricsCore.cpp b/IntelPresentMon/UnitTests/MetricsCore.cpp index 2b4ac586b..98cef9dab 100644 --- a/IntelPresentMon/UnitTests/MetricsCore.cpp +++ b/IntelPresentMon/UnitTests/MetricsCore.cpp @@ -950,6 +950,75 @@ TEST_CLASS(ComputeMetricsForPresentTests) Assert::AreEqual(uint64_t(22'500), afterLookaheadPresent[0].computed.metrics.screenTimeQpc); Assert::IsTrue(HasMetricValue(afterLookaheadPresent[0].computed.metrics.msAnimationTime)); } + + TEST_METHOD(AmdFsrFg_MultiEntryPresent_ReleasesClosedIntervalGeneratedRowsImmediately_ClosingAppRowOnLookahead) + { + // AppA seed, then gen+gen+gen+AppB, then AppC for closing-app lookahead: the + // multi-gen present releases timeline origin AppA together with the 3x AMD_FSR_FG + // rows (their own nextScreenTime is already known from sibling entries in the + // same present); AppB itself releases alone once the next present supplies its + // lookahead. + QpcConverter qpc(10'000'000, 0); + UnifiedSwapChain swapChain{}; + + FrameData bootstrap{}; + bootstrap.presentStartTime = 1; + bootstrap.timeInPresent = 1; + bootstrap.readyTime = 1; + bootstrap.finalState = PresentResult::Presented; + + (void)swapChain.ProcessPresent(qpc, std::move(bootstrap)); + + FrameData seed{}; + seed.presentStartTime = 19'000; + seed.timeInPresent = 400; + seed.readyTime = 19'500; + seed.finalState = PresentResult::Presented; + seed.appSimStartTime = 18'000; + seed.displayed.PushBack({ FrameType::Application, 20'000 }); + + Assert::AreEqual(size_t(0), swapChain.ProcessPresent(qpc, std::move(seed)).size()); + + FrameData present{}; + present.presentStartTime = 20'000; + present.timeInPresent = 600; + present.readyTime = 30'000; + present.finalState = PresentResult::Presented; + present.appSimStartTime = 19'500; + present.displayed.PushBack({ FrameType::AMD_FSR_FG, 21'000 }); + present.displayed.PushBack({ FrameType::AMD_FSR_FG, 21'500 }); + present.displayed.PushBack({ FrameType::AMD_FSR_FG, 22'000 }); + present.displayed.PushBack({ FrameType::Application, 22'500 }); + + auto afterMultiGenPresent = swapChain.ProcessPresent(qpc, std::move(present)); + Assert::AreEqual(size_t(4), afterMultiGenPresent.size()); + Assert::AreEqual((int)FrameType::Application, (int)afterMultiGenPresent[0].computed.metrics.frameType); + Assert::AreEqual(uint64_t(20'000), afterMultiGenPresent[0].computed.metrics.screenTimeQpc); + Assert::IsFalse(HasMetricValue(afterMultiGenPresent[0].computed.metrics.msAnimationError)); + Assert::AreEqual((int)FrameType::AMD_FSR_FG, (int)afterMultiGenPresent[1].computed.metrics.frameType); + Assert::AreEqual((int)FrameType::AMD_FSR_FG, (int)afterMultiGenPresent[2].computed.metrics.frameType); + Assert::AreEqual((int)FrameType::AMD_FSR_FG, (int)afterMultiGenPresent[3].computed.metrics.frameType); + Assert::AreEqual(uint64_t(21'000), afterMultiGenPresent[1].computed.metrics.screenTimeQpc); + Assert::AreEqual(uint64_t(21'500), afterMultiGenPresent[2].computed.metrics.screenTimeQpc); + Assert::AreEqual(uint64_t(22'000), afterMultiGenPresent[3].computed.metrics.screenTimeQpc); + Assert::IsTrue(HasMetricValue(afterMultiGenPresent[1].computed.metrics.msAnimationTime)); + Assert::IsTrue(HasMetricValue(afterMultiGenPresent[2].computed.metrics.msAnimationTime)); + Assert::IsTrue(HasMetricValue(afterMultiGenPresent[3].computed.metrics.msAnimationTime)); + + FrameData lookahead{}; + lookahead.presentStartTime = 23'000; + lookahead.timeInPresent = 400; + lookahead.readyTime = 30'500; + lookahead.finalState = PresentResult::Presented; + lookahead.appSimStartTime = 20'000; + lookahead.displayed.PushBack({ FrameType::Application, 24'000 }); + + auto afterLookaheadPresent = swapChain.ProcessPresent(qpc, std::move(lookahead)); + Assert::AreEqual(size_t(1), afterLookaheadPresent.size()); + Assert::AreEqual((int)FrameType::Application, (int)afterLookaheadPresent[0].computed.metrics.frameType); + Assert::AreEqual(uint64_t(22'500), afterLookaheadPresent[0].computed.metrics.screenTimeQpc); + Assert::IsTrue(HasMetricValue(afterLookaheadPresent[0].computed.metrics.msAnimationTime)); + } }; TEST_CLASS(DisplayedDroppedDisplayedSequenceTests) diff --git a/PresentData/Debug.cpp b/PresentData/Debug.cpp index 398cf258f..3eefb9528 100644 --- a/PresentData/Debug.cpp +++ b/PresentData/Debug.cpp @@ -182,6 +182,7 @@ wchar_t const* PMPFrameTypeToString(Intel_PresentMon::FrameType type) case Intel_PresentMon::FrameType::Repeated: return L"Repeated"; case Intel_PresentMon::FrameType::Intel_XEFG: return L"Intel XeSS-FG"; case Intel_PresentMon::FrameType::AMD_AFMF: return L"AMD AFMF"; + case Intel_PresentMon::FrameType::AMD_FSR_FG: return L"AMD FSR FG"; } assert(false); @@ -196,6 +197,7 @@ void PrintFrameType(FrameType type) case FrameType::Repeated: wprintf(L"Repeated"); break; case FrameType::Intel_XEFG: wprintf(L"Intel XeSS-FG"); break; case FrameType::AMD_AFMF: wprintf(L"AMD AFMF"); break; + case FrameType::AMD_FSR_FG: wprintf(L"AMD FSR FG"); break; default: wprintf(L"Unknown (%u)", type); assert(false); break; } } diff --git a/PresentData/ETW/Intel_PresentMon.h b/PresentData/ETW/Intel_PresentMon.h index 33f35cffa..35af1ba72 100644 --- a/PresentData/ETW/Intel_PresentMon.h +++ b/PresentData/ETW/Intel_PresentMon.h @@ -55,6 +55,7 @@ enum class FrameType : uint8_t { Repeated = 2, Intel_XEFG = 50, AMD_AFMF = 100, + AMD_FSR_FG = 101, }; enum class InputType : uint32_t { diff --git a/PresentData/PresentEventEnums.hpp b/PresentData/PresentEventEnums.hpp index ff808f590..49f85a8dd 100644 --- a/PresentData/PresentEventEnums.hpp +++ b/PresentData/PresentEventEnums.hpp @@ -40,4 +40,5 @@ enum class FrameType { Repeated = 3, Intel_XEFG = 50, AMD_AFMF = 100, + AMD_FSR_FG = 101, }; \ No newline at end of file diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index 0a05137e1..37a52ac9c 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -39,6 +39,7 @@ static inline FrameType ConvertPMPFrameTypeToFrameType(Intel_PresentMon::FrameTy case Intel_PresentMon::FrameType::Repeated: return FrameType::Repeated; case Intel_PresentMon::FrameType::Intel_XEFG: return FrameType::Intel_XEFG; case Intel_PresentMon::FrameType::AMD_AFMF: return FrameType::AMD_AFMF; + case Intel_PresentMon::FrameType::AMD_FSR_FG: return FrameType::AMD_FSR_FG; } DebugAssert(false); @@ -70,7 +71,8 @@ static inline bool ShouldAppendApplicationAfterGeneratedDisplay(std::shared_ptr< { bool hasGeneratedFrame = HasDisplayedFrameType(p, FrameType::Intel_XEFG) || - HasDisplayedFrameType(p, FrameType::AMD_AFMF); + HasDisplayedFrameType(p, FrameType::AMD_AFMF) || + HasDisplayedFrameType(p, FrameType::AMD_FSR_FG); bool hasApplicationFrame = HasDisplayedFrameType(p, FrameType::Application); @@ -3900,7 +3902,9 @@ bool PMTraceConsumer::IsApplicationPresent(std::shared_ptr const& } auto frameType = present->Displayed[0]; - return ((frameType.first != FrameType::Intel_XEFG) && (frameType.first != FrameType::AMD_AFMF)); + return ((frameType.first != FrameType::Intel_XEFG) && + (frameType.first != FrameType::AMD_AFMF) && + (frameType.first != FrameType::AMD_FSR_FG)); } void PMTraceConsumer::SetAppTimingDataAsComplete(uint32_t processId, uint32_t appFrameId) { diff --git a/PresentMon/CsvOutput.cpp b/PresentMon/CsvOutput.cpp index 4b9cc9801..e2223b9ca 100644 --- a/PresentMon/CsvOutput.cpp +++ b/PresentMon/CsvOutput.cpp @@ -64,6 +64,7 @@ const char* FrameTypeToString(FrameType ft) case FrameType::Application: return "Application"; case FrameType::Intel_XEFG: return "Intel XeSS-FG"; case FrameType::AMD_AFMF: return "AMD AFMF"; + case FrameType::AMD_FSR_FG: return "AMD FSR FG"; } return "Unknown"; diff --git a/Provider/Intel-PresentMon.man b/Provider/Intel-PresentMon.man index c9b01f0f3f31377517b83dd4901a053258764ecc..5b96a0c3ea8531a35f59812837b9ea81bc055cb0 100644 GIT binary patch delta 99 zcmcb#f^o-c#tj!dCLdtunEb&@VzPk;%j5trpUDj#5|cN0S!}-GA;SscC>t^;F(@#U zF(fkNFqAT+GS~w7jtssGE)4MuZVbT;K@9Pe?+0X0e&8uK`JCS*AT6?aMbIin0I1_3 A2LJ#7 delta 31 mcmdn7n(@*K#tj!dHc#-B;hem{OKftV|0EzSvY9Vr6(a!UQVl-< diff --git a/Provider/PresentMonProvider.cpp b/Provider/PresentMonProvider.cpp index 5eef26c76..1e5eb55a3 100644 --- a/Provider/PresentMonProvider.cpp +++ b/Provider/PresentMonProvider.cpp @@ -187,6 +187,7 @@ bool IsValid( frameType == PresentMonProvider_FrameType_Repeated || frameType == PresentMonProvider_FrameType_Intel_XEFG || frameType == PresentMonProvider_FrameType_AMD_AFMF || + frameType == PresentMonProvider_FrameType_AMD_FSR_FG || frameType == PresentMonProvider_FrameType_NVIDIA_DLSS; } diff --git a/Provider/PresentMonProvider.h b/Provider/PresentMonProvider.h index bf2449d37..9dc62b880 100644 --- a/Provider/PresentMonProvider.h +++ b/Provider/PresentMonProvider.h @@ -71,6 +71,7 @@ enum PresentMonProvider_FrameType { PresentMonProvider_FrameType_Repeated, // The frame rendered by the application is being repeated. PresentMonProvider_FrameType_Intel_XEFG = 50, // Frame generated by Intel Xe Frame Generation. PresentMonProvider_FrameType_AMD_AFMF = 100, // Frame generated by AMD Fluid Motion Frames. + PresentMonProvider_FrameType_AMD_FSR_FG = 101, // Frame generated by AMD FSR Frame Generation. PresentMonProvider_FrameType_NVIDIA_DLSS = 150, // Frame generated by NVIDIA DLSS. };