Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding hybrid present tracking to PresentMon console application #415

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions IntelPresentMon/Streamer/Streamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,7 @@ void Streamer::ProcessPresentEvent(
gpu_telemetry_cap_bits,
std::bitset<static_cast<size_t>(CpuTelemetryCapBits::cpu_telemetry_count)>
cpu_telemetry_cap_bits) {
uint32_t process_id;
if (stream_mode_ == StreamMode::kOfflineEtl) {
process_id = static_cast<uint32_t>(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.
Expand Down
4 changes: 4 additions & 0 deletions PresentData/Debug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
10 changes: 9 additions & 1 deletion PresentData/ETW/Microsoft_Windows_DXGI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand All @@ -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,
};

}
34 changes: 34 additions & 0 deletions PresentData/PresentMonTraceConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ PresentEvent::PresentEvent()
, IsCompleted(false)
, IsLost(false)
, PresentFailed(false)
, IsHybridPresent(false)
, PresentInDwmWaitingStruct(false)

, WaitingForPresentStop(false)
Expand Down Expand Up @@ -292,6 +293,27 @@ void PMTraceConsumer::HandleDXGIEvent(EVENT_RECORD* pEventRecord)
RuntimePresentStop(Runtime::DXGI, hdr, mMetadata.GetEventData<uint32_t>(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<uint64_t>();
auto hybridPresentMode = desc[1].GetData<uint32_t>();
auto key = std::make_pair(hdr.ProcessId, pSwapChain);
mHybridPresentModeBySwapChainPid[key] = hybridPresentMode;
}
}
}
break;
default:
assert(!mFilteredEvents); // Assert that filtering is working if expected
break;
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 16 additions & 5 deletions PresentData/PresentMonTraceConsumer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -423,12 +426,15 @@ struct PMTraceConsumer
std::size_t operator()(Win32KPresentHistoryToken const& v) const noexcept;
};

// Custom hash function for std::pair<uint32_t, uint32_t>
template <typename T, typename U>
struct PairHash {
std::size_t operator()(const std::pair<uint32_t, uint32_t>& p) const noexcept {
return std::hash<uint32_t>{}(p.first) ^ (std::hash<uint32_t>{}(p.second) << 1);
size_t operator()(const std::pair<T, U>& p) const noexcept
{
using namespace pmon::util::hash;
return DualHash(p.first, p.second);
}
};

std::unordered_map<uint32_t, std::shared_ptr<PresentEvent>> mPresentByThreadId; // ThreadId -> PresentEvent
std::unordered_map<uint32_t, OrderedPresents> mOrderedPresentsByProcessId; // ProcessId -> ordered PresentStartTime -> PresentEvent
std::unordered_map<uint32_t, std::unordered_map<uint64_t, std::shared_ptr<PresentEvent>>>
Expand All @@ -442,10 +448,15 @@ struct PMTraceConsumer
std::unordered_map<uint64_t, std::shared_ptr<PresentEvent>> mLastPresentByWindow; // HWND -> PresentEvent
std::unordered_map<uint64_t, MouseClickData> mReceivedMouseClickByHwnd; // HWND -> MouseClickData
std::unordered_map<std::pair<uint32_t, uint32_t>,
std::shared_ptr<PresentEvent>, PairHash> mPresentByAppFrameId; // Intel provider app frame id -> PresentEvent
std::shared_ptr<PresentEvent>,
PairHash<uint32_t, uint32_t>> mPresentByAppFrameId; // Intel provider app frame id -> PresentEvent
std::unordered_map<uint32_t, uint32_t> mNextAppFrameIdByProcessid; // ProcessId -> Next Intel provider app frame id
std::unordered_map<std::pair<uint32_t, uint32_t>,
AppTimingData, PairHash> mPendingAppTimingDataByAppFrameId; // Intel provider app frame id -> AppTimingData
AppTimingData,
PairHash<uint32_t, uint32_t>> mPendingAppTimingDataByAppFrameId; // Intel provider app frame id -> AppTimingData
std::unordered_map<std::pair<uint32_t, uint64_t>,
uint32_t,
PairHash<uint32_t, uint64_t>> mHybridPresentModeBySwapChainPid; // SwapChain and process id -> HybridPresentMode

// mGpuTrace tracks work executed on the GPU.
GpuTrace mGpuTrace;
Expand Down
4 changes: 4 additions & 0 deletions PresentData/PresentMonTraceSession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,10 @@ ULONG EnableProvidersListing(
provider.AddEvent<Microsoft_Windows_DXGI::Present_Stop>();
provider.AddEvent<Microsoft_Windows_DXGI::PresentMultiplaneOverlay_Start>();
provider.AddEvent<Microsoft_Windows_DXGI::PresentMultiplaneOverlay_Stop>();
if (pmConsumer->mTrackHybridPresent) {
provider.AddEvent<Microsoft_Windows_DXGI::SwapChain_Start>();
provider.AddEvent<Microsoft_Windows_DXGI::ResizeBuffers_Start>();
}
status = provider.Enable(sessionHandle, Microsoft_Windows_DXGI::GUID);
if (status != ERROR_SUCCESS) return status;

Expand Down
25 changes: 21 additions & 4 deletions PresentData/TraceConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -207,7 +225,7 @@ void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc,
ii = metadata_.emplace(key, std::vector<uint8_t>(sizeof(TRACE_EVENT_INFO), 0)).first;
assert(false);
}
}
}

auto tei = (TRACE_EVENT_INFO*) ii->second.data();

Expand Down Expand Up @@ -235,7 +253,7 @@ void EventMetadata::GetEventData(EVENT_RECORD* eventRecord, EventDataDesc* desc,

foundCount += 1;
if (foundCount == descCount) {
return;
return foundCount;
}
}
}
Expand All @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions PresentData/TraceConsumer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,15 @@ 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<typename T> T GetEventData(EVENT_RECORD* eventRecord, wchar_t const* name)
{
EventDataDesc desc = { name };
GetEventData(eventRecord, &desc, 1);
return desc.GetData<T>();
}

private:
uint32_t GetEventDataWithCount(EVENT_RECORD* eventRecord, EventDataDesc* desc, uint32_t descCount);
};
7 changes: 5 additions & 2 deletions PresentMon/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions PresentMon/CsvOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,9 @@ void WriteCsvHeader<FrameMetrics>(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;
Expand Down Expand Up @@ -351,6 +354,10 @@ void WriteCsvRow<FrameMetrics>(
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));
Expand Down
1 change: 1 addition & 0 deletions PresentMon/MainThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions PresentMon/PresentMon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct CommandLineArgs {
bool mTrackFrameType;
bool mTrackPMMeasurements;
bool mTrackAppTiming;
bool mTrackHybridPresent;
bool mScrollLockIndicator;
bool mExcludeDropped;
bool mTerminateExistingSession;
Expand Down
4 changes: 4 additions & 0 deletions PresentMon/PresentMon.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
Expand All @@ -136,6 +137,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|x64'">stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
Expand All @@ -145,6 +147,8 @@
<ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
<ClCompile>
<PreprocessorDefinitions>WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">stdcpp17</LanguageStandard>
<LanguageStandard Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">stdcpp17</LanguageStandard>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Platform)'=='ARM'">
Expand Down