Skip to content

Commit 349566e

Browse files
committed
Add support of NVIDIA DLSS4 flip-metering
1. Adds a custom build step in PresentMon.vcxproj to compile and embed the manifest of NV DisplayDriver (DD) ETW event in the PresentMon binary. The custom build is currently added for x64 build only. 2. Adds NVTraceConsumer for handling NV DD events that informs the metering of each flip. 3. Updates the CSV output to take into account flip metering. Metrics "DisplayedTime" and "MsBetweenDisplayChange" (V1 metrics) now reflect actual flip intervals when DLSS4 flip-metering is engaged.
1 parent ffbb9ce commit 349566e

16 files changed

+310
-13
lines changed

PresentData/ETW/NV_DD.h

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
2+
// SPDX-License-Identifier: MIT
3+
4+
#pragma once
5+
6+
namespace NvidiaDisplayDriver_Events {
7+
8+
enum class Keyword : uint64_t {
9+
None = 0x00,
10+
};
11+
12+
struct __declspec(uuid("{AE4F8626-8265-40D1-A70B-11B64240E8E9}")) GUID_STRUCT;
13+
static const auto GUID = __uuidof(GUID_STRUCT);
14+
15+
// Event descriptors:
16+
#define EVENT_DESCRIPTOR_DECL(name_, id_, version_, channel_, level_, opcode_, task_, keyword_) struct name_ { \
17+
static uint16_t const Id = id_; \
18+
static uint8_t const Version = version_; \
19+
static uint8_t const Channel = channel_; \
20+
static uint8_t const Level = level_; \
21+
static uint8_t const Opcode = opcode_; \
22+
static uint16_t const Task = task_; \
23+
static Keyword const Keyword = (Keyword) keyword_; \
24+
};
25+
26+
EVENT_DESCRIPTOR_DECL(FlipRequest, 0x0001, 0x00, 0x13, 0x04, 0x0a, 0x0001, 0x1000000000000000)
27+
#undef EVENT_DESCRIPTOR_DECL
28+
}

PresentData/NvidiaTraceConsumer.cpp

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
2+
// SPDX-License-Identifier: MIT
3+
4+
#include "PresentMonTraceConsumer.hpp"
5+
#include "NvidiaTraceConsumer.hpp"
6+
7+
NVTraceConsumer::NVTraceConsumer()
8+
{
9+
// Nothing to do
10+
}
11+
12+
NVTraceConsumer::~NVTraceConsumer()
13+
{
14+
// Nothing to do
15+
}
16+
17+
void NVTraceConsumer::HandleNvidiaDisplayDriverEvent(EVENT_RECORD* const pEventRecord, PMTraceConsumer* const pmConsumer)
18+
{
19+
enum {
20+
FlipRequest = 1
21+
};
22+
23+
auto const& hdr = pEventRecord->EventHeader;
24+
switch (hdr.EventDescriptor.Id) {
25+
case FlipRequest: {
26+
auto alloc = pmConsumer->mMetadata.GetEventData<uint64_t>(pEventRecord, L"alloc");
27+
auto vidPnSourceId = pmConsumer->mMetadata.GetEventData<uint32_t>(pEventRecord, L"vidPnSourceId");
28+
auto& lastFlipTime = mLastFlipTimeByHead[vidPnSourceId];
29+
auto ts = pmConsumer->mMetadata.GetEventData<uint64_t>(pEventRecord, L"ts");
30+
auto token = pmConsumer->mMetadata.GetEventData<uint32_t>(pEventRecord, L"token");
31+
uint64_t proposedFlipTime = 0;
32+
uint64_t delay = 0;
33+
34+
if (token == mLastFlipToken) {
35+
return;
36+
}
37+
mLastFlipToken = token;
38+
39+
if (alloc == 0) {
40+
assert(!delay);
41+
assert(ts);
42+
// proposedFlipTime in number of ticks
43+
proposedFlipTime = ts;
44+
auto t1 = *(uint64_t*)&hdr.TimeStamp;
45+
if (proposedFlipTime >= t1) {
46+
delay = proposedFlipTime - t1;
47+
}
48+
49+
if (proposedFlipTime && lastFlipTime && (proposedFlipTime < lastFlipTime)) {
50+
delay = lastFlipTime - proposedFlipTime;
51+
proposedFlipTime = lastFlipTime;
52+
}
53+
}
54+
55+
lastFlipTime = proposedFlipTime;
56+
57+
{
58+
NvFlipRequest flipRequset;
59+
flipRequset.FlipDelay = delay;
60+
flipRequset.FlipToken = token;
61+
mNvFlipRequestByThreadId.emplace(hdr.ThreadId, flipRequset);
62+
}
63+
break;
64+
}
65+
default:
66+
break;
67+
}
68+
}
69+
70+
void NVTraceConsumer::ApplyFlipDelay(PresentEvent* present, uint32_t threadId)
71+
{
72+
auto flipIter = mNvFlipRequestByThreadId.find(threadId);
73+
if (flipIter != mNvFlipRequestByThreadId.end()) {
74+
present->FlipDelay = flipIter->second.FlipDelay;
75+
present->FlipToken = flipIter->second.FlipToken;
76+
// Clear the map (we want the whole map cleared, not just the element with the thread)
77+
mNvFlipRequestByThreadId.clear();
78+
}
79+
}

