-
Notifications
You must be signed in to change notification settings - Fork 0
Fpscounter #28
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
Fpscounter #28
Changes from all commits
621e5be
1c57c1d
67640ee
3a9c815
6b4ada2
03b5845
de4f052
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 |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
|
|
||
| #include "stdafx.h" | ||
| #include <vector> | ||
| #include <algorithm> | ||
| #include <numeric> | ||
| #include "SceneManager.h" | ||
|
|
||
| //============================================================================= | ||
|
|
@@ -58,6 +60,112 @@ extern bool Destroy; | |
| extern double WorldTime; | ||
| extern float FPS_ANIMATION_FACTOR; | ||
|
|
||
| static bool g_bShowDebugInfo = | ||
| #ifdef _DEBUG | ||
| true; | ||
| #else | ||
| false; | ||
| #endif | ||
|
|
||
| static bool g_bShowFpsCounter = false; | ||
|
|
||
| void SetShowDebugInfo(bool enabled) | ||
| { | ||
| g_bShowDebugInfo = enabled; | ||
| if (enabled) g_bShowFpsCounter = false; | ||
| } | ||
|
|
||
| void SetShowFpsCounter(bool enabled) | ||
| { | ||
| g_bShowFpsCounter = enabled; | ||
| if (enabled) g_bShowDebugInfo = false; | ||
| } | ||
|
|
||
| //============================================================================= | ||
| // Frame Statistics Tracker | ||
| //============================================================================= | ||
|
|
||
| static constexpr int FRAME_HISTORY_SIZE = 300; // ~5 seconds at 60fps | ||
| static constexpr float MIN_FRAME_TIME_MS = 0.5f; // clamp to 2000fps max | ||
| static constexpr double STATS_UPDATE_INTERVAL = 500.0; // ms between percentile recalculations | ||
| static constexpr int MIN_FRAMES_FOR_STATS = 10; | ||
| static constexpr float GRAPH_MAX_MS = 33.3f; // graph Y-axis scale (30fps) | ||
| static constexpr float THRESHOLD_60FPS_MS = 16.67f; // 60 FPS threshold | ||
| static constexpr float THRESHOLD_40FPS_MS = 25.0f; // 40 FPS threshold | ||
| static constexpr float DEBUG_TEXT_X = 10.0f; // debug overlay X position | ||
| static constexpr int DEBUG_TEXT_Y_START = 26; // debug overlay Y start | ||
| static constexpr int DEBUG_TEXT_LINE_HEIGHT = 10; // line spacing | ||
| static constexpr float DEBUG_GRAPH_WIDTH = 200.0f; // frame graph width | ||
| static constexpr float DEBUG_GRAPH_HEIGHT = 40.0f; // frame graph height | ||
| static constexpr float DEBUG_GRAPH_Y_OFFSET = 2.0f; // gap between text and graph | ||
|
|
||
| static float s_frameTimesMs[FRAME_HISTORY_SIZE] = {}; | ||
| static int s_frameIndex = 0; | ||
| static int s_frameCount = 0; | ||
| static double s_lastFrameTime = 0.0; | ||
| static double s_highestFps = 0.0; | ||
|
|
||
| // Percentile stats (updated periodically) | ||
| static float s_avgFps = 0.0f; | ||
| static float s_onePercentLow = 0.0f; | ||
| static float s_slowestFrameFps = 0.0f; | ||
| static double s_lastStatsUpdate = 0.0; | ||
|
|
||
| void ResetFrameStats() | ||
| { | ||
| memset(s_frameTimesMs, 0, sizeof(s_frameTimesMs)); | ||
| s_frameIndex = 0; | ||
| s_frameCount = 0; | ||
| s_lastFrameTime = 0.0; | ||
| s_highestFps = 0.0; | ||
| s_avgFps = 0.0f; | ||
| s_onePercentLow = 0.0f; | ||
| s_slowestFrameFps = 0.0f; | ||
| s_lastStatsUpdate = 0.0; | ||
| } | ||
|
|
||
| static void UpdateFrameStats() | ||
| { | ||
| double now = WorldTime; | ||
| if (s_lastFrameTime > 0.0) | ||
| { | ||
| double dt = now - s_lastFrameTime; | ||
| if (dt < MIN_FRAME_TIME_MS) dt = MIN_FRAME_TIME_MS; | ||
| s_frameTimesMs[s_frameIndex] = static_cast<float>(dt); | ||
| s_frameIndex = (s_frameIndex + 1) % FRAME_HISTORY_SIZE; | ||
| if (s_frameCount < FRAME_HISTORY_SIZE) s_frameCount++; | ||
|
|
||
| double instantaneousFps = 1000.0 / dt; | ||
| if (instantaneousFps > s_highestFps) s_highestFps = instantaneousFps; | ||
| } | ||
| s_lastFrameTime = now; | ||
|
|
||
| // Update percentile stats periodically | ||
| if (now - s_lastStatsUpdate > STATS_UPDATE_INTERVAL && s_frameCount > MIN_FRAMES_FOR_STATS) | ||
| { | ||
| s_lastStatsUpdate = now; | ||
|
|
||
| // Copy and sort frame times (descending = slowest first) | ||
| static float sorted[FRAME_HISTORY_SIZE]; | ||
| memcpy(sorted, s_frameTimesMs, sizeof(float) * s_frameCount); | ||
| std::sort(sorted, sorted + s_frameCount, std::greater<float>()); | ||
|
|
||
| // Average | ||
| float sum = std::accumulate(sorted, sorted + s_frameCount, 0.0f); | ||
| float avgMs = sum / s_frameCount; | ||
| s_avgFps = (avgMs > 0.0f) ? 1000.0f / avgMs : 0.0f; | ||
|
|
||
| // 1% low: average of the slowest 1% of frames | ||
| int onePercCount = std::max(1, s_frameCount / 100); | ||
| float onePercSum = std::accumulate(sorted, sorted + onePercCount, 0.0f); | ||
| float onePercAvgMs = onePercSum / onePercCount; | ||
| s_onePercentLow = (onePercAvgMs > 0.0f) ? 1000.0f / onePercAvgMs : 0.0f; | ||
|
|
||
| // Slowest frame: the single slowest frame in the window | ||
| s_slowestFrameFps = (sorted[0] > 0.0f) ? 1000.0f / sorted[0] : 0.0f; | ||
| } | ||
| } | ||
|
|
||
| void SetTargetFps(double targetFps) | ||
| { | ||
| if (IsVSyncEnabled() && targetFps >= GetFPSLimit()) | ||
|
|
@@ -292,29 +400,141 @@ static bool RenderCurrentScene(HDC hDC) | |
| } | ||
|
|
||
| /** | ||
| * @brief Renders debug information overlay in development builds. | ||
| * @brief Renders a frame time graph using raw OpenGL quads. | ||
| * | ||
| * Draws a bar chart of recent frame times inside BeginBitmap's 2D ortho projection. | ||
| * Coordinates are in virtual 640x480 space, converted to window pixels. | ||
| */ | ||
| static void RenderFrameGraph(float graphX, float graphY, float graphW, float graphH) | ||
| { | ||
| if (s_frameCount < 2) | ||
| return; | ||
|
|
||
| // Convert virtual 640x480 coords to actual window pixels | ||
| float gx = graphX * (float)WindowWidth / 640.f; | ||
| float gy = graphY * (float)WindowHeight / 480.f; | ||
| float gw = graphW * (float)WindowWidth / 640.f; | ||
| float gh = graphH * (float)WindowHeight / 480.f; | ||
|
|
||
| // Flip Y for OpenGL (origin bottom-left) | ||
| float glBottom = (float)WindowHeight - gy - gh; | ||
| float glTop = (float)WindowHeight - gy; | ||
|
|
||
| // Background | ||
| glDisable(GL_TEXTURE_2D); | ||
| glEnable(GL_BLEND); | ||
| glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | ||
|
|
||
| glColor4f(0.0f, 0.0f, 0.0f, 0.5f); | ||
| glBegin(GL_QUADS); | ||
| glVertex2f(gx, glBottom); | ||
| glVertex2f(gx + gw, glBottom); | ||
| glVertex2f(gx + gw, glTop); | ||
| glVertex2f(gx, glTop); | ||
| glEnd(); | ||
|
|
||
| // Target line at 16.67ms (60fps) | ||
| float target60 = THRESHOLD_60FPS_MS / GRAPH_MAX_MS; | ||
| float lineY = glBottom + target60 * gh; | ||
| glColor4f(0.3f, 0.8f, 0.3f, 0.5f); | ||
| glBegin(GL_LINES); | ||
| glVertex2f(gx, lineY); | ||
| glVertex2f(gx + gw, lineY); | ||
| glEnd(); | ||
|
|
||
| // Frame bars | ||
| float barW = gw / FRAME_HISTORY_SIZE; | ||
| int oldest = (s_frameCount < FRAME_HISTORY_SIZE) ? 0 : s_frameIndex; | ||
|
|
||
| glBegin(GL_QUADS); | ||
| for (int i = 0; i < s_frameCount; i++) | ||
| { | ||
| int idx = (oldest + i) % FRAME_HISTORY_SIZE; | ||
| float ms = s_frameTimesMs[idx]; | ||
| float norm = std::min(ms / GRAPH_MAX_MS, 1.0f); | ||
| float barH = norm * gh; | ||
|
|
||
| // Color: green < 16.67ms, yellow < 25ms, red >= 25ms | ||
| if (ms < THRESHOLD_60FPS_MS) | ||
| glColor4f(0.2f, 0.9f, 0.2f, 0.8f); | ||
| else if (ms < THRESHOLD_40FPS_MS) | ||
| glColor4f(0.9f, 0.9f, 0.2f, 0.8f); | ||
| else | ||
| glColor4f(0.9f, 0.2f, 0.2f, 0.8f); | ||
|
|
||
| float bx = gx + i * barW; | ||
| glVertex2f(bx, glBottom); | ||
| glVertex2f(bx + barW, glBottom); | ||
| glVertex2f(bx + barW, glBottom + barH); | ||
| glVertex2f(bx, glBottom + barH); | ||
| } | ||
Mosch0512 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| glEnd(); | ||
|
|
||
| glEnable(GL_TEXTURE_2D); | ||
| } | ||
|
Comment on lines
+408
to
+474
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. The use of immediate mode OpenGL ( |
||
|
|
||
| /** | ||
| * @brief Renders debug information overlay. | ||
| * | ||
| * Shows FPS, mouse position, and camera info on screen. | ||
| * Shows FPS stats, percentile lows, mouse position, camera info, and frame time graph. | ||
| */ | ||
| static void RenderDebugInfo() | ||
| { | ||
| #if defined(_DEBUG) || defined(LDS_FOR_DEVELOPMENT_TESTMODE) || defined(LDS_UNFIXED_FIXEDFRAME_FORDEBUG) | ||
| if (!g_bShowDebugInfo) | ||
| return; | ||
|
|
||
| UpdateFrameStats(); | ||
|
|
||
| BeginBitmap(); | ||
| wchar_t szDebugText[128]; | ||
| swprintf(szDebugText, L"FPS: %.1f Vsync: %d CPU: %.1f%%", FPS_AVG, IsVSyncEnabled(), CPU_AVG); | ||
| wchar_t szMousePos[128]; | ||
| swprintf(szMousePos, L"MousePos : %d %d %d", MouseX, MouseY, MouseLButtonPush); | ||
| wchar_t szCamera3D[128]; | ||
| swprintf(szCamera3D, L"Camera3D : %.1f %.1f:%.1f:%.1f", CameraFOV, CameraAngle[0], CameraAngle[1], CameraAngle[2]); | ||
|
|
||
| wchar_t szLine[128]; | ||
| g_pRenderText->SetFont(g_hFontBold); | ||
| g_pRenderText->SetBgColor(0, 0, 0, 100); | ||
| g_pRenderText->SetTextColor(255, 255, 255, 200); | ||
| g_pRenderText->RenderText(10, 26, szDebugText); | ||
| g_pRenderText->RenderText(10, 36, szMousePos); | ||
| g_pRenderText->RenderText(10, 46, szCamera3D); | ||
|
|
||
| int y = DEBUG_TEXT_Y_START; | ||
| swprintf(szLine, L"FPS: %.1f Avg: %.1f Max: %.1f Vsync: %d CPU: %.1f%%", | ||
| FPS_AVG, s_avgFps, s_highestFps, IsVSyncEnabled(), CPU_AVG); | ||
| g_pRenderText->RenderText((int)DEBUG_TEXT_X, y, szLine); y += DEBUG_TEXT_LINE_HEIGHT; | ||
|
|
||
| swprintf(szLine, L"1%% Low: %.1f Slowest: %.1f Frame: %.2fms", | ||
| s_onePercentLow, s_slowestFrameFps, | ||
| (s_avgFps > 0.0f) ? 1000.0f / s_avgFps : 0.0f); | ||
| g_pRenderText->RenderText((int)DEBUG_TEXT_X, y, szLine); y += DEBUG_TEXT_LINE_HEIGHT; | ||
|
|
||
| swprintf(szLine, L"MousePos: %d %d %d", MouseX, MouseY, MouseLButtonPush); | ||
| g_pRenderText->RenderText((int)DEBUG_TEXT_X, y, szLine); y += DEBUG_TEXT_LINE_HEIGHT; | ||
|
|
||
| swprintf(szLine, L"Camera3D: %.1f %.1f:%.1f:%.1f", CameraFOV, CameraAngle[0], CameraAngle[1], CameraAngle[2]); | ||
| g_pRenderText->RenderText((int)DEBUG_TEXT_X, y, szLine); y += DEBUG_TEXT_LINE_HEIGHT; | ||
|
|
||
| // Frame time graph below text | ||
| RenderFrameGraph(DEBUG_TEXT_X, (float)y + DEBUG_GRAPH_Y_OFFSET, DEBUG_GRAPH_WIDTH, DEBUG_GRAPH_HEIGHT); | ||
|
|
||
| g_pRenderText->SetFont(g_hFont); | ||
| EndBitmap(); | ||
| } | ||
|
|
||
| /** | ||
| * @brief Renders a simple FPS counter overlay showing only current FPS. | ||
| */ | ||
| static void RenderFpsCounter() | ||
| { | ||
| if (!g_bShowFpsCounter) | ||
| return; | ||
|
|
||
| BeginBitmap(); | ||
|
|
||
| wchar_t szLine[64]; | ||
| g_pRenderText->SetFont(g_hFontBold); | ||
| g_pRenderText->SetBgColor(0, 0, 0, 100); | ||
| g_pRenderText->SetTextColor(255, 255, 255, 200); | ||
|
|
||
| swprintf(szLine, L"FPS: %.1f", FPS_AVG); | ||
| g_pRenderText->RenderText((int)DEBUG_TEXT_X, DEBUG_TEXT_Y_START, szLine); | ||
|
|
||
| g_pRenderText->SetFont(g_hFont); | ||
| EndBitmap(); | ||
| #endif // defined(_DEBUG) || defined(LDS_FOR_DEVELOPMENT_TESTMODE) || defined(LDS_UNFIXED_FIXEDFRAME_FORDEBUG) | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -697,6 +917,7 @@ void MainScene(HDC hDC) | |
| { | ||
| Success = RenderCurrentScene(hDC); | ||
| RenderDebugInfo(); | ||
| RenderFpsCounter(); | ||
|
|
||
| if (Success) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| #include "GlobalBitmap.h" | ||
| #include "ZzzTexture.h" | ||
| #include "Scenes/SceneCore.h" | ||
| #include "Scenes/SceneManager.h" | ||
|
|
||
| #ifdef _EDITOR | ||
| #include "../MuEditor/UI/Console/MuEditorConsoleUI.h" | ||
|
|
@@ -79,28 +80,47 @@ void CmuConsoleDebug::UpdateMainScene() | |
|
|
||
| bool CmuConsoleDebug::CheckCommand(const std::wstring& strCommand) | ||
| { | ||
| if (strCommand.compare(0, 4, L"$fps") == 0) | ||
| if (strCommand.compare(L"$fpscounter on") == 0) | ||
| { | ||
| SetShowFpsCounter(true); | ||
| return true; | ||
| } | ||
| else if (strCommand.compare(L"$fpscounter off") == 0) | ||
| { | ||
| SetShowFpsCounter(false); | ||
| return true; | ||
| } | ||
| else if (strCommand.compare(L"$details on") == 0) | ||
| { | ||
| SetShowDebugInfo(true); | ||
| return true; | ||
| } | ||
| else if (strCommand.compare(L"$details off") == 0) | ||
| { | ||
| SetShowDebugInfo(false); | ||
| return true; | ||
| } | ||
Mosch0512 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| else if (strCommand.compare(0, 4, L"$fps") == 0) | ||
| { | ||
| auto fps_str = strCommand.substr(5); | ||
| auto target_fps = std::stof(fps_str); | ||
| SetTargetFps(target_fps); | ||
| return true; | ||
| } | ||
|
|
||
| if (strCommand.compare(L"$vsync on") == 0) | ||
| else if (strCommand.compare(L"$vsync on") == 0) | ||
| { | ||
| EnableVSync(); | ||
| SetTargetFps(-1); // unlimited | ||
| ResetFrameStats(); | ||
| return true; | ||
| } | ||
|
|
||
| if (strCommand.compare(L"$vsync off") == 0) | ||
| else if (strCommand.compare(L"$vsync off") == 0) | ||
| { | ||
| DisableVSync(); | ||
| ResetFrameStats(); | ||
| return true; | ||
| } | ||
|
Comment on lines
+83
to
122
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. |
||
|
|
||
| if (strCommand.compare(0, 7, L"$winmsg") == 0) | ||
| else if (strCommand.compare(0, 7, L"$winmsg") == 0) | ||
| { | ||
| auto str_limit = strCommand.substr(8); | ||
| auto message_limit = std::stof(str_limit); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.