From daa5cd703e0e00a1109c83d0660433dbdb135fe0 Mon Sep 17 00:00:00 2001 From: "Galvan, Mark" Date: Fri, 13 Mar 2026 09:29:54 -0700 Subject: [PATCH] Adding output of display metadata --- .../mc/MetricsCalculatorDisplay.cpp | 12 ++++++++++- .../CommonUtilities/mc/MetricsTypes.cpp | 9 ++++++++ .../CommonUtilities/mc/MetricsTypes.h | 9 ++++++-- PresentData/PresentMonTraceConsumer.cpp | 7 +++++-- PresentData/PresentMonTraceConsumer.hpp | 3 ++- PresentMon/CommandLine.cpp | 14 +++++++++++-- PresentMon/CsvOutput.cpp | 21 ++++++++++++++++++- PresentMon/PresentMon.hpp | 3 ++- README-ConsoleApplication.md | 7 ++++++- 9 files changed, 74 insertions(+), 11 deletions(-) diff --git a/IntelPresentMon/CommonUtilities/mc/MetricsCalculatorDisplay.cpp b/IntelPresentMon/CommonUtilities/mc/MetricsCalculatorDisplay.cpp index 268a8282..313b6f46 100644 --- a/IntelPresentMon/CommonUtilities/mc/MetricsCalculatorDisplay.cpp +++ b/IntelPresentMon/CommonUtilities/mc/MetricsCalculatorDisplay.cpp @@ -87,7 +87,15 @@ namespace pmon::util::metrics return std::nullopt; } - + std::pair GetDisplayId(const FrameData& present) + { + auto vidPnSourceId = uint32_t(present.vidPnLayerId >> 32); // vidPnSourceId + auto layerIndex = uint32_t(present.vidPnLayerId & 0xFFFFFFFF); // layerIndex + if (vidPnSourceId != 0 || layerIndex != 0) { + return {vidPnSourceId, layerIndex}; + } + return {0, 0}; + } } // ---- NV collapsed/runt correction ---- @@ -202,5 +210,7 @@ namespace pmon::util::metrics metrics.msReadyTimeToDisplayLatency = ComputeMsReadyTimeToDisplayLatency(qpc, present, isDisplayed, screenTime); metrics.isDroppedFrame = !isDisplayed; metrics.screenTimeQpc = screenTime; + metrics.displayId = GetDisplayId(present); + metrics.presentId = present.presentId; } } diff --git a/IntelPresentMon/CommonUtilities/mc/MetricsTypes.cpp b/IntelPresentMon/CommonUtilities/mc/MetricsTypes.cpp index 9d0339e4..1de7d535 100644 --- a/IntelPresentMon/CommonUtilities/mc/MetricsTypes.cpp +++ b/IntelPresentMon/CommonUtilities/mc/MetricsTypes.cpp @@ -21,6 +21,7 @@ namespace pmon::util::metrics { frame.runtime = p.Runtime; frame.presentMode = p.PresentMode; + frame.presentStartTime = p.PresentStartTime; frame.readyTime = p.ReadyTime; frame.timeInPresent = p.TimeInPresent; @@ -67,6 +68,14 @@ namespace pmon::util::metrics { frame.appFrameId = p.AppFrameId; frame.pclFrameId = p.PclFrameId; + // Extract VidPnSourceId and layer index from PresentIds if available for display identification + if (!p.ReportedPresentIds.empty()) { + auto presentIdEntry = p.ReportedPresentIds.begin(); + // VidPnSourceId and Layer Index are encoded in the key + frame.vidPnLayerId = presentIdEntry->first; + frame.presentId = presentIdEntry->second; + } + return frame; } } diff --git a/IntelPresentMon/CommonUtilities/mc/MetricsTypes.h b/IntelPresentMon/CommonUtilities/mc/MetricsTypes.h index e00f1ad1..ba1ab7a5 100644 --- a/IntelPresentMon/CommonUtilities/mc/MetricsTypes.h +++ b/IntelPresentMon/CommonUtilities/mc/MetricsTypes.h @@ -77,6 +77,9 @@ namespace pmon::util::metrics { int32_t syncInterval = 0; uint32_t presentFlags = 0; + uint64_t vidPnLayerId = 0; + uint64_t presentId = 0; + // Metadata PresentResult finalState = {}; bool supportsTearing = 0; @@ -113,8 +116,10 @@ namespace pmon::util::metrics { double msUntilDisplayed = 0; double msBetweenDisplayChange = 0; uint64_t screenTimeQpc = 0; - std::optional msReadyTimeToDisplayLatency; + std::optional msReadyTimeToDisplayLatency = {}; bool isDroppedFrame = false; + std::pair displayId = {}; // {vidPnSourceId, layerIndex} + uint64_t presentId = 0; // CPU Metrics (app frames only) double msCPUBusy = 0; @@ -131,7 +136,7 @@ namespace pmon::util::metrics { // Input Latency (optional, app+displayed only) std::optional msClickToPhotonLatency = {}; std::optional msAllInputPhotonLatency = {}; - std::optional msInstrumentedInputTime; + std::optional msInstrumentedInputTime = {}; // Animation (optional, app+displayed only) std::optional msAnimationError = {}; diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index a48db047..92cf8904 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT @@ -1394,7 +1394,10 @@ void PMTraceConsumer::HandleDXGKEvent(EVENT_RECORD* pEventRecord) // the present ids to it. if (present != nullptr) { VerboseTraceBeforeModifyingPresent(present.get()); - present->PresentIds.emplace(vidPnLayerId, PresentId[i]); + auto inserted = present->PresentIds.emplace(vidPnLayerId, PresentId[i]); + if (inserted.second) { + present->ReportedPresentIds.push_back({ vidPnLayerId, PresentId[i] }); + } mPresentByVidPnLayerId.emplace(vidPnLayerId, present); } diff --git a/PresentData/PresentMonTraceConsumer.hpp b/PresentData/PresentMonTraceConsumer.hpp index cf547613..ee594d41 100644 --- a/PresentData/PresentMonTraceConsumer.hpp +++ b/PresentData/PresentMonTraceConsumer.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT #pragma once @@ -220,6 +220,7 @@ struct PresentEvent { uint32_t QueueSubmitSequence; // mPresentBySubmitSequence uint32_t RingIndex; // mTrackedPresents and mCompletedPresents std::unordered_map PresentIds; // mPresentByVidPnLayerId + std::vector> ReportedPresentIds; // Note: the following index tracking structures as well but are defined elsewhere: // ProcessId -> mOrderedPresentsByProcessId // ThreadId, DriverThreadId -> mPresentByThreadId diff --git a/PresentMon/CommandLine.cpp b/PresentMon/CommandLine.cpp index 8bb10dbc..7b07408f 100644 --- a/PresentMon/CommandLine.cpp +++ b/PresentMon/CommandLine.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // SPDX-License-Identifier: MIT #include @@ -275,6 +275,7 @@ void PrintUsage() LR"(--exclude_dropped)", LR"(Exclude frames that were not displayed to the screen from the CSV output.)", LR"(--v1_metrics)", LR"(Output a CSV using PresentMon 1.x metrics.)", LR"(--v2_metrics)", LR"(Output a CSV using PresentMon 2.x metrics.)", + LR"(--write_display_metadata)", LR"(Write VidPnSourceId, LayerIndex, and PresentId columns to CSV output.)", LR"(--Recording Options)", nullptr, LR"(--hotkey key)", LR"(Use the specified key press to start and stop recording. 'key' is of the form MODIFIER+KEY, e.g., "ALT+SHIFT+F11".)", @@ -408,6 +409,7 @@ bool ParseCommandLine(int argc, wchar_t** argv) args->mUseV1Metrics = false; args->mUseV2Metrics = false; args->mStopExistingSession = false; + args->mWriteDisplayMetadata = false; args->mWriteFrameId = false; args->mWriteDisplayTime = false; args->mDisableOfflineBackpressure = false; @@ -446,6 +448,7 @@ bool ParseCommandLine(int argc, wchar_t** argv) else if (ParseArg(argv[i], L"exclude_dropped")) { args->mExcludeDropped = true; continue; } else if (ParseArg(argv[i], L"v1_metrics")) { args->mUseV1Metrics = true; continue; } else if (ParseArg(argv[i], L"v2_metrics")) { args->mUseV2Metrics = true; continue; } + else if (ParseArg(argv[i], L"write_display_metadata")) { args->mWriteDisplayMetadata = true; continue; } // Recording options: else if (ParseArg(argv[i], L"hotkey")) { if (ParseValue(argv, argc, &i) && AssignHotkey(argv[i], args)) continue; } @@ -536,13 +539,14 @@ bool ParseCommandLine(int argc, wchar_t** argv) } // Ignore CSV-only options when --no_csv is used - if (csvOutputNone && (qpcTime || qpcmsTime || dtTime || args->mMultiCsv || args->mHotkeySupport)) { + if (csvOutputNone && (qpcTime || qpcmsTime || dtTime || args->mMultiCsv || args->mHotkeySupport || args->mWriteDisplayMetadata)) { PrintWarning(L"warning: ignoring CSV-related options due to --no_csv:"); if (qpcTime) { qpcTime = false; PrintWarning(L" --qpc_time"); } if (qpcmsTime) { qpcmsTime = false; PrintWarning(L" --qpc_time_ms"); } if (dtTime) { dtTime = false; PrintWarning(L" --date_time"); } if (args->mMultiCsv) { args->mMultiCsv = false; PrintWarning(L" --multi_csv"); } if (args->mHotkeySupport) { args->mHotkeySupport = false; PrintWarning(L" --hotkey"); } + if (args->mWriteDisplayMetadata) { args->mWriteDisplayMetadata = false; PrintWarning(L" --write_display_metadata"); } PrintWarning(L"\n"); } @@ -590,6 +594,12 @@ bool ParseCommandLine(int argc, wchar_t** argv) PrintWarning(L"warning: ignoring --v1_metrics due to --v2_metrics.\n"); args->mUseV1Metrics = false; } + + if (args->mUseV1Metrics && args->mWriteDisplayMetadata) { + PrintWarning(L"warning: ignoring --write_display_metadata due to --v1_metrics.\n"); + args->mWriteDisplayMetadata = false; + } + // Enable verbose trace if requested, and disable Full or Simple console output #if PRESENTMON_ENABLE_DEBUG_TRACE if (verboseTrace) { diff --git a/PresentMon/CsvOutput.cpp b/PresentMon/CsvOutput.cpp index ccbbc383..dd531a02 100644 --- a/PresentMon/CsvOutput.cpp +++ b/PresentMon/CsvOutput.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT @@ -519,6 +519,10 @@ void WriteCsvHeader(FILE* fp) L",PresentRuntime" L",SyncInterval" L",PresentFlags"); + if (args.mWriteDisplayMetadata) { + fwprintf(fp, L",VidPnSourceId" + L",LayerIndex"); + } if (args.mTrackDisplay) { fwprintf(fp, L",AllowsTearing" L",PresentMode"); @@ -629,6 +633,11 @@ void WriteCsvHeader(FILE* fp) } if (args.mWriteFrameId) { fwprintf(fp, L",FrameId"); + } + if (args.mWriteDisplayMetadata) { + fwprintf(fp, L",PresentId"); + } + if (args.mWriteFrameId) { if (args.mTrackAppTiming) { fwprintf(fp, L",AppFrameId"); } @@ -1098,6 +1107,11 @@ void WriteCsvRow( RuntimeToString(p.runtime), p.syncInterval, p.presentFlags); + if (args.mWriteDisplayMetadata) { + fwprintf(fp, L",%u,%u", + metrics.displayId.first, + metrics.displayId.second); + } if (args.mTrackDisplay) { fwprintf(fp, L",%d,%hs", p.supportsTearing, PresentModeToString(p.presentMode)); @@ -1290,6 +1304,11 @@ void WriteCsvRow( } if (args.mWriteFrameId) { fwprintf(fp, L",%u", p.frameId); + } + if (args.mWriteDisplayMetadata) { + fwprintf(fp, L",%llu", metrics.presentId); + } + if (args.mWriteFrameId) { if (args.mTrackAppTiming) { fwprintf(fp, L",%u", p.appFrameId); } diff --git a/PresentMon/PresentMon.hpp b/PresentMon/PresentMon.hpp index e581c274..9b15d936 100644 --- a/PresentMon/PresentMon.hpp +++ b/PresentMon/PresentMon.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2024 Intel Corporation +// Copyright (C) 2017-2024 Intel Corporation // Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved // SPDX-License-Identifier: MIT @@ -107,6 +107,7 @@ struct CommandLineArgs { bool mUseV1Metrics; bool mUseV2Metrics; bool mStopExistingSession; + bool mWriteDisplayMetadata; bool mWriteFrameId; bool mWriteDisplayTime; bool mDisableOfflineBackpressure; diff --git a/README-ConsoleApplication.md b/README-ConsoleApplication.md index e0b799bc..0dada5af 100644 --- a/README-ConsoleApplication.md +++ b/README-ConsoleApplication.md @@ -1,4 +1,4 @@ -# PresentMon Console Application +# PresentMon Console Application The PresentMon/ directory contains source for a standalone console application that uses the *PresentMon SDK* to capture and analyze graphics applications, outputting data to the console and/or @@ -29,6 +29,7 @@ A binary of the console application is provided in the release, e.g.: | `--exclude_dropped` | Exclude frames that were not displayed to the screen from the CSV output. | | `--v1_metrics` | Output a CSV using PresentMon 1.x metrics. | | `--v2_metrics` | Output a CSV using PresentMon 2.x metrics. | +| `--write_display_metadata` | Write VidPnSourceId, LayerIndex, and PresentId columns to CSV output. | | Recording Options | | | ------------------------------ | --- | @@ -85,6 +86,8 @@ Default metrics include: | *Application* | The name of the process that generated the frame. | | *ProcessID* | The process ID of the process that generated the frame. | | *SwapChainAddress* | The address of the swap chain used to present the frame. | +| *VidPnSourceId* | The display source identifier associated with the reported present. This column is written when `--write_display_metadata` is used. | +| *LayerIndex* | The display layer index associated with the reported present. This column is written when `--write_display_metadata` is used. | | *PresentRuntime* | The API used to present the frame. | | *SyncInterval* | The sync interval provided by the application when presenting the frame. Note: this value may be modified later by the driver, e.g., based on control panel overrides. | | *PresentFlags* | The present flags provided by the application when presenting the frame. | @@ -114,6 +117,7 @@ Default metrics include: | *MsBetweenSimulationStart* | The time between the start of simulation processing of the previous frame and this one, in milliseconds. | | *MsPCLatency* | Time between PC receiving input and frame being sent to the display, in milliseconds. | | *MsBetweenAppStart* | How long it took from the start of this frame until the CPU started working on the next frame, in milliseconds. | +| *PresentId* | The present identifier associated with the reported VidPnSourceId and LayerIndex pair. This column is written when `--write_display_metadata` is used. | Some metrics are enabled or disabled depending on the command line options: @@ -121,6 +125,7 @@ Some metrics are enabled or disabled depending on the command line options: | -------------------- | --------------- | ---------------- | | `--track_frame_type` | *FrameType* | | | `--track_gpu_video` | *VideoBusy* | | +| `--write_display_metadata` | *VidPnSourceId
LayerIndex
PresentId* | | | `--no_track_gpu` | | *GPULatency
GPUBusy
GPUWait
VideoBusy* | | `--no_track_input` | | *ClickToPhotonLatency* | | `--no_track_display`
(requires `--no_track_gpu` and `--no_track_input` as well) | | *AllowsTearing
PresentMode
DisplayLatency
DisplayedTime* |