PresentData/NvidiaTraceConsumer.hpp

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
2+
// SPDX-License-Identifier: MIT
3+
#pragma once
4+
5+
#ifndef NOMINMAX
6+
#define NOMINMAX
7+
#endif
8+
9+
#include <unordered_map>
10+
#include <windows.h>
11+
#include <evntcons.h> // must include after windows.h
12+
13+
struct NvFlipRequest {
14+
uint64_t FlipDelay;
15+
uint32_t FlipToken;
16+
};
17+
18+
struct NVTraceConsumer
19+
{
20+
NVTraceConsumer();
21+
~NVTraceConsumer();
22+
23+
NVTraceConsumer(const NVTraceConsumer&) = delete;
24+
NVTraceConsumer& operator=(const NVTraceConsumer&) = delete;
25+
NVTraceConsumer(NVTraceConsumer&&) = delete;
26+
NVTraceConsumer& operator=(NVTraceConsumer&&) = delete;
27+
28+
// ThreaId -> NV FlipRequest
29+
std::unordered_map<uint32_t, NvFlipRequest> mNvFlipRequestByThreadId;
30+
// vidPnSourceId -> flip qpcTime
31+
std::unordered_map<uint32_t, uint64_t> mLastFlipTimeByHead;
32+
33+
void HandleNvidiaDisplayDriverEvent(EVENT_RECORD* const pEventRecord, PMTraceConsumer* const pmConsumer);
34+
void ApplyFlipDelay(PresentEvent* present, uint32_t threadId);
35+
36+
uint32_t mLastFlipToken = 0;
37+
};

PresentData/PresentData.vcxproj

+3
Original file line numberDiff line numberDiff line change
@@ -353,14 +353,17 @@
353353
<ClInclude Include="ETW\Microsoft_Windows_Win32k.h" />
354354
<ClInclude Include="ETW\NT_Process.h" />
355355
<ClInclude Include="Debug.hpp" />
356+
<ClInclude Include="ETW\NV_DD.h" />
356357
<ClInclude Include="GpuTrace.hpp" />
358+
<ClInclude Include="NvidiaTraceConsumer.hpp" />
357359
<ClInclude Include="PresentMonTraceConsumer.hpp" />
358360
<ClInclude Include="TraceConsumer.hpp" />
359361
<ClInclude Include="PresentMonTraceSession.hpp" />
360362
</ItemGroup>
361363
<ItemGroup>
362364
<ClCompile Include="Debug.cpp" />
363365
<ClCompile Include="GpuTrace.cpp" />
366+
<ClCompile Include="NvidiaTraceConsumer.cpp" />
364367
<ClCompile Include="PresentMonTraceConsumer.cpp" />
365368
<ClCompile Include="TraceConsumer.cpp" />
366369
<ClCompile Include="PresentMonTraceSession.cpp" />

PresentData/PresentData.vcxproj.filters

+5
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@
3939
<ClInclude Include="ETW\Microsoft_Windows_Dwm_Core_Win7.h">
4040
<Filter>ETW</Filter>
4141
</ClInclude>
42+
<ClInclude Include="ETW\NV_DD.h">
43+
<Filter>ETW</Filter>
44+
</ClInclude>
45+
<ClInclude Include="NvidiaTraceConsumer.hpp" />
4246
</ItemGroup>
4347
<ItemGroup>
4448
<ClCompile Include="Debug.cpp" />
4549
<ClCompile Include="PresentMonTraceConsumer.cpp" />
4650
<ClCompile Include="TraceConsumer.cpp" />
4751
<ClCompile Include="PresentMonTraceSession.cpp" />
4852
<ClCompile Include="GpuTrace.cpp" />
53+
<ClCompile Include="NvidiaTraceConsumer.cpp" />
4954
</ItemGroup>
5055
<ItemGroup>
5156
<Filter Include="ETW">

PresentData/PresentMonTraceConsumer.cpp

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (C) 2017-2024 Intel Corporation
2+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
23
// SPDX-License-Identifier: MIT
34

