diff --git a/PresentData/ETW/NV_DD.h b/PresentData/ETW/NV_DD.h new file mode 100644 index 00000000..879ffba1 --- /dev/null +++ b/PresentData/ETW/NV_DD.h @@ -0,0 +1,28 @@ +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved +// SPDX-License-Identifier: MIT + +#pragma once + +namespace NvidiaDisplayDriver_Events { + + enum class Keyword : uint64_t { + None = 0x00, + }; + + struct __declspec(uuid("{AE4F8626-8265-40D1-A70B-11B64240E8E9}")) GUID_STRUCT; + static const auto GUID = __uuidof(GUID_STRUCT); + + // Event descriptors: +#define EVENT_DESCRIPTOR_DECL(name_, id_, version_, channel_, level_, opcode_, task_, keyword_) struct name_ { \ + static uint16_t const Id = id_; \ + static uint8_t const Version = version_; \ + static uint8_t const Channel = channel_; \ + static uint8_t const Level = level_; \ + static uint8_t const Opcode = opcode_; \ + static uint16_t const Task = task_; \ + static Keyword const Keyword = (Keyword) keyword_; \ +}; + + EVENT_DESCRIPTOR_DECL(FlipRequest, 0x0001, 0x00, 0x13, 0x04, 0x0a, 0x0001, 0x1000000000000000) +#undef EVENT_DESCRIPTOR_DECL +} \ No newline at end of file diff --git a/PresentData/NvidiaTraceConsumer.cpp b/PresentData/NvidiaTraceConsumer.cpp new file mode 100644 index 00000000..cd535d02 --- /dev/null +++ b/PresentData/NvidiaTraceConsumer.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved +// SPDX-License-Identifier: MIT + +#include "PresentMonTraceConsumer.hpp" +#include "NvidiaTraceConsumer.hpp" + +NVTraceConsumer::NVTraceConsumer() +{ + // Nothing to do +} + +NVTraceConsumer::~NVTraceConsumer() +{ + // Nothing to do +} + +void NVTraceConsumer::HandleNvidiaDisplayDriverEvent(EVENT_RECORD* const pEventRecord, PMTraceConsumer* const pmConsumer) +{ + enum { + FlipRequest = 1 + }; + + auto const& hdr = pEventRecord->EventHeader; + switch (hdr.EventDescriptor.Id) { + case FlipRequest: { + auto alloc = pmConsumer->mMetadata.GetEventData(pEventRecord, L"alloc"); + auto vidPnSourceId = pmConsumer->mMetadata.GetEventData(pEventRecord, L"vidPnSourceId"); + auto& lastFlipTime = mLastFlipTimeByHead[vidPnSourceId]; + auto ts = pmConsumer->mMetadata.GetEventData(pEventRecord, L"ts"); + auto token = pmConsumer->mMetadata.GetEventData(pEventRecord, L"token"); + uint64_t proposedFlipTime = 0; + uint64_t delay = 0; + + if (token == mLastFlipToken) { + return; + } + mLastFlipToken = token; + + if (alloc == 0) { + assert(!delay); + assert(ts); + // proposedFlipTime in number of ticks + proposedFlipTime = ts; + auto t1 = *(uint64_t*)&hdr.TimeStamp; + if (proposedFlipTime >= t1) { + delay = proposedFlipTime - t1; + } + + if (proposedFlipTime && lastFlipTime && (proposedFlipTime < lastFlipTime)) { + delay = lastFlipTime - proposedFlipTime; + proposedFlipTime = lastFlipTime; + } + } + + lastFlipTime = proposedFlipTime; + + { + NvFlipRequest flipRequest; + flipRequest.FlipDelay = delay; + flipRequest.FlipToken = token; + mNvFlipRequestByThreadId.emplace(hdr.ThreadId, flipRequest); + } + break; + } + default: + break; + } +} + +void NVTraceConsumer::ApplyFlipDelay(PresentEvent* present, uint32_t threadId) +{ + auto flipIter = mNvFlipRequestByThreadId.find(threadId); + if (flipIter != mNvFlipRequestByThreadId.end()) { + present->FlipDelay = flipIter->second.FlipDelay; + present->FlipToken = flipIter->second.FlipToken; + // Clear the map (we want the whole map cleared, not just the element with the thread) + mNvFlipRequestByThreadId.clear(); + } +} \ No newline at end of file diff --git a/PresentData/NvidiaTraceConsumer.hpp b/PresentData/NvidiaTraceConsumer.hpp new file mode 100644 index 00000000..3760d8d2 --- /dev/null +++ b/PresentData/NvidiaTraceConsumer.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved +// SPDX-License-Identifier: MIT +#pragma once + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include +#include // must include after windows.h + +struct NvFlipRequest { + uint64_t FlipDelay; + uint32_t FlipToken; +}; + +struct NVTraceConsumer +{ + NVTraceConsumer(); + ~NVTraceConsumer(); + + NVTraceConsumer(const NVTraceConsumer&) = delete; + NVTraceConsumer& operator=(const NVTraceConsumer&) = delete; + NVTraceConsumer(NVTraceConsumer&&) = delete; + NVTraceConsumer& operator=(NVTraceConsumer&&) = delete; + + // ThreaId -> NV FlipRequest + std::unordered_map mNvFlipRequestByThreadId; + // vidPnSourceId -> flip qpcTime + std::unordered_map mLastFlipTimeByHead; + + void HandleNvidiaDisplayDriverEvent(EVENT_RECORD* const pEventRecord, PMTraceConsumer* const pmConsumer); + void ApplyFlipDelay(PresentEvent* present, uint32_t threadId); + + uint32_t mLastFlipToken = 0; +}; \ No newline at end of file diff --git a/PresentData/PresentData.vcxproj b/PresentData/PresentData.vcxproj index 5f2791f5..52cb7732 100644 --- a/PresentData/PresentData.vcxproj +++ b/PresentData/PresentData.vcxproj @@ -353,7 +353,9 @@ + + @@ -361,6 +363,7 @@ + diff --git a/PresentData/PresentData.vcxproj.filters b/PresentData/PresentData.vcxproj.filters index 461fc546..f6158e32 100644 --- a/PresentData/PresentData.vcxproj.filters +++ b/PresentData/PresentData.vcxproj.filters @@ -39,6 +39,10 @@ ETW + + ETW + + @@ -46,6 +50,7 @@ + diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index edd5f8fa..cf4cbbd5 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #include "PresentMonTraceConsumer.hpp" @@ -930,6 +931,9 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord) auto present = FindPresentBySubmitSequence(submitSequence); if (present != nullptr) { + // Apply Nvidia FlipDelay, if any, to the presentEvent + mNvTraceConsumer.ApplyFlipDelay(present.get(), hdr.ThreadId); + // Complete the GPU tracking for this frame. // // For some present modes (e.g., Hardware_Legacy_Flip) this may be @@ -952,7 +956,7 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord) // this the present screen time. if (FlipEntryStatusAfterFlip != (uint32_t) Microsoft_Windows_DxgKrnl::FlipEntryStatus::FlipWaitHSync) { - SetScreenTime(present, hdr.TimeStamp.QuadPart); + SetScreenTime(present, hdr.TimeStamp.QuadPart + present->FlipDelay); if (present->PresentMode == PresentMode::Hardware_Legacy_Flip) { CompletePresent(present); @@ -1353,6 +1357,12 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord) assert(!mFilteredEvents); // Assert that filtering is working if expected } + +void PMTraceConsumer::HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord) +{ + mNvTraceConsumer.HandleNvidiaDisplayDriverEvent(pEventRecord, this); +} + void PMTraceConsumer::HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord) { using namespace Microsoft_Windows_DxgKrnl::Win7; diff --git a/PresentData/PresentMonTraceConsumer.hpp b/PresentData/PresentMonTraceConsumer.hpp index b219c59c..78a27300 100644 --- a/PresentData/PresentMonTraceConsumer.hpp +++ b/PresentData/PresentMonTraceConsumer.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #pragma once @@ -24,6 +25,7 @@ #include "Debug.hpp" #include "GpuTrace.hpp" #include "TraceConsumer.hpp" +#include "NvidiaTraceConsumer.hpp" #include "../IntelPresentMon/CommonUtilities/Hash.h" // PresentMode represents the different paths a present can take on windows. @@ -271,6 +273,9 @@ struct PresentEvent { // until a PresentFrameType_Info event with a different FrameId). bool WaitingForFrameId; + // Data from NV DisplayDriver event + uint64_t FlipDelay = 0; + uint32_t FlipToken = 0; PresentEvent(); PresentEvent(uint32_t fid); @@ -487,6 +492,8 @@ struct PMTraceConsumer std::unordered_map mRetrievedInput; // ProcessID -> InputData + // Trace consumer that handles events coming from Nvidia DisplayDriver + NVTraceConsumer mNvTraceConsumer; // ------------------------------------------------------------------------------------------- // Functions for decoding ETW and analysing process and present events. @@ -516,6 +523,7 @@ struct PMTraceConsumer void HandleDWMEvent(EVENT_RECORD* pEventRecord); void HandleMetadataEvent(EVENT_RECORD* pEventRecord); void HandleIntelPresentMonEvent(EVENT_RECORD* pEventRecord); + void HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord); void HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord); void HandleWin7DxgkFlip(EVENT_RECORD* pEventRecord); diff --git a/PresentData/PresentMonTraceSession.cpp b/PresentData/PresentMonTraceSession.cpp index 71ca9bac..92c26320 100644 --- a/PresentData/PresentMonTraceSession.cpp +++ b/PresentData/PresentMonTraceSession.cpp @@ -1,9 +1,11 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #include "Debug.hpp" #include "PresentMonTraceConsumer.hpp" #include "PresentMonTraceSession.hpp" +#include "NvidiaTraceConsumer.hpp" #include "ETW/Microsoft_Windows_D3D9.h" #include "ETW/Microsoft_Windows_Dwm_Core.h" @@ -16,6 +18,7 @@ #include "ETW/Microsoft_Windows_Win32k.h" #include "ETW/NT_Process.h" #include "ETW/Intel_PresentMon.h" +#include "ETW/NV_DD.h" namespace { @@ -280,6 +283,7 @@ void DisableProviders(TRACEHANDLE sessionHandle) status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_DxgKrnl::Win7::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Kernel_Process::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Win32k::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); + status = EnableTraceEx2(sessionHandle, &NvidiaDisplayDriver_Events::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); } template< @@ -363,6 +367,10 @@ void CALLBACK EventRecordCallback(EVENT_RECORD* pEventRecord) session->mPMConsumer->HandleWin7DxgkMMIOFlip(pEventRecord); return; } + if (hdr.ProviderId == NvidiaDisplayDriver_Events::GUID) { + session->mPMConsumer->HandleNvidiaDisplayDriverEvent(pEventRecord); + return; + } } if constexpr (TRACK_PRESENTMON) { @@ -777,6 +785,13 @@ ULONG EnableProvidersListing( if (status != ERROR_SUCCESS) return status; } + // Nvidia_DisplayDriver + // + provider.ClearFilter(); + provider.AddEvent(); + status = provider.Enable(sessionHandle, NvidiaDisplayDriver_Events::GUID); + if (status != ERROR_SUCCESS) return status; + return ERROR_SUCCESS; } diff --git a/PresentData/PresentMonTraceSession.hpp b/PresentData/PresentMonTraceSession.hpp index 31f696bc..8cc8f119 100644 --- a/PresentData/PresentMonTraceSession.hpp +++ b/PresentData/PresentMonTraceSession.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT struct PMTraceConsumer; diff --git a/PresentMon/CsvOutput.cpp b/PresentMon/CsvOutput.cpp index 5850e10e..39c3f55d 100644 --- a/PresentMon/CsvOutput.cpp +++ b/PresentMon/CsvOutput.cpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #include "PresentMon.hpp" @@ -156,7 +157,8 @@ void WriteCsvHeader(FILE* fp) L",PresentMode" L",msUntilRenderComplete" L",msUntilDisplayed" - L",msBetweenDisplayChange"); + L",msBetweenDisplayChange" + L",msFlipDelay"); } if (args.mTrackGPU) { fwprintf(fp, L",msUntilRenderStart" @@ -221,11 +223,12 @@ void WriteCsvRow( fwprintf(fp, L",%.*lf,%.*lf", DBL_DIG - 1, metrics.msInPresentApi, DBL_DIG - 1, metrics.msBetweenPresents); if (args.mTrackDisplay) { - fwprintf(fp, L",%d,%hs,%.*lf,%.*lf,%.*lf", p.SupportsTearing, - PresentModeToString(p.PresentMode), - DBL_DIG - 1, metrics.msUntilRenderComplete, - DBL_DIG - 1, metrics.msUntilDisplayed, - DBL_DIG - 1, metrics.msBetweenDisplayChange); + fwprintf(fp, L",%d,%hs,%.*lf,%.*lf,%.*lf,%.*lf", p.SupportsTearing, + PresentModeToString(p.PresentMode), + DBL_DIG - 1, metrics.msUntilRenderComplete, + DBL_DIG - 1, metrics.msUntilDisplayed, + DBL_DIG - 1, metrics.msBetweenDisplayChange, + DBL_DIG - 1, metrics.msFlipDelay); } if (args.mTrackGPU) { fwprintf(fp, L",%.*lf,%.*lf", DBL_DIG - 1, metrics.msUntilRenderStart, @@ -306,7 +309,8 @@ void WriteCsvHeader(FILE* fp) fwprintf(fp, L",DisplayLatency" L",DisplayedTime" L",AnimationError" - L",AnimationTime"); + L",AnimationTime" + L",FlipDelay"); } if (args.mTrackInput) { fwprintf(fp, L",AllInputToPhotonLatency"); @@ -397,12 +401,13 @@ void WriteCsvRow( } if (args.mTrackDisplay) { if (metrics.mDisplayedTime == 0.0) { - fwprintf(fp, L",NA,NA,NA,NA"); + fwprintf(fp, L",NA,NA,NA,NA,NA"); } else { - fwprintf(fp, L",%.4lf,%.4lf,%.4lf,%.4lf", metrics.mDisplayLatency, - metrics.mDisplayedTime, - metrics.mAnimationError, - metrics.mAnimationTime); + fwprintf(fp, L",%.4lf,%.4lf,%.4lf,%.4lf,%.4lf", metrics.mDisplayLatency, + metrics.mDisplayedTime, + metrics.mAnimationError, + metrics.mAnimationTime, + metrics.mFlipDelay); } } if (args.mTrackInput) { diff --git a/PresentMon/MainThread.cpp b/PresentMon/MainThread.cpp index 6fefe908..cb9485dc 100644 --- a/PresentMon/MainThread.cpp +++ b/PresentMon/MainThread.cpp @@ -1,6 +1,8 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT +#define WINVER _WIN32_WINNT_WIN10 // To make TdhLoadManifestFromBinary available #include "PresentMon.hpp" enum { @@ -15,6 +17,24 @@ static HWND gWnd = NULL; static bool gIsRecording = false; static uint32_t gHotkeyIgnoreCount = 0; +static bool LoadNVDDManifest() +{ + WCHAR CurrModuleDirW[MAX_PATH]; + if (!GetModuleFileNameW(NULL, CurrModuleDirW, MAX_PATH)) { + PrintError(L"error: failed to get exe fullpath\n"); + return false; + } + + std::wstring mypath = CurrModuleDirW; + auto status = TdhLoadManifestFromBinary(&mypath[0]); + if (ERROR_SUCCESS != status) + { + PrintError(L"error: failed to load manifest embedded in %s\n", mypath.c_str()); + return false; + } + return true; +} + static bool EnableScrollLock(bool enable) { auto const& args = GetCommandLineArgs(); @@ -179,6 +199,11 @@ int wmain(int argc, wchar_t** argv) LoadLibraryExA("tdh.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); LoadLibraryExA("user32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + // Load NVIDIA DisplayDriver event manifest embedded in the PresentMon binary + if (!LoadNVDDManifest()) { + return 1; + } + // Initialize console SetThreadDescription(GetCurrentThread(), L"PresentMon Consumer Thread"); InitializeConsole(); diff --git a/PresentMon/OutputThread.cpp b/PresentMon/OutputThread.cpp index 47d5f20f..16fed8df 100644 --- a/PresentMon/OutputThread.cpp +++ b/PresentMon/OutputThread.cpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #include "PresentMon.hpp" @@ -250,11 +251,33 @@ static void UpdateChain( chain->mLastPresent->TimeInPresent; } chain->mLastDisplayedScreenTime = p->Displayed.empty() ? 0 : p->Displayed.back().second; + // Update last flipDelay. For NV GPU, size of p->Displayed can only be empty or 1 + chain->mLastDisplayedFlipDelay = p->Displayed.empty() ? 0 : p->FlipDelay; } chain->mLastPresent = p; } +static void AdjustScreenTimeForCollapsedPresentNV1( + SwapChainData* chain, + std::shared_ptr const& p, + uint64_t& screenTime) +{ + if (chain->mLastDisplayedFlipDelay > 0 && (chain->mLastDisplayedScreenTime > screenTime)) { + // If chain->mLastDisplayedScreenTime that is adjusted by flipDelay is larger than screenTime, + // it implies the last displayed present is a collapsed present, or a runt frame. + // So we adjust the screenTime and flipDelay of screenTime, + // effectively making screenTime equals to chain->mLastDisplayedScreenTime. + + // Cast away constness of p to adjust the screenTime and flipDelay. + PresentEvent* pp = const_cast(p.get()); + if (!pp->Displayed.empty()) { + pp->FlipDelay += chain->mLastDisplayedScreenTime - screenTime; + pp->Displayed[0].second = chain->mLastDisplayedScreenTime; + } + } +} + static void ReportMetrics1( PMTraceSession const& pmSession, ProcessInfo* processInfo, @@ -267,6 +290,9 @@ static void ReportMetrics1( uint64_t screenTime = p->Displayed.empty() ? 0 : p->Displayed[0].second; + // Special handling for NV flipDelay + AdjustScreenTimeForCollapsedPresentNV1(chain, p, screenTime); + FrameMetrics1 metrics; metrics.msBetweenPresents = chain->mLastPresent == nullptr ? 0 : pmSession.TimestampDeltaToUnsignedMilliSeconds(chain->mLastPresent->PresentStartTime, p->PresentStartTime); metrics.msInPresentApi = pmSession.TimestampDeltaToMilliSeconds(p->TimeInPresent); @@ -278,6 +304,7 @@ static void ReportMetrics1( metrics.msVideoDuration = pmSession.TimestampDeltaToMilliSeconds(p->GPUVideoDuration); metrics.msSinceInput = p->InputTime == 0 ? 0 : pmSession.TimestampDeltaToMilliSeconds(p->PresentStartTime - p->InputTime); metrics.qpcScreenTime = screenTime; + metrics.msFlipDelay = p->FlipDelay ? pmSession.TimestampDeltaToMilliSeconds(p->FlipDelay) : 0; if (isRecording) { UpdateCsv(pmSession, processInfo, *p, metrics); @@ -311,6 +338,28 @@ static void CalculateAnimationTime( } } + +static void AdjustScreenTimeForCollapsedPresentNV( + std::shared_ptr const& p, + PresentEvent const* nextDisplayedPresent, + uint64_t& screenTime, + uint64_t& nextScreenTime) +{ + // nextDisplayedPresent should always be non-null for NV GPU. + if (p->FlipDelay && screenTime > nextScreenTime && nextDisplayedPresent) { + // If screenTime that is adjusted by flipDelay is larger than nextScreenTime, + // it implies this present is a collapsed present, or a runt frame. + // So we adjust the screenTime and flipDelay of nextDisplayedPresent, + // effectively making nextScreenTime equals to screenTime. + + // Cast away constness of nextDisplayedPresent to adjust the screenTime and flipDelay. + PresentEvent* nextDispPresent = const_cast(nextDisplayedPresent); + nextDispPresent->FlipDelay += (screenTime - nextScreenTime); + nextScreenTime = screenTime; + nextDispPresent->Displayed[0].second = nextScreenTime; + } +} + static void ReportMetricsHelper( PMTraceSession const& pmSession, ProcessInfo* processInfo, @@ -405,6 +454,10 @@ static void ReportMetricsHelper( } if (displayed) { + // Special handling for NV flipDelay + AdjustScreenTimeForCollapsedPresentNV(p, nextDisplayedPresent, screenTime, nextScreenTime); + + metrics.mFlipDelay = p->FlipDelay ? pmSession.TimestampDeltaToMilliSeconds(p->FlipDelay) : 0; metrics.mDisplayLatency = pmSession.TimestampDeltaToUnsignedMilliSeconds(metrics.mCPUStart, screenTime); metrics.mDisplayedTime = pmSession.TimestampDeltaToUnsignedMilliSeconds(screenTime, nextScreenTime); metrics.mScreenTime = screenTime; diff --git a/PresentMon/PresentMon.hpp b/PresentMon/PresentMon.hpp index 3d9cbcca..65f91219 100644 --- a/PresentMon/PresentMon.hpp +++ b/PresentMon/PresentMon.hpp @@ -1,4 +1,5 @@ // Copyright (C) 2017-2024 Intel Corporation +// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #pragma once @@ -117,6 +118,9 @@ struct FrameMetrics { double mInstrumentedGpuLatency; double mReadyTimeToDisplayLatency; double mInstrumentedInputTime; + + // Internal NVIDIA Metrics + double mFlipDelay; }; struct FrameMetrics1 { @@ -130,6 +134,9 @@ struct FrameMetrics1 { double msVideoDuration; double msSinceInput; uint64_t qpcScreenTime; + + // Internal NVIDIA Metrics + double msFlipDelay; }; // We store SwapChainData per process and per swapchain, where we maintain: @@ -162,6 +169,9 @@ struct SwapChainData { float mAvgGPUDuration = 0.f; float mAvgDisplayLatency = 0.f; float mAvgDisplayedTime = 0.f; + + // Internal NVIDIA Metrics + uint64_t mLastDisplayedFlipDelay = 0; }; struct ProcessInfo { diff --git a/PresentMon/PresentMon.vcxproj b/PresentMon/PresentMon.vcxproj index b4af26d5..5d2a5457 100644 --- a/PresentMon/PresentMon.vcxproj +++ b/PresentMon/PresentMon.vcxproj @@ -94,6 +94,9 @@ false + + ClCompile + false @@ -180,6 +183,20 @@ {892028e5-32f6-45fc-8ab2-90fcbcac4bf6} + + + Document + mc.exe -um -v -z %(Filename)Events -h .\ %(FullPath) + Autogenerate header from DD events manifest + %(Filename)Events.rc;%(Filename)Events.h;%(Filename)_MSG00001.bin + mc.exe -um -v -z %(Filename)Events -h .\ %(FullPath) + Autogenerate header from DD events manifest + %(Filename)Events.rc;%(Filename)Events.h;%(Filename)_MSG00001.bin + + + + + diff --git a/PresentMon/ddETWExternal.xml b/PresentMon/ddETWExternal.xml new file mode 100644 index 00000000..510ec353 Binary files /dev/null and b/PresentMon/ddETWExternal.xml differ diff --git a/PresentMon/ddETWExternalRcWrapper.rc b/PresentMon/ddETWExternalRcWrapper.rc new file mode 100644 index 00000000..0a2ec5cb --- /dev/null +++ b/PresentMon/ddETWExternalRcWrapper.rc @@ -0,0 +1 @@ +#include "ddETWExternalEvents.rc" \ No newline at end of file