From 26b39b81829e396b182a3fb2a1cacfad286a7ec9 Mon Sep 17 00:00:00 2001 From: markgalvan-intel Date: Mon, 24 Feb 2025 11:53:56 -0800 Subject: [PATCH 1/3] Removed check for hardcoded ETL process id. This was causing all frames to be dropped during IPM ETL testing --- IntelPresentMon/Streamer/Streamer.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/IntelPresentMon/Streamer/Streamer.cpp b/IntelPresentMon/Streamer/Streamer.cpp index 84137d6b..fb80b228 100644 --- a/IntelPresentMon/Streamer/Streamer.cpp +++ b/IntelPresentMon/Streamer/Streamer.cpp @@ -153,12 +153,7 @@ void Streamer::ProcessPresentEvent( gpu_telemetry_cap_bits, std::bitset(CpuTelemetryCapBits::cpu_telemetry_count)> cpu_telemetry_cap_bits) { - uint32_t process_id; - if (stream_mode_ == StreamMode::kOfflineEtl) { - process_id = static_cast(StreamPidOverride::kEtlPid); - } else { - process_id = present_event->ProcessId; - } + uint32_t process_id = present_event->ProcessId; // Lock the nsm mutex as stop streaming calls can occur at any time // and destroy the named shared memory during writing of frame data. From 5909760bd139856109317ee35bb01faf1f5e324e Mon Sep 17 00:00:00 2001 From: markgalvan-intel Date: Mon, 24 Feb 2025 13:06:56 -0800 Subject: [PATCH 2/3] Added a version of GetEventData that returns back the number of found descriptions to allow for events that have added new member but have not updated the version number. --- PresentData/TraceConsumer.cpp | 25 +++++++++++++++++++++---- PresentData/TraceConsumer.hpp | 4 ++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/PresentData/TraceConsumer.cpp b/PresentData/TraceConsumer.cpp index 5d89fae2..737c5c46 100644 --- a/PresentData/TraceConsumer.cpp +++ b/PresentData/TraceConsumer.cpp @@ -187,6 +187,24 @@ void EventMetadata::AddMetadata(EVENT_RECORD* eventRecord) // If the metadata isn't found look it up using TDH. Then, look up each // property in the metadata to obtain it's data pointer and size. void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t descCount, uint32_t optionalCount /*=0*/) +{ + [[maybe_unused]] auto foundCount = GetEventDataWithCount(eventRecord, desc, descCount); + assert(foundCount >= descCount - optionalCount); + (void)optionalCount; +} + +// Some events have been evolving over time but not incrementing the version number. As an example DXGI::SwapChain::Start. +// Use this version of GetEventData when a data param might be available based on the version of the event +// being processed. Be sure to check the returned event cound to ensure the expected number of descriptions have +// been found. +void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t* descCount) +{ + auto foundCount = GetEventDataWithCount(eventRecord, desc, *descCount); + *descCount = foundCount; + return; +} + +uint32_t EventMetadata::GetEventDataWithCount(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t descCount) { // Look up stored metadata. If not found, look up metadata using TDH and // cache it for future events. @@ -207,7 +225,7 @@ void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, ii = metadata_.emplace(key, std::vector(sizeof(TRACE_EVENT_INFO), 0)).first; assert(false); } - } +} auto tei = (TRACE_EVENT_INFO*) ii->second.data(); @@ -235,7 +253,7 @@ void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, foundCount += 1; if (foundCount == descCount) { - return; + return foundCount; } } } @@ -244,8 +262,7 @@ void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, offset += info.size_ * info.count_; } - assert(foundCount >= descCount - optionalCount); - (void) optionalCount; + return foundCount; } namespace { diff --git a/PresentData/TraceConsumer.hpp b/PresentData/TraceConsumer.hpp index a91b277e..3206c602 100644 --- a/PresentData/TraceConsumer.hpp +++ b/PresentData/TraceConsumer.hpp @@ -71,6 +71,7 @@ struct EventMetadata { void AddMetadata(EVENT_RECORD* eventRecord); void GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t descCount, uint32_t optionalCount=0); + void GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t* descCount); template T GetEventData(EVENT_RECORD* eventRecord, wchar_t const* name) { @@ -78,4 +79,7 @@ struct EventMetadata { GetEventData(eventRecord, &desc, 1); return desc.GetData(); } + +private: + uint32_t GetEventDataWithCount(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t descCount); }; From fc9e317682a69416138d144ba7c9c2f283177a42 Mon Sep 17 00:00:00 2001 From: markgalvan-intel Date: Mon, 24 Feb 2025 19:11:02 -0800 Subject: [PATCH 3/3] Added in new events to track and report hybrid presents in console application. --- PresentData/Debug.cpp | 4 +++ PresentData/ETW/Microsoft_Windows_DXGI.h | 10 ++++++- PresentData/PresentMonTraceConsumer.cpp | 34 ++++++++++++++++++++++++ PresentData/PresentMonTraceConsumer.hpp | 21 +++++++++++---- PresentData/PresentMonTraceSession.cpp | 4 +++ PresentMon/CommandLine.cpp | 7 +++-- PresentMon/CsvOutput.cpp | 7 +++++ PresentMon/MainThread.cpp | 1 + PresentMon/PresentMon.hpp | 1 + PresentMon/PresentMon.vcxproj | 4 +++ 10 files changed, 85 insertions(+), 8 deletions(-) diff --git a/PresentData/Debug.cpp b/PresentData/Debug.cpp index 123f187f..82d06213 100644 --- a/PresentData/Debug.cpp +++ b/PresentData/Debug.cpp @@ -415,6 +415,10 @@ void VerboseTraceEventImpl(PMTraceConsumer* pmConsumer, EVENT_RECORD* eventRecor case Present_Stop::Id: PrintEventHeader(hdr, "DXGIPresent_Stop"); break; case PresentMultiplaneOverlay_Stop::Id: PrintEventHeader(hdr, "DXGIPresentMPO_Stop"); break; } + if (pmConsumer->mTrackHybridPresent) { + if (hdr.EventDescriptor.Id == ResizeBuffers_Start::Id) { PrintEventHeader(hdr, "DXGIResizeBuffers_Start"); } + if (hdr.EventDescriptor.Id == SwapChain_Start::Id) { PrintEventHeader(hdr, "DXGISwapChain_Start"); } + } return; } diff --git a/PresentData/ETW/Microsoft_Windows_DXGI.h b/PresentData/ETW/Microsoft_Windows_DXGI.h index 23baf934..21e41a25 100644 --- a/PresentData/ETW/Microsoft_Windows_DXGI.h +++ b/PresentData/ETW/Microsoft_Windows_DXGI.h @@ -3,7 +3,7 @@ // // This file originally generated by etw_list // version: public 1b19f39ddb669f7a700a5d0c16cf079943e996d5 -// parameters: --no_event_structs --event=Present::Start --event=Present::Stop --event=PresentMultiplaneOverlay::Start --event=PresentMultiplaneOverlay::Stop --provider=Microsoft-Windows-DXGI +// parameters: --no_event_structs --event=Present::Start --event=Present::Stop --event=PresentMultiplaneOverlay::Start --event=PresentMultiplaneOverlay::Stop --event=SwapChain::Start --event=ResizeBuffers::Start --provider=Microsoft-Windows-DXGI #pragma once namespace Microsoft_Windows_DXGI { @@ -43,6 +43,8 @@ EVENT_DESCRIPTOR_DECL(PresentMultiplaneOverlay_Start, 0x0037, 0x00, 0x10, 0x00, EVENT_DESCRIPTOR_DECL(PresentMultiplaneOverlay_Stop , 0x0038, 0x00, 0x10, 0x00, 0x02, 0x000e, 0x8000000000000002) EVENT_DESCRIPTOR_DECL(Present_Start , 0x002a, 0x00, 0x10, 0x00, 0x01, 0x0009, 0x8000000000000002) EVENT_DESCRIPTOR_DECL(Present_Stop , 0x002b, 0x00, 0x10, 0x00, 0x02, 0x0009, 0x8000000000000002) +EVENT_DESCRIPTOR_DECL(ResizeBuffers_Start , 0x002d, 0x00, 0x10, 0x00, 0x01, 0x0005, 0x8000000000000002); +EVENT_DESCRIPTOR_DECL(SwapChain_Start , 0x000a, 0x00, 0x10, 0x00, 0x01, 0x0002, 0x8000000000000001); #undef EVENT_DESCRIPTOR_DECL @@ -56,4 +58,10 @@ enum class DXGIPresentFlags : uint32_t { DXGI_PRESENT_RESTRICT_TO_OUTPUT = 64, }; +enum class HybridPresentMode : uint32_t { + NOT_HYBRID = 0, + TWO_COPY_PATH = 1, + ONE_COPY_PATH_CASO = 2, +}; + } diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index 61877087..edd5f8fa 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -151,6 +151,7 @@ PresentEvent::PresentEvent() , IsCompleted(false) , IsLost(false) , PresentFailed(false) + , IsHybridPresent(false) , PresentInDwmWaitingStruct(false) , WaitingForPresentStop(false) @@ -292,6 +293,27 @@ void PMTraceConsumer::HandleDXGIEvent(EVENT_RECORD* pEventRecord) RuntimePresentStop(Runtime::DXGI, hdr, mMetadata.GetEventData(pEventRecord, L"Result")); } break; + case Microsoft_Windows_DXGI::SwapChain_Start::Id: + case Microsoft_Windows_DXGI::ResizeBuffers_Start::Id: + if (mTrackHybridPresent) { + if (IsProcessTrackedForFiltering(hdr.ProcessId)) { + EventDataDesc desc[] = { + { L"pIDXGISwapChain" }, + { L"HybridPresentMode" }, + }; + // Check to see if the event has both the pIDXGISwapChain and HybridPresentMode + // fields. If not do not process. + uint32_t descCount = _countof(desc); + mMetadata.GetEventData(pEventRecord, desc, &descCount); + if (descCount == _countof(desc)) { + auto pSwapChain = desc[0].GetData(); + auto hybridPresentMode = desc[1].GetData(); + auto key = std::make_pair(hdr.ProcessId, pSwapChain); + mHybridPresentModeBySwapChainPid[key] = hybridPresentMode; + } + } + } + break; default: assert(!mFilteredEvents); // Assert that filtering is working if expected break; @@ -2396,6 +2418,18 @@ void PMTraceConsumer::RuntimePresentStop(Runtime runtime, EVENT_HEADER const& hd } } + if (mTrackHybridPresent) { + // Determine if this is a hybrid present + auto key = std::make_pair(present->ProcessId, present->SwapChainAddress); + auto swapIter = mHybridPresentModeBySwapChainPid.find(key); + if (swapIter != mHybridPresentModeBySwapChainPid.end()) { + Microsoft_Windows_DXGI::HybridPresentMode hybridPresentMode = (Microsoft_Windows_DXGI::HybridPresentMode)swapIter->second; + if (hybridPresentMode != Microsoft_Windows_DXGI::HybridPresentMode::NOT_HYBRID) { + present->IsHybridPresent = true; + } + } + } + // Set the runtime and Present_Stop time. VerboseTraceBeforeModifyingPresent(present.get()); present->Runtime = runtime; diff --git a/PresentData/PresentMonTraceConsumer.hpp b/PresentData/PresentMonTraceConsumer.hpp index a4e072c3..b219c59c 100644 --- a/PresentData/PresentMonTraceConsumer.hpp +++ b/PresentData/PresentMonTraceConsumer.hpp @@ -24,6 +24,7 @@ #include "Debug.hpp" #include "GpuTrace.hpp" #include "TraceConsumer.hpp" +#include "../IntelPresentMon/CommonUtilities/Hash.h" // PresentMode represents the different paths a present can take on windows. // @@ -249,6 +250,7 @@ struct PresentEvent { bool IsLost; // This PresentEvent was found in an unexpected state and analysis could not continue (potentially // due to missing a critical ETW event'. bool PresentFailed; // The Present() call failed. + bool IsHybridPresent; // This present is a hybrid present and is performing a cross adapter copy. bool PresentInDwmWaitingStruct; // Whether this PresentEvent is currently stored in // PMTraceConsumer::mPresentsWaitingForDWM @@ -293,6 +295,7 @@ struct PMTraceConsumer bool mTrackFrameType = false; // ... the frame type communicated through the Intel-PresentMon provider. bool mTrackPMMeasurements = false; // ... external measurements provided through the Intel-PresentMon provider bool mTrackAppTiming = false; // ... app timing data communicated through the Intel-PresentMon provider. + bool mTrackHybridPresent = false; // ... hybrid presents. // When PresentEvents are missing data that may still arrive later, they get put into a deferred // state until the data arrives. This time limit specifies how long a PresentEvent can be @@ -423,12 +426,15 @@ struct PMTraceConsumer std::size_t operator()(Win32KPresentHistoryToken const& v) const noexcept; }; - // Custom hash function for std::pair + template struct PairHash { - std::size_t operator()(const std::pair& p) const noexcept { - return std::hash{}(p.first) ^ (std::hash{}(p.second) << 1); + size_t operator()(const std::pair& p) const noexcept + { + using namespace pmon::util::hash; + return DualHash(p.first, p.second); } }; + std::unordered_map> mPresentByThreadId; // ThreadId -> PresentEvent std::unordered_map mOrderedPresentsByProcessId; // ProcessId -> ordered PresentStartTime -> PresentEvent std::unordered_map>> @@ -442,10 +448,15 @@ struct PMTraceConsumer std::unordered_map> mLastPresentByWindow; // HWND -> PresentEvent std::unordered_map mReceivedMouseClickByHwnd; // HWND -> MouseClickData std::unordered_map, - std::shared_ptr, PairHash> mPresentByAppFrameId; // Intel provider app frame id -> PresentEvent + std::shared_ptr, + PairHash> mPresentByAppFrameId; // Intel provider app frame id -> PresentEvent std::unordered_map mNextAppFrameIdByProcessid; // ProcessId -> Next Intel provider app frame id std::unordered_map, - AppTimingData, PairHash> mPendingAppTimingDataByAppFrameId; // Intel provider app frame id -> AppTimingData + AppTimingData, + PairHash> mPendingAppTimingDataByAppFrameId; // Intel provider app frame id -> AppTimingData + std::unordered_map, + uint32_t, + PairHash> mHybridPresentModeBySwapChainPid; // SwapChain and process id -> HybridPresentMode // mGpuTrace tracks work executed on the GPU. GpuTrace mGpuTrace; diff --git a/PresentData/PresentMonTraceSession.cpp b/PresentData/PresentMonTraceSession.cpp index 6ea132f6..d6cb49f1 100644 --- a/PresentData/PresentMonTraceSession.cpp +++ b/PresentData/PresentMonTraceSession.cpp @@ -729,6 +729,10 @@ ULONG EnableProvidersListing( provider.AddEvent(); provider.AddEvent(); provider.AddEvent(); + if (pmConsumer->mTrackHybridPresent) { + provider.AddEvent(); + provider.AddEvent(); + } status = provider.Enable(sessionHandle, Microsoft_Windows_DXGI::GUID); if (status != ERROR_SUCCESS) return status; diff --git a/PresentMon/CommandLine.cpp b/PresentMon/CommandLine.cpp index 3303f817..9db54266 100644 --- a/PresentMon/CommandLine.cpp +++ b/PresentMon/CommandLine.cpp @@ -296,7 +296,8 @@ void PrintUsage() LR"(--Beta Options)", nullptr, LR"(--track_frame_type)", LR"(Track the type of each displayed frame; requires application and/or driver instrumentation using Intel-PresentMon provider.)", LR"(--track_hw_measurements)", LR"(Tracks HW-measured latency and/or power data coming from a LMT and/or PCAT device.)", - LR"(--track_app_timing)", LR"(Track app timines for each displayed frame; requires application and/or driver instrumentation using Intel-PresentMon provider.)", + LR"(--track_app_timing)", LR"(Track app times for each displayed frame; requires application and/or driver instrumentation using Intel-PresentMon provider.)", + LR"(--track_hybrid_present)", LR"(Tracks if the present is a hybrid present and is performing a cross adapter copy.)", }; // Layout @@ -390,6 +391,7 @@ bool ParseCommandLine(int argc, wchar_t** argv) args->mTrackFrameType = false; args->mTrackPMMeasurements = false; args->mTrackAppTiming = false; + args->mTrackHybridPresent = false; args->mScrollLockIndicator = false; args->mExcludeDropped = false; args->mTerminateExistingSession = false; @@ -459,7 +461,8 @@ bool ParseCommandLine(int argc, wchar_t** argv) // Beta options: else if (ParseArg(argv[i], L"track_frame_type")) { args->mTrackFrameType = true; continue; } else if (ParseArg(argv[i], L"track_hw_measurements")) { args->mTrackPMMeasurements = true; continue; } - else if (ParseArg(argv[i], L"track_app_timing")) { args->mTrackAppTiming = true; continue; } + else if (ParseArg(argv[i], L"track_app_timing")) { args->mTrackAppTiming = true; continue; } + else if (ParseArg(argv[i], L"track_hybrid_present")) { args->mTrackHybridPresent = true; continue; } // Hidden options: #if PRESENTMON_ENABLE_DEBUG_TRACE diff --git a/PresentMon/CsvOutput.cpp b/PresentMon/CsvOutput.cpp index eec5cee4..5850e10e 100644 --- a/PresentMon/CsvOutput.cpp +++ b/PresentMon/CsvOutput.cpp @@ -281,6 +281,9 @@ void WriteCsvHeader(FILE* fp) if (args.mTrackFrameType) { fwprintf(fp, L",FrameType"); } + if (args.mTrackHybridPresent) { + fwprintf(fp, L",HybridPresent"); + } switch (args.mTimeUnit) { case TimeUnit::MilliSeconds: fwprintf(fp, L",CPUStartTime"); break; case TimeUnit::QPC: fwprintf(fp, L",CPUStartQPC"); break; @@ -351,6 +354,10 @@ void WriteCsvRow( if (args.mTrackFrameType) { fwprintf(fp, L",%hs", FrameTypeToString(metrics.mFrameType)); } + if (args.mTrackHybridPresent) { + fwprintf(fp, L",%d", p.IsHybridPresent); + } + switch (args.mTimeUnit) { case TimeUnit::MilliSeconds: fwprintf(fp, L",%.4lf", pmSession.TimestampToMilliSeconds(metrics.mCPUStart)); diff --git a/PresentMon/MainThread.cpp b/PresentMon/MainThread.cpp index c0bf7989..6fefe908 100644 --- a/PresentMon/MainThread.cpp +++ b/PresentMon/MainThread.cpp @@ -261,6 +261,7 @@ int wmain(int argc, wchar_t** argv) pmConsumer.mTrackFrameType = args.mTrackFrameType; pmConsumer.mTrackPMMeasurements = args.mTrackPMMeasurements; pmConsumer.mTrackAppTiming = args.mTrackAppTiming; + pmConsumer.mTrackHybridPresent = args.mTrackHybridPresent; pmConsumer.mDisableOfflineBackpressure = args.mDisableOfflineBackpressure; if (args.mTargetPid != 0) { pmConsumer.mFilteredProcessIds = true; diff --git a/PresentMon/PresentMon.hpp b/PresentMon/PresentMon.hpp index b9d48b3f..3d9cbcca 100644 --- a/PresentMon/PresentMon.hpp +++ b/PresentMon/PresentMon.hpp @@ -75,6 +75,7 @@ struct CommandLineArgs { bool mTrackFrameType; bool mTrackPMMeasurements; bool mTrackAppTiming; + bool mTrackHybridPresent; bool mScrollLockIndicator; bool mExcludeDropped; bool mTerminateExistingSession; diff --git a/PresentMon/PresentMon.vcxproj b/PresentMon/PresentMon.vcxproj index be1916cb..b4af26d5 100644 --- a/PresentMon/PresentMon.vcxproj +++ b/PresentMon/PresentMon.vcxproj @@ -127,6 +127,7 @@ Disabled _DEBUG;%(PreprocessorDefinitions) MultiThreadedDebug + stdcpp17 @@ -136,6 +137,7 @@ true NDEBUG;%(PreprocessorDefinitions) MultiThreaded + stdcpp17 true @@ -145,6 +147,8 @@ WIN32;%(PreprocessorDefinitions) + stdcpp17 + stdcpp17