45
#include "PresentMonTraceConsumer.hpp"
@@ -930,6 +931,9 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
930931
auto present = FindPresentBySubmitSequence(submitSequence);
931932
if (present != nullptr) {
932933

934+
// Apply Nvidia FlipDelay, if any, to the presentEvent
935+
mNvTraceConsumer.ApplyFlipDelay(present.get(), hdr.ThreadId);
936+
933937
// Complete the GPU tracking for this frame.
934938
//
935939
// For some present modes (e.g., Hardware_Legacy_Flip) this may be
@@ -952,7 +956,7 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
952956
// this the present screen time.
953957
if (FlipEntryStatusAfterFlip != (uint32_t) Microsoft_Windows_DxgKrnl::FlipEntryStatus::FlipWaitHSync) {
954958

955-
SetScreenTime(present, hdr.TimeStamp.QuadPart);
959+
SetScreenTime(present, hdr.TimeStamp.QuadPart + present->FlipDelay);
956960

957961
if (present->PresentMode == PresentMode::Hardware_Legacy_Flip) {
958962
CompletePresent(present);
@@ -1353,6 +1357,12 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
13531357
assert(!mFilteredEvents); // Assert that filtering is working if expected
13541358
}
13551359

1360+
1361+
void PMTraceConsumer::HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord)
1362+
{
1363+
mNvTraceConsumer.HandleNvidiaDisplayDriverEvent(pEventRecord, this);
1364+
}
1365+
13561366
void PMTraceConsumer::HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord)
13571367
{
13581368
using namespace Microsoft_Windows_DxgKrnl::Win7;

PresentData/PresentMonTraceConsumer.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (C) 2017-2024 Intel Corporation
2+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
23
// SPDX-License-Identifier: MIT
34
#pragma once
45

@@ -24,6 +25,7 @@
2425
#include "Debug.hpp"
2526
#include "GpuTrace.hpp"
2627
#include "TraceConsumer.hpp"
28+
#include "NvidiaTraceConsumer.hpp"
2729
#include "../IntelPresentMon/CommonUtilities/Hash.h"
2830

2931
// PresentMode represents the different paths a present can take on windows.
@@ -271,6 +273,9 @@ struct PresentEvent {
271273
// until a PresentFrameType_Info event with a different FrameId).
272274
bool WaitingForFrameId;
273275

276+
// Data from NV DisplayDriver event
277+
uint64_t FlipDelay = 0;
278+
uint32_t FlipToken = 0;
274279

275280
PresentEvent();
276281
PresentEvent(uint32_t fid);
@@ -487,6 +492,8 @@ struct PMTraceConsumer
487492

488493
std::unordered_map<uint32_t, InputData> mRetrievedInput; // ProcessID -> InputData<InputTime, InputType, isMouseClick>
489494

495+
// Trace consumer that handles events coming from Nvidia DisplayDriver
496+
NVTraceConsumer mNvTraceConsumer;
490497

491498
// -------------------------------------------------------------------------------------------
492499
// Functions for decoding ETW and analysing process and present events.
@@ -516,6 +523,7 @@ struct PMTraceConsumer
516523
void HandleDWMEvent(EVENT_RECORD* pEventRecord);
517524
void HandleMetadataEvent(EVENT_RECORD* pEventRecord);
518525
void HandleIntelPresentMonEvent(EVENT_RECORD* pEventRecord);
526+
void HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord);
519527

520528
void HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord);
521529
void HandleWin7DxgkFlip(EVENT_RECORD* pEventRecord);

PresentData/PresentMonTraceSession.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Copyright (C) 2017-2024 Intel Corporation
2+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
23
// SPDX-License-Identifier: MIT
34

45
#include "Debug.hpp"
56
#include "PresentMonTraceConsumer.hpp"
67
#include "PresentMonTraceSession.hpp"
8+
#include "NvidiaTraceConsumer.hpp"
79

810
#include "ETW/Microsoft_Windows_D3D9.h"
911
#include "ETW/Microsoft_Windows_Dwm_Core.h"
@@ -16,6 +18,7 @@
1618
#include "ETW/Microsoft_Windows_Win32k.h"
1719
#include "ETW/NT_Process.h"
1820
#include "ETW/Intel_PresentMon.h"
21+
#include "ETW/NV_DD.h"
1922

2023
namespace {
2124

@@ -280,6 +283,7 @@ void DisableProviders(TRACEHANDLE sessionHandle)
280283
status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_DxgKrnl::Win7::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr);
281284
status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Kernel_Process::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr);
282285
status = EnableTraceEx2(sessionHandle, &Microsoft_Windows_Win32k::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr);
286+
status = EnableTraceEx2(sessionHandle, &NvidiaDisplayDriver_Events::GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr);
283287
}
284288

285289
template<
@@ -363,6 +367,10 @@ void CALLBACK EventRecordCallback(EVENT_RECORD* pEventRecord)
363367
session->mPMConsumer->HandleWin7DxgkMMIOFlip(pEventRecord);
364368
return;
365369
}
370+
if (hdr.ProviderId == NvidiaDisplayDriver_Events::GUID) {
371+
session->mPMConsumer->HandleNvidiaDisplayDriverEvent(pEventRecord);
372+
return;
373+
}
366374
}
367375

368376
if constexpr (TRACK_PRESENTMON) {
@@ -777,6 +785,13 @@ ULONG EnableProvidersListing(
777785
if (status != ERROR_SUCCESS) return status;
778786
}
779787

788+
// Nvidia_DisplayDriver
789+
//
790+
provider.ClearFilter();
791+
provider.AddEvent<NvidiaDisplayDriver_Events::FlipRequest>();
792+
status = provider.Enable(sessionHandle, NvidiaDisplayDriver_Events::GUID);
793+
if (status != ERROR_SUCCESS) return status;
794+
780795
return ERROR_SUCCESS;
781796
}
782797

PresentData/PresentMonTraceSession.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (C) 2017-2024 Intel Corporation
2+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
23
// SPDX-License-Identifier: MIT
34

45
struct PMTraceConsumer;

PresentMon/CsvOutput.cpp

+17-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (C) 2017-2024 Intel Corporation
2+
// Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved
23
// SPDX-License-Identifier: MIT
34

45
#include "PresentMon.hpp"
@@ -156,7 +157,8 @@ void WriteCsvHeader<FrameMetrics1>(FILE* fp)
156157
L",PresentMode"
157158
L",msUntilRenderComplete"
158159
L",msUntilDisplayed"
159-
L",msBetweenDisplayChange");
160+
L",msBetweenDisplayChange"
161+
L",msFlipDelay");
160162
}
161163
if (args.mTrackGPU) {
162164
fwprintf(fp, L",msUntilRenderStart"
@@ -221,11 +223,12 @@ void WriteCsvRow<FrameMetrics1>(
221223
fwprintf(fp, L",%.*lf,%.*lf", DBL_DIG - 1, metrics.msInPresentApi,
222224
DBL_DIG - 1, metrics.msBetweenPresents);
223225
if (args.mTrackDisplay) {
224-
fwprintf(fp, L",%d,%hs,%.*lf,%.*lf,%.*lf", p.SupportsTearing,
225-
PresentModeToString(p.PresentMode),
226-
DBL_DIG - 1, metrics.msUntilRenderComplete,
227-
DBL_DIG - 1, metrics.msUntilDisplayed,
228-
DBL_DIG - 1, metrics.msBetweenDisplayChange);
226+
fwprintf(fp, L",%d,%hs,%.*lf,%.*lf,%.*lf,%.*lf", p.SupportsTearing,
227+
PresentModeToString(p.PresentMode),
228+
DBL_DIG - 1, metrics.msUntilRenderComplete,
229+
DBL_DIG - 1, metrics.msUntilDisplayed,
230+
DBL_DIG - 1, metrics.msBetweenDisplayChange,
231+
DBL_DIG - 1, metrics.msFlipDelay);
229232
}
230233
if (args.mTrackGPU) {
231234
fwprintf(fp, L",%.*lf,%.*lf", DBL_DIG - 1, metrics.msUntilRenderStart,
@@ -306,7 +309,8 @@ void WriteCsvHeader<FrameMetrics>(FILE* fp)
306309
fwprintf(fp, L",DisplayLatency"
307310
L",DisplayedTime"
308311
L",AnimationError"
309-
L",AnimationTime");
312+
L",AnimationTime"
313+
L",FlipDelay");
310314
}
311315
if (args.mTrackInput) {
312316
fwprintf(fp, L",AllInputToPhotonLatency");
@@ -397,12 +401,13 @@ void WriteCsvRow<FrameMetrics>(
397401
}
398402
if (args.mTrackDisplay) {
399403
if (metrics.mDisplayedTime == 0.0) {
400-
fwprintf(fp, L",NA,NA,NA,NA");
404+
fwprintf(fp, L",NA,NA,NA,NA,NA");
401405
} else {
402-
fwprintf(fp, L",%.4lf,%.4lf,%.4lf,%.4lf", metrics.mDisplayLatency,
403-
metrics.mDisplayedTime,
404-
metrics.mAnimationError,
405-
metrics.mAnimationTime);
406+
fwprintf(fp, L",%.4lf,%.4lf,%.4lf,%.4lf,%.4lf", metrics.mDisplayLatency,
407+
metrics.mDisplayedTime,
408+
metrics.mAnimationError,
409+
metrics.mAnimationTime,
410+
metrics.mFlipDelay);
406411
}
407412
}
408413
if (args.mTrackInput) {

0 commit comments

Comments
 (0)