Skip to content

Commit 1b5215d

Browse files
Merge pull request #440 from HugoNVDA/DLSS4_FLIP-METERING_SUPPORT
Add support of NVIDIA DLSS4 flip-metering
2 parents 7e03a46 + deff345 commit 1b5215d

16 files changed

+311
-9
lines changed

PresentData/ETW/NV_DD.h

Lines changed: 28 additions & 0 deletions
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

Lines changed: 79 additions & 0 deletions
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 flipRequest;
59+
flipRequest.FlipDelay = delay;
60+
flipRequest.FlipToken = token;
61+
mNvFlipRequestByThreadId.emplace(hdr.ThreadId, flipRequest);
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

Lines changed: 37 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,9 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
964964
auto present = FindPresentBySubmitSequence(submitSequence);
965965
if (present != nullptr) {
966966

967+
// Apply Nvidia FlipDelay, if any, to the presentEvent
968+
mNvTraceConsumer.ApplyFlipDelay(present.get(), hdr.ThreadId);
969+
967970
// Complete the GPU tracking for this frame.
968971
//
969972
// For some present modes (e.g., Hardware_Legacy_Flip) this may be
@@ -986,7 +989,7 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
986989
// this the present screen time.
987990
if (FlipEntryStatusAfterFlip != (uint32_t) Microsoft_Windows_DxgKrnl::FlipEntryStatus::FlipWaitHSync) {
988991

989-
SetScreenTime(present, hdr.TimeStamp.QuadPart);
992+
SetScreenTime(present, hdr.TimeStamp.QuadPart + present->FlipDelay);
990993

991994
if (present->PresentMode == PresentMode::Hardware_Legacy_Flip) {
992995
CompletePresent(present);
@@ -1387,6 +1390,12 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord)
13871390
assert(!mFilteredEvents); // Assert that filtering is working if expected
13881391
}
13891392

1393+
1394+
void PMTraceConsumer::HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord)
1395+
{
1396+
mNvTraceConsumer.HandleNvidiaDisplayDriverEvent(pEventRecord, this);
1397+
}
1398+
13901399
void PMTraceConsumer::HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord)
13911400
{
13921401
using namespace Microsoft_Windows_DxgKrnl::Win7;

PresentData/PresentMonTraceConsumer.hpp

Lines changed: 8 additions & 0 deletions
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.
@@ -285,6 +287,9 @@ struct PresentEvent {
285287
// until a PresentFrameType_Info event with a different FrameId).
286288
bool WaitingForFrameId;
287289

290+
// Data from NV DisplayDriver event
291+
uint64_t FlipDelay = 0;
292+
uint32_t FlipToken = 0;
288293

289294
PresentEvent();
290295
PresentEvent(uint32_t fid);
@@ -504,6 +509,8 @@ struct PMTraceConsumer
504509

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

512+
// Trace consumer that handles events coming from Nvidia DisplayDriver
513+
NVTraceConsumer mNvTraceConsumer;
507514

508515
// -------------------------------------------------------------------------------------------
509516
// Functions for decoding ETW and analysing process and present events.
@@ -534,6 +541,7 @@ struct PMTraceConsumer
534541
void HandleDWMEvent(EVENT_RECORD* pEventRecord);
535542
void HandleMetadataEvent(EVENT_RECORD* pEventRecord);
536543
void HandleIntelPresentMonEvent(EVENT_RECORD* pEventRecord);
544+
void HandleNvidiaDisplayDriverEvent(EVENT_RECORD* pEventRecord);
537545

538546
void HandleWin7DxgkBlt(EVENT_RECORD* pEventRecord);
539547
void HandleWin7DxgkFlip(EVENT_RECORD* pEventRecord);

PresentData/PresentMonTraceSession.cpp

Lines changed: 15 additions & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 18 additions & 8 deletions
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,
@@ -333,7 +336,8 @@ void WriteCsvHeader<FrameMetrics>(FILE* fp)
333336
L",DisplayLatency"
334337
L",DisplayedTime"
335338
L",AnimationError"
336-
L",AnimationTime");
339+
L",AnimationTime"
340+
L",FlipDelay");
337341
}
338342
if (args.mTrackInput) {
339343
fwprintf(fp, L",AllInputToPhotonLatency");
@@ -364,7 +368,8 @@ void WriteCsvHeader<FrameMetrics>(FILE* fp)
364368
}
365369
if (args.mTrackDisplay) {
366370
fwprintf(fp, L",MsAnimationError"
367-
L",AnimationTime");
371+
L",AnimationTime"
372+
L",FlipDelay");
368373
}
369374
if (args.mTrackInput) {
370375
fwprintf(fp, L",MsAllInputToPhotonLatency");
@@ -542,6 +547,11 @@ void WriteCsvRow<FrameMetrics>(
542547
else {
543548
fwprintf(fp, L",NA");
544549
}
550+
if (metrics.mMsFlipDelay == 0.0) {
551+
fwprintf(fp, L",NA");
552+
} else {
553+
fwprintf(fp, L",%.4lf", metrics.mMsFlipDelay);
554+
}
545555
}
546556
if (args.mTrackInput) {
547557
if (metrics.mMsAllInputPhotonLatency == 0.0) {

0 commit comments

Comments
 (0)