-
Notifications
You must be signed in to change notification settings - Fork 203
feat(stats): Add 1% low FPS tracking #2661
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
base: main
Are you sure you want to change the base?
Changes from all commits
f70857e
c062022
a303d26
514a311
62076ac
ecd3596
512ece6
62978ac
ca6d2a6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -340,6 +340,7 @@ W3DDisplay::W3DDisplay() | |
| m_2DScene = nullptr; | ||
| m_3DInterfaceScene = nullptr; | ||
| m_averageFPS = TheGlobalData->m_framesPerSecondLimit; | ||
| m_low1PercentFPS = TheGlobalData->m_framesPerSecondLimit; | ||
| #if defined(RTS_DEBUG) | ||
| m_timerAtCumuFPSStart = 0; | ||
| #endif | ||
|
|
@@ -360,6 +361,16 @@ W3DDisplay::W3DDisplay() | |
| m_batchGrayscale = FALSE; | ||
| m_batchNeedsInit = FALSE; | ||
|
|
||
| m_historyOffset = 0; | ||
| m_historyCount = 1; | ||
| m_lastUpdateTime64 = 0; | ||
| m_currentFPS = 30.0f; | ||
| for (Int h = 0; h < FPS_HISTORY_SIZE; ++h) | ||
| { | ||
| m_fpsHistory[h] = 30.0f; | ||
| m_durationHistory[h] = 1.0f / 30.0f; | ||
| } | ||
|
|
||
| #ifdef PROFILER_ENABLED | ||
| m_profilerFrameCapture = NEW W3DProfilerFrameCapture(); | ||
| #endif | ||
|
|
@@ -915,6 +926,7 @@ void W3DDisplay::init() | |
|
|
||
| // we're now online | ||
| m_initialized = true; | ||
| m_lastUpdateTime64 = getPerformanceCounter(); | ||
| if( TheGlobalData->m_displayDebug ) | ||
| { | ||
| m_debugDisplayCallback = StatDebugDisplay; | ||
|
|
@@ -958,14 +970,86 @@ void W3DDisplay::reset() | |
|
|
||
| const UnsignedInt START_CUMU_FRAME = LOGICFRAMES_PER_SECOND / 2; // skip first half-sec | ||
|
|
||
| void W3DDisplay::updateAverageFPS() | ||
| void W3DDisplay::addFpsSample(Real elapsedSeconds) | ||
| { | ||
| constexpr const Int FPS_HISTORY_SIZE = 30; | ||
| if (elapsedSeconds < 0.0001f) | ||
| { | ||
| elapsedSeconds = 0.0001f; | ||
| } | ||
|
|
||
| static Int64 lastUpdateTime64 = 0; | ||
| static Int historyOffset = 0; | ||
| static Real fpsHistory[FPS_HISTORY_SIZE] = {0}; | ||
| m_currentFPS = 1.0f / elapsedSeconds; | ||
| m_fpsHistory[m_historyOffset] = m_currentFPS; | ||
| m_durationHistory[m_historyOffset] = elapsedSeconds; | ||
|
|
||
| m_historyOffset = (m_historyOffset + 1) & (FPS_HISTORY_SIZE - 1); | ||
| if (m_historyCount < FPS_HISTORY_SIZE) | ||
| { | ||
| m_historyCount++; | ||
| } | ||
| } | ||
|
|
||
| Real W3DDisplay::calculateAverageFPS(Real windowSeconds) | ||
| { | ||
| Real timeSum = 0; | ||
| Int samples = 0; | ||
|
|
||
| Int idx = m_historyOffset - 1; | ||
| for (Int i = 0; i < m_historyCount; ++i) | ||
| { | ||
| if (idx < 0) idx += FPS_HISTORY_SIZE; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing line break Multiple times. |
||
| timeSum += m_durationHistory[idx]; | ||
| samples++; | ||
|
|
||
| if (timeSum >= windowSeconds) | ||
| { | ||
| break; | ||
| } | ||
| --idx; | ||
| } | ||
|
|
||
| return (timeSum > 0) ? ((Real)samples / timeSum) : m_currentFPS; | ||
| } | ||
|
|
||
| Real W3DDisplay::calculateLow1PercentFPS(Real windowSeconds) | ||
| { | ||
| Real timeSum = 0; | ||
| Int sampleCount = 0; | ||
| Int i; | ||
|
|
||
| Int idx = m_historyOffset - 1; | ||
| for (i = 0; i < m_historyCount; ++i) | ||
| { | ||
| if (idx < 0) idx += FPS_HISTORY_SIZE; | ||
| timeSum += m_durationHistory[idx]; | ||
| m_sortBuffer[sampleCount++] = m_fpsHistory[idx]; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe the sort buffer can be on the stack instead? Should be a lot cheaper. |
||
|
|
||
| if (timeSum >= windowSeconds) | ||
| { | ||
| break; | ||
| } | ||
| --idx; | ||
| } | ||
|
|
||
| if (sampleCount == 0) | ||
| { | ||
| return m_currentFPS; | ||
| } | ||
|
|
||
| const Int bottomSampleCount = std::max((sampleCount + 50) / 100, 1); | ||
|
|
||
| std::nth_element(m_sortBuffer, m_sortBuffer + bottomSampleCount, m_sortBuffer + sampleCount); | ||
|
|
||
| Real lowSum = 0; | ||
| for (i = 0; i < bottomSampleCount; ++i) | ||
| { | ||
| lowSum += m_sortBuffer[i]; | ||
| } | ||
|
|
||
| return lowSum / (Real)bottomSampleCount; | ||
| } | ||
|
|
||
| void W3DDisplay::updatePerformanceMetrics() | ||
| { | ||
| const Int64 freq64 = getPerformanceCounterFrequency(); | ||
| const Int64 time64 = getPerformanceCounter(); | ||
|
|
||
|
|
@@ -976,23 +1060,21 @@ void W3DDisplay::updateAverageFPS() | |
| } | ||
| #endif | ||
|
|
||
| const Int64 timeDiff = time64 - lastUpdateTime64; | ||
|
|
||
| // convert elapsed time to seconds | ||
| Real elapsedSeconds = (Real)timeDiff/(Real)freq64; | ||
| const Int64 timeDiff = time64 - m_lastUpdateTime64; | ||
| Real elapsedSeconds = (Real)timeDiff / (Real)freq64; | ||
|
|
||
| // append new sample to fps history. | ||
| if (historyOffset >= FPS_HISTORY_SIZE) | ||
| historyOffset = 0; | ||
| addFpsSample(elapsedSeconds); | ||
| m_averageFPS = calculateAverageFPS(1.0f); // 1.0s window for smooth Dynamic LOD tracking and UI matching | ||
|
|
||
| m_currentFPS = 1.0f/elapsedSeconds; | ||
| fpsHistory[historyOffset++] = m_currentFPS; | ||
|
|
||
| // determine average frame rate over our past history. | ||
| const Real sum = std::accumulate(fpsHistory, fpsHistory + FPS_HISTORY_SIZE, 0.0f); | ||
| m_averageFPS = sum / FPS_HISTORY_SIZE; | ||
| static UnsignedInt lastLowUpdate = 0; | ||
| UnsignedInt now = timeGetTime(); | ||
| if (now - lastLowUpdate >= 100) // update low 1% metrics at 100ms intervals instead of 1000ms since it is now extremely cheap | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does "is now extremely cheap" mean? If it was extremely cheap, it would be updated every frame, but it is updated 10 times a second. |
||
| { | ||
| lastLowUpdate = now; | ||
| m_low1PercentFPS = calculateLow1PercentFPS(3.0f); | ||
| } | ||
|
|
||
| lastUpdateTime64 = time64; | ||
| m_lastUpdateTime64 = time64; | ||
| } | ||
|
|
||
| #if defined(RTS_DEBUG) //debug hack to view object under mouse stats | ||
|
|
@@ -1693,6 +1775,11 @@ Real W3DDisplay::getAverageFPS() | |
| return m_averageFPS; | ||
| } | ||
|
|
||
| Real W3DDisplay::getLow1PercentFPS() | ||
| { | ||
| return m_low1PercentFPS; | ||
| } | ||
|
|
||
| Real W3DDisplay::getCurrentFPS() | ||
| { | ||
| return m_currentFPS; | ||
|
|
@@ -1727,7 +1814,7 @@ void W3DDisplay::draw() | |
| if (TheGlobalData->m_headless) | ||
| return; | ||
|
|
||
| updateAverageFPS(); | ||
| updatePerformanceMetrics(); | ||
| if (TheGlobalData->m_enableDynamicLOD && TheGameLogic->getShowDynamicLOD()) | ||
| { | ||
| DynamicGameLODLevel lod=TheGameLODManager->findDynamicLODLevel(m_averageFPS); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this clamp necessary? In what event is the frame 0 seconds long?