Skip to content

Commit 8590065

Browse files
Merge pull request #319 from GameTechDev/feature/etl-backpressure
Backpressure and other options for PresentData debugging
2 parents 03274cb + 3a310ec commit 8590065

File tree

7 files changed

+80
-19
lines changed

7 files changed

+80
-19
lines changed

PresentData/PresentMonTraceConsumer.cpp

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,21 +1894,33 @@ void PMTraceConsumer::CompletePresent(std::shared_ptr<PresentEvent> const& p)
18941894
void PMTraceConsumer::AddPresentToCompletedList(std::shared_ptr<PresentEvent> const& present)
18951895
{
18961896
{
1897-
std::lock_guard<std::mutex> lock(mPresentEventMutex);
1897+
std::unique_lock<std::mutex> lock(mPresentEventMutex);
18981898

1899-
// If the completed list is full, throw away the oldest completed present, if it IsLost; or this
1900-
// present, if it IsLost; or the oldest completed present.
19011899
uint32_t index;
1900+
// if completed buffer is full
19021901
if (mCompletedCount == PRESENTEVENT_CIRCULAR_BUFFER_SIZE) {
1903-
if (!mCompletedPresents[mCompletedIndex]->IsLost && present->IsLost) {
1904-
return;
1902+
// if we are in offline ETL processing mode, block instead of overwriting events
1903+
// unless either A) the buffer is full of non-ready events or B) backpressure disabled via CLI option
1904+
if (!mIsRealtimeSession && mReadyCount != 0 && !mDisableOfflineBackpressure) {
1905+
mCompletedRingCondition.wait(lock, [this] { return mCompletedCount < PRESENTEVENT_CIRCULAR_BUFFER_SIZE; });
1906+
index = GetRingIndex(mCompletedIndex + mCompletedCount);
1907+
mCompletedCount++;
19051908
}
1909+
// Completed present overflow routine (when not blocking):
1910+
// If the completed list is full, throw away the oldest completed present, if it IsLost; or this
1911+
// present, if it IsLost; or the oldest completed present.
1912+
else {
1913+
if (!mCompletedPresents[mCompletedIndex]->IsLost && present->IsLost) {
1914+
return;
1915+
}
19061916

1907-
index = mCompletedIndex;
1908-
mCompletedIndex = GetRingIndex(mCompletedIndex + 1);
1909-
if (mReadyCount > 0) {
1910-
mReadyCount--;
1917+
index = mCompletedIndex;
1918+
mCompletedIndex = GetRingIndex(mCompletedIndex + 1);
1919+
if (mReadyCount > 0) {
1920+
mReadyCount--;
1921+
}
19111922
}
1923+
// otherwise, completed buffer still has available space
19121924
} else {
19131925
index = GetRingIndex(mCompletedIndex + mCompletedCount);
19141926
mCompletedCount++;
@@ -2384,19 +2396,20 @@ void PMTraceConsumer::DequeueProcessEvents(std::vector<ProcessEvent>& outProcess
23842396
void PMTraceConsumer::DequeuePresentEvents(std::vector<std::shared_ptr<PresentEvent>>& outPresentEvents)
23852397
{
23862398
outPresentEvents.clear();
2399+
{
2400+
std::lock_guard<std::mutex> lock(mPresentEventMutex);
2401+
if (mReadyCount > 0) {
2402+
outPresentEvents.resize(mReadyCount, nullptr);
2403+
for (uint32_t i = 0; i < mReadyCount; ++i) {
2404+
std::swap(outPresentEvents[i], mCompletedPresents[mCompletedIndex]);
2405+
mCompletedIndex = GetRingIndex(mCompletedIndex + 1);
2406+
}
23872407

2388-
std::lock_guard<std::mutex> lock(mPresentEventMutex);
2389-
2390-
if (mReadyCount > 0) {
2391-
outPresentEvents.resize(mReadyCount, nullptr);
2392-
for (uint32_t i = 0; i < mReadyCount; ++i) {
2393-
std::swap(outPresentEvents[i], mCompletedPresents[mCompletedIndex]);
2394-
mCompletedIndex = GetRingIndex(mCompletedIndex + 1);
2408+
mCompletedCount -= mReadyCount;
2409+
mReadyCount = 0;
23952410
}
2396-
2397-
mCompletedCount -= mReadyCount;
2398-
mReadyCount = 0;
23992411
}
2412+
mCompletedRingCondition.notify_one();
24002413
}
24012414

24022415
#ifdef TRACK_PRESENT_PATHS

PresentData/PresentMonTraceConsumer.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ struct PMTraceConsumer
251251
// deferred, potentially leading to dequeued events that are missing data.
252252
uint64_t mDeferralTimeLimit = 0; // QPC duration
253253

254+
bool mIsRealtimeSession = true; // allow consumer to have different behavior for realtime vs. offline analysis
255+
bool mDisableOfflineBackpressure = false;
254256

255257
// -------------------------------------------------------------------------------------------
256258
// These functions can be used to filter PresentEvents by process from within the consumer.
@@ -289,6 +291,7 @@ struct PMTraceConsumer
289291
// Mutexs to protect consumer/dequeue access from different threads:
290292
std::mutex mProcessEventMutex;
291293
std::mutex mPresentEventMutex;
294+
std::condition_variable mCompletedRingCondition;
292295

293296

294297
// EventMetadata stores the structure of ETW events to optimize subsequent property retrieval.

PresentData/PresentMonTraceSession.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ ULONG PMTraceSession::Start(
514514
mStartTimestamp.QuadPart = 0;
515515
mContinueProcessingBuffers = TRUE;
516516
mIsRealtimeSession = etlPath == nullptr;
517+
mPMConsumer->mIsRealtimeSession = mIsRealtimeSession;
517518

518519
// If we're not reading an ETL, start a realtime trace session with the
519520
// required providers enabled.

PresentMon/CommandLine.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,9 @@ bool ParseCommandLine(int argc, wchar_t** argv)
397397
args->mMultiCsv = false;
398398
args->mUseV1Metrics = false;
399399
args->mStopExistingSession = false;
400+
args->mWriteFrameId = false;
401+
args->mWriteDisplayTime = false;
402+
args->mDisableOfflineBackpressure = false;
400403

401404
bool sessionNameSet = false;
402405
bool csvOutputStdout = false;
@@ -456,6 +459,9 @@ bool ParseCommandLine(int argc, wchar_t** argv)
456459
#if PRESENTMON_ENABLE_DEBUG_TRACE
457460
else if (ParseArg(argv[i], L"debug_verbose_trace")) { verboseTrace = true; continue; }
458461
#endif
462+
else if (ParseArg(argv[i], L"write_frame_id")) { args->mWriteFrameId = true; continue; }
463+
else if (ParseArg(argv[i], L"write_display_time")) { args->mWriteDisplayTime = true; continue; }
464+
else if (ParseArg(argv[i], L"disable_offline_backpressure")) { args->mDisableOfflineBackpressure = true; continue; }
459465

460466
// Provided argument wasn't recognized
461467
else if (!(ParseArg(argv[i], L"?") || ParseArg(argv[i], L"h") || ParseArg(argv[i], L"help"))) {

PresentMon/CsvOutput.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ void WriteCsvHeader<FrameMetrics1>(FILE* fp)
164164
if (args.mTimeUnit == TimeUnit::QPC || args.mTimeUnit == TimeUnit::QPCMilliSeconds) {
165165
fwprintf(fp, L",QPCTime");
166166
}
167+
if (args.mWriteDisplayTime) {
168+
fwprintf(fp, L",msDisplayTime");
169+
}
170+
if (args.mWriteFrameId) {
171+
fwprintf(fp, L",FrameId");
172+
}
167173
fwprintf(fp, L"\n");
168174

169175
if (args.mCSVOutput == CSVOutput::Stdout) {
@@ -232,6 +238,17 @@ void WriteCsvRow<FrameMetrics1>(
232238
fwprintf(fp, L",%.*lf", DBL_DIG - 1, 0.001 * pmSession.TimestampDeltaToMilliSeconds(p.PresentStartTime));
233239
break;
234240
}
241+
if (args.mWriteDisplayTime) {
242+
if (p.ScreenTime == 0) {
243+
fwprintf(fp, L",NA");
244+
}
245+
else {
246+
fwprintf(fp, L",%.*lf", DBL_DIG - 1, 0.001 * pmSession.TimestampToMilliSeconds(p.ScreenTime));
247+
}
248+
}
249+
if (args.mWriteFrameId) {
250+
fwprintf(fp, L",%u", p.FrameId);
251+
}
235252
fwprintf(fp, L"\n");
236253

237254
if (args.mCSVOutput == CSVOutput::Stdout) {
@@ -283,6 +300,12 @@ void WriteCsvHeader<FrameMetrics>(FILE* fp)
283300
if (args.mTrackInput) {
284301
fwprintf(fp, L",ClickToPhotonLatency");
285302
}
303+
if (args.mWriteDisplayTime) {
304+
fwprintf(fp, L",DisplayTimeAbs");
305+
}
306+
if (args.mWriteFrameId) {
307+
fwprintf(fp, L",FrameId");
308+
}
286309
fwprintf(fp, L"\n");
287310
}
288311

@@ -360,6 +383,17 @@ void WriteCsvRow<FrameMetrics>(
360383
fwprintf(fp, L",%.4lf", metrics.mClickToPhotonLatency);
361384
}
362385
}
386+
if (args.mWriteDisplayTime) {
387+
if (p.ScreenTime == 0) {
388+
fwprintf(fp, L",NA");
389+
}
390+
else {
391+
fwprintf(fp, L",%.4lf", pmSession.TimestampToMilliSeconds(p.ScreenTime));
392+
}
393+
}
394+
if (args.mWriteFrameId) {
395+
fwprintf(fp, L",%u", p.FrameId);
396+
}
363397
fwprintf(fp, L"\n");
364398
}
365399

PresentMon/MainThread.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ int wmain(int argc, wchar_t** argv)
259259
pmConsumer.mTrackGPUVideo = args.mTrackGPUVideo;
260260
pmConsumer.mTrackInput = args.mTrackInput;
261261
pmConsumer.mTrackFrameType = args.mTrackFrameType;
262+
pmConsumer.mDisableOfflineBackpressure = args.mDisableOfflineBackpressure;
262263

263264
if (args.mTargetPid != 0) {
264265
pmConsumer.mFilteredProcessIds = true;

PresentMon/PresentMon.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ struct CommandLineArgs {
8383
bool mMultiCsv;
8484
bool mUseV1Metrics;
8585
bool mStopExistingSession;
86+
bool mWriteFrameId;
87+
bool mWriteDisplayTime;
88+
bool mDisableOfflineBackpressure;
8689
};
8790

8891
// Metrics computed per-frame. Duration and Latency metrics are in milliseconds.

0 commit comments

Comments
 (0)