diff --git a/PresentData/PresentMonTraceConsumer.cpp b/PresentData/PresentMonTraceConsumer.cpp index 438542d51..2a179ad74 100644 --- a/PresentData/PresentMonTraceConsumer.cpp +++ b/PresentData/PresentMonTraceConsumer.cpp @@ -22,15 +22,8 @@ #include #include -static constexpr int PRESENTEVENT_CIRCULAR_BUFFER_SIZE = 4096; - static uint32_t gNextFrameId = 1; -static inline uint32_t GetRingIndex(uint32_t index) -{ - return index % PRESENTEVENT_CIRCULAR_BUFFER_SIZE; -} - static inline uint64_t GenerateVidPnLayerId(uint32_t vidPnSourceId, uint32_t layerIndex) { return (((uint64_t) vidPnSourceId) << 32) | (uint64_t) layerIndex; @@ -248,6 +241,16 @@ PresentEvent::PresentEvent(uint32_t fid) PMTraceConsumer::PMTraceConsumer() : mTrackedPresents(PRESENTEVENT_CIRCULAR_BUFFER_SIZE) , mCompletedPresents(PRESENTEVENT_CIRCULAR_BUFFER_SIZE) + , mCircularBufferSize(PRESENTEVENT_CIRCULAR_BUFFER_SIZE) + , mGpuTrace(this) +{ + hEventsReadyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); +} + +PMTraceConsumer::PMTraceConsumer(uint32_t circularBufferSize) + : mTrackedPresents(circularBufferSize) + , mCompletedPresents(circularBufferSize) + , mCircularBufferSize(circularBufferSize) , mGpuTrace(this) { hEventsReadyEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); @@ -2210,11 +2213,11 @@ void PMTraceConsumer::CompletePresent(std::shared_ptr const& p) if (present != nullptr) { uint32_t index; // if completed buffer is full - if (mCompletedCount == PRESENTEVENT_CIRCULAR_BUFFER_SIZE) { + if (mCompletedCount == mCircularBufferSize) { // if we are in offline ETL processing mode, block instead of overwriting events // unless either A) the buffer is full of non-ready events or B) backpressure disabled via CLI option if (!mIsRealtimeSession && mReadyCount != 0 && !mDisableOfflineBackpressure) { - mCompletedRingCondition.wait(lock, [this] { return mCompletedCount < PRESENTEVENT_CIRCULAR_BUFFER_SIZE; }); + mCompletedRingCondition.wait(lock, [this] { return mCompletedCount < mCircularBufferSize; }); index = GetRingIndex(mCompletedIndex + mCompletedCount); mCompletedCount++; } @@ -2230,6 +2233,7 @@ void PMTraceConsumer::CompletePresent(std::shared_ptr const& p) if (mReadyCount > 0) { mReadyCount--; } + mNumOverflowedPresents++; } // otherwise, completed buffer still has available space } diff --git a/PresentData/PresentMonTraceConsumer.hpp b/PresentData/PresentMonTraceConsumer.hpp index d3364e4d4..635db9219 100644 --- a/PresentData/PresentMonTraceConsumer.hpp +++ b/PresentData/PresentMonTraceConsumer.hpp @@ -294,6 +294,8 @@ struct PresentEvent { struct PMTraceConsumer { + static constexpr int PRESENTEVENT_CIRCULAR_BUFFER_SIZE = 2048; + // ------------------------------------------------------------------------------------------- // The following are parameters to the analysis, which can be configured prior to starting the // trace session. @@ -353,6 +355,8 @@ struct PMTraceConsumer uint32_t mCompletedIndex = 0; // The index of mCompletedPresents of the oldest completed present. uint32_t mCompletedCount = 0; // The total number of presents in mCompletedPresents. uint32_t mReadyCount = 0; // The number of presents in mCompletedPresents, starting at mCompletedIndex, that are ready to be dequeued. + uint32_t mNumOverflowedPresents = 0; // The number of presents that have been lost due to the ring buffer wrapping. + uint32_t mCircularBufferSize = 0; // The size of the ring buffers for presents. // Mutexs to protect consumer/dequeue access from different threads: std::mutex mProcessEventMutex; @@ -505,6 +509,7 @@ struct PMTraceConsumer // Functions for decoding ETW and analysing process and present events. PMTraceConsumer(); + PMTraceConsumer(uint32_t circularBufferSize); ~PMTraceConsumer(); PMTraceConsumer(const PMTraceConsumer&) = delete; @@ -566,4 +571,10 @@ struct PMTraceConsumer AppTimingData* ExtractAppTimingData(uint32_t processId, uint32_t appFrameId, uint64_t presentStartTime); bool IsApplicationPresent(std::shared_ptr const& present); void SetAppTimingDataAsComplete(uint32_t processId, uint32_t appFrameId); + + inline uint32_t GetRingIndex(uint32_t index) + { + return index % mCircularBufferSize; + } + }; diff --git a/PresentMon/CommandLine.cpp b/PresentMon/CommandLine.cpp index b97fb304a..cc4cf04e1 100644 --- a/PresentMon/CommandLine.cpp +++ b/PresentMon/CommandLine.cpp @@ -299,6 +299,7 @@ void PrintUsage() 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 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.)", + LR"(--set_circular_buffer_size)", LR"(Overide the default present event circular buffer size of 2048. Must be a power of 2.)", }; // Layout @@ -384,6 +385,7 @@ bool ParseCommandLine(int argc, wchar_t** argv) args->mTimer = 0; args->mHotkeyModifiers = MOD_NOREPEAT; args->mHotkeyVirtualKeyCode = 0; + args->mPresentEventCircularBufferSize = 0; args->mConsoleOutput = ConsoleOutput::Statistics; args->mTrackDisplay = true; args->mTrackInput = true; @@ -460,6 +462,7 @@ bool ParseCommandLine(int argc, wchar_t** argv) else if (ParseArg(argv[i], L"restart_as_admin")) { args->mTryToElevate = true; continue; } else if (ParseArg(argv[i], L"terminate_on_proc_exit")) { args->mTerminateOnProcExit = true; continue; } else if (ParseArg(argv[i], L"terminate_after_timed")) { args->mTerminateAfterTimer = true; continue; } + else if (ParseArg(argv[i], L"set_circular_buffer_size")) { if (ParseValue(argv, argc, &i, &args->mPresentEventCircularBufferSize)) { continue; } } // Beta options: else if (ParseArg(argv[i], L"track_frame_type")) { args->mTrackFrameType = true; continue; } @@ -507,6 +510,14 @@ bool ParseCommandLine(int argc, wchar_t** argv) return false; } + // Disallow a circular buffer size that is not a power of 2. + if (args->mPresentEventCircularBufferSize != 0) { + if ((args->mPresentEventCircularBufferSize & (args->mPresentEventCircularBufferSize - 1)) != 0) { + PrintError(L"error: --set_circular_buffer_size must be a power of 2.\n"); + return false; + } + } + // Ensure only one of --output_file --output_stdout --no_csv. if (csvOutputNone + csvOutputStdout + (args->mOutputCsvFileName != nullptr) > 1) { PrintWarning(L"warning: only one of the following options may be used:"); diff --git a/PresentMon/MainThread.cpp b/PresentMon/MainThread.cpp index 6fefe908f..4ffa831a0 100644 --- a/PresentMon/MainThread.cpp +++ b/PresentMon/MainThread.cpp @@ -253,7 +253,11 @@ int wmain(int argc, wchar_t** argv) SetConsoleCtrlHandler(HandleCtrlEvent, TRUE); // Create event consumers - PMTraceConsumer pmConsumer; + PMTraceConsumer pmConsumer( + args.mPresentEventCircularBufferSize != 0 + ? args.mPresentEventCircularBufferSize + : PMTraceConsumer::PRESENTEVENT_CIRCULAR_BUFFER_SIZE); + pmConsumer.mTrackDisplay = args.mTrackDisplay; pmConsumer.mTrackGPU = args.mTrackGPU; pmConsumer.mTrackGPUVideo = args.mTrackGPUVideo; @@ -383,6 +387,14 @@ int wmain(int argc, wchar_t** argv) if (pmSession.mNumEventsLost > 0) { PrintWarning(L"warning: %lu ETW events were lost.\n", pmSession.mNumEventsLost); } + if (pmConsumer.mNumOverflowedPresents > 0) { + PrintWarning(L"warning: %lu overflowed present events detected. This could be due to a high-fps application.\n", + pmConsumer.mNumOverflowedPresents); + PrintWarning(L" Consider increasing the present event circular buffer size to a value larger\n"); + PrintWarning(L" than the default of 2048, e.g., --set_circular_buffer_size 4096.\n"); + + + } /* We cannot remove the Ctrl handler because it is in an infinite sleep so * this call will never return, either hanging the application or having diff --git a/PresentMon/PresentMon.hpp b/PresentMon/PresentMon.hpp index 52fa1720d..67c3d5d69 100644 --- a/PresentMon/PresentMon.hpp +++ b/PresentMon/PresentMon.hpp @@ -66,6 +66,7 @@ struct CommandLineArgs { UINT mTimer; UINT mHotkeyModifiers; UINT mHotkeyVirtualKeyCode; + UINT mPresentEventCircularBufferSize; TimeUnit mTimeUnit; CSVOutput mCSVOutput; ConsoleOutput mConsoleOutput;