diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java index 79d76ada40..9ee01f8b96 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java @@ -175,7 +175,10 @@ public enum BooleanSetting implements AbstractBooleanSetting GFX_WIDESCREEN_HACK(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "wideScreenHack", false), GFX_CROP(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "Crop", false), GFX_SHOW_FPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFPS", false), + GFX_SHOW_FTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowFTimes", false), GFX_SHOW_VPS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVPS", false), + GFX_SHOW_VTIMES(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowVTimes", false), + GFX_SHOW_GRAPHS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowGraphs", false), GFX_SHOW_SPEED(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeed", false), GFX_SHOW_SPEED_COLORS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "ShowSpeedColors", true), GFX_OVERLAY_STATS(Settings.FILE_GFX, Settings.SECTION_GFX_SETTINGS, "OverlayStats", false), diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 5a83729f17..e2751f37fd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -770,8 +770,14 @@ public final class SettingsFragmentPresenter R.string.video_backend, 0, R.array.videoBackendEntries, R.array.videoBackendValues)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FPS, R.string.show_fps, R.string.show_fps_description)); + sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_FTIMES, R.string.show_ftimes, + R.string.show_ftimes_description)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VPS, R.string.show_vps, R.string.show_vps_description)); + sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_VTIMES, R.string.show_vtimes, + R.string.show_vtimes_description)); + sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_GRAPHS, R.string.show_graphs, + R.string.show_graphs_description)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED, R.string.show_speed, R.string.show_speed_description)); sl.add(new SwitchSetting(mContext, BooleanSetting.GFX_SHOW_SPEED_COLORS, diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index e970976b8b..69c6029060 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -245,8 +245,14 @@ Select the API used for graphics rendering. Show FPS Shows the number of distinct frames rendered per second as a measure of visual smoothness. + Show Frame Times + Shows the average time in ms between each distinct rendered frame alongside the standard deviation. Show VPS Show the number of frames rendered per second as a measure of emulation speed. + Show VBlank Times + Shows the average time in ms between each rendered frame alongside the standard deviation. + Show Performance Graphs + Shows frametime graph along with statistics as a representation of emulation performance. Show % Speed Shows the % speed of emulation compared to full speed. Show Speed Color diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp index ccec2db5c9..f71c1e0c83 100644 --- a/Source/Core/AudioCommon/Mixer.cpp +++ b/Source/Core/AudioCommon/Mixer.cpp @@ -14,6 +14,7 @@ #include "Common/Swap.h" #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" +#include "VideoCommon/PerformanceMetrics.h" static u32 DPL2QualityToFrameBlockSize(AudioCommon::DPL2Quality quality) { @@ -160,6 +161,8 @@ unsigned int Mixer::Mix(short* samples, unsigned int num_samples) memset(samples, 0, num_samples * 2 * sizeof(short)); + // TODO: Determine how emulation speed will be used in audio + // const float emulation_speed = std::roundf(g_perf_metrics.GetSpeed()) / 100.f; const float emulation_speed = m_config_emulation_speed; const int timing_variance = m_config_timing_variance; if (m_config_audio_stretch) diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h index 7191fd6ab0..11039443ca 100644 --- a/Source/Core/AudioCommon/Mixer.h +++ b/Source/Core/AudioCommon/Mixer.h @@ -48,9 +48,6 @@ public: void StartLogDSPAudio(const std::string& filename); void StopLogDSPAudio(); - float GetCurrentSpeed() const { return m_speed.load(); } - void UpdateSpeed(float val) { m_speed.store(val); } - // 54000000 doesn't work here as it doesn't evenly divide with 32000, but 108000000 does static constexpr u64 FIXED_SAMPLE_RATE_DIVIDEND = 54000000 * 2; @@ -117,9 +114,6 @@ private: bool m_log_dtk_audio = false; bool m_log_dsp_audio = false; - // Current rate of emulation (1.0 = 100% speed) - std::atomic m_speed{0.0f}; - float m_config_emulation_speed; int m_config_timing_variance; bool m_config_audio_stretch; diff --git a/Source/Core/Common/CommonTypes.h b/Source/Core/Common/CommonTypes.h index 726a1b4f95..68397c1c3f 100644 --- a/Source/Core/Common/CommonTypes.h +++ b/Source/Core/Common/CommonTypes.h @@ -7,6 +7,7 @@ #pragma once +#include #include #ifdef _WIN32 @@ -26,3 +27,10 @@ using s8 = std::int8_t; using s16 = std::int16_t; using s32 = std::int32_t; using s64 = std::int64_t; + +using Clock = std::chrono::steady_clock; +using TimePoint = Clock::time_point; +using DT = Clock::duration; +using DT_us = std::chrono::duration; +using DT_ms = std::chrono::duration; +using DT_s = std::chrono::duration>; diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 5db6e93830..c52403d476 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -27,7 +27,10 @@ const Info GFX_CROP{{System::GFX, "Settings", "Crop"}, false}; const Info GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES{ {System::GFX, "Settings", "SafeTextureCacheColorSamples"}, 128}; const Info GFX_SHOW_FPS{{System::GFX, "Settings", "ShowFPS"}, false}; +const Info GFX_SHOW_FTIMES{{System::GFX, "Settings", "ShowFTimes"}, false}; const Info GFX_SHOW_VPS{{System::GFX, "Settings", "ShowVPS"}, false}; +const Info GFX_SHOW_VTIMES{{System::GFX, "Settings", "ShowVTimes"}, false}; +const Info GFX_SHOW_GRAPHS{{System::GFX, "Settings", "ShowGraphs"}, false}; const Info GFX_SHOW_SPEED{{System::GFX, "Settings", "ShowSpeed"}, false}; const Info GFX_SHOW_SPEED_COLORS{{System::GFX, "Settings", "ShowSpeedColors"}, true}; const Info GFX_PERF_SAMP_WINDOW{{System::GFX, "Settings", "PerfSampWindowMS"}, 1000}; diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 601ae56263..df97bd12c2 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -30,7 +30,10 @@ extern const Info GFX_SUGGESTED_ASPECT_RATIO; extern const Info GFX_CROP; extern const Info GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES; extern const Info GFX_SHOW_FPS; +extern const Info GFX_SHOW_FTIMES; extern const Info GFX_SHOW_VPS; +extern const Info GFX_SHOW_VTIMES; +extern const Info GFX_SHOW_GRAPHS; extern const Info GFX_SHOW_SPEED; extern const Info GFX_SHOW_SPEED_COLORS; extern const Info GFX_PERF_SAMP_WINDOW; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 5607a80dc2..9a65f3af95 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -86,6 +86,7 @@ #include "VideoCommon/Fifo.h" #include "VideoCommon/HiresTextures.h" #include "VideoCommon/OnScreenDisplay.h" +#include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" @@ -100,8 +101,6 @@ static bool s_wants_determinism; // Declarations and definitions static Common::Timer s_timer; static u64 s_timer_offset; -static std::atomic s_drawn_frame; -static std::atomic s_drawn_video; static bool s_is_stopping = false; static bool s_hardware_initialized = false; @@ -347,6 +346,9 @@ static void CpuThread(const std::optional& savestate_path, bool del // This needs to be delayed until after the video backend is ready. DolphinAnalytics::Instance().ReportGameStart(); + // Clear performance data collected from previous threads. + g_perf_metrics.Reset(); + #ifdef ANDROID // For some reason, calling the JNI function AttachCurrentThread from the CPU thread after a // certain point causes a crash if fastmem is enabled. Let's call it early to avoid that problem. @@ -843,19 +845,15 @@ void RunOnCPUThread(std::function function, bool wait_for_completion) // This should only be called from VI void VideoThrottle() { + g_perf_metrics.CountVBlank(); + // Update info per second u64 elapsed_ms = s_timer.ElapsedMs(); - if ((elapsed_ms >= 1000 && s_drawn_video.load() > 0) || s_frame_step) + if ((elapsed_ms >= 500) || s_frame_step) { s_timer.Start(); - - UpdateTitle(elapsed_ms); - - s_drawn_frame.store(0); - s_drawn_video.store(0); + UpdateTitle(); } - - s_drawn_video++; } // --- Callbacks for backends / engine --- @@ -864,9 +862,9 @@ void VideoThrottle() // frame is presented to the host screen void Callback_FramePresented(double actual_emulation_speed) { - s_last_actual_emulation_speed = actual_emulation_speed; + g_perf_metrics.CountFrame(); - s_drawn_frame++; + s_last_actual_emulation_speed = actual_emulation_speed; s_stop_frame_step.store(true); } @@ -891,15 +889,11 @@ void Callback_NewField() } } -void UpdateTitle(u64 elapsed_ms) +void UpdateTitle() { - if (elapsed_ms == 0) - elapsed_ms = 1; - - float FPS = (float)(s_drawn_frame.load() * 1000.0 / elapsed_ms); - float VPS = (float)(s_drawn_video.load() * 1000.0 / elapsed_ms); - float Speed = (float)(s_drawn_video.load() * (100 * 1000.0) / - (VideoInterface::GetTargetRefreshRate() * elapsed_ms)); + float FPS = g_perf_metrics.GetFPS(); + float VPS = g_perf_metrics.GetVPS(); + float Speed = g_perf_metrics.GetSpeed(); // Settings are shown the same for both extended and summary info const std::string SSettings = fmt::format( @@ -956,15 +950,6 @@ void UpdateTitle(u64 elapsed_ms) message += " | " + title; } - // Update the audio timestretcher with the current speed - auto& system = Core::System::GetInstance(); - SoundStream* sound_stream = system.GetSoundStream(); - if (sound_stream) - { - Mixer* mixer = sound_stream->GetMixer(); - mixer->UpdateSpeed((float)Speed / 100); - } - Host_UpdateTitle(message); } diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 54c7692e39..89aad7d2de 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -126,7 +126,7 @@ void OnFrameEnd(); void VideoThrottle(); -void UpdateTitle(u64 elapsed_ms); +void UpdateTitle(); // Run a function as the CPU thread. // diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 14b6f1b5cc..fefb6b5fe4 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -637,7 +637,6 @@ - @@ -672,6 +671,8 @@ + + @@ -1244,7 +1245,6 @@ - @@ -1272,6 +1272,8 @@ + + diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index 79bd9c7509..31f8153c2e 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -377,6 +377,7 @@ PRIVATE Qt${QT_VERSION_MAJOR}::Widgets uicommon imgui + implot ) if (WIN32) diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp index bd51c9e372..ebc08a9345 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp @@ -51,7 +51,10 @@ void AdvancedWidget::CreateWidgets() performance_box->setLayout(performance_layout); m_show_fps = new GraphicsBool(tr("Show FPS"), Config::GFX_SHOW_FPS); + m_show_ftimes = new GraphicsBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES); m_show_vps = new GraphicsBool(tr("Show VPS"), Config::GFX_SHOW_VPS); + m_show_vtimes = new GraphicsBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES); + m_show_graphs = new GraphicsBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS); m_show_speed = new GraphicsBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED); m_show_speed_colors = new GraphicsBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS); m_perf_samp_window = new GraphicsInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, 100); @@ -59,12 +62,15 @@ void AdvancedWidget::CreateWidgets() new GraphicsBool(tr("Log Render Time to File"), Config::GFX_LOG_RENDER_TIME_TO_FILE); performance_layout->addWidget(m_show_fps, 0, 0); + performance_layout->addWidget(m_show_ftimes, 0, 1); performance_layout->addWidget(m_show_vps, 1, 0); - performance_layout->addWidget(m_show_speed, 0, 1); - performance_layout->addWidget(new QLabel(tr("Performance Sample Window (ms):")), 2, 0); - performance_layout->addWidget(m_perf_samp_window, 2, 1); - performance_layout->addWidget(m_log_render_time, 3, 0); - performance_layout->addWidget(m_show_speed_colors, 3, 1); + performance_layout->addWidget(m_show_vtimes, 1, 1); + performance_layout->addWidget(m_show_speed, 2, 0); + performance_layout->addWidget(m_show_graphs, 2, 1); + performance_layout->addWidget(new QLabel(tr("Performance Sample Window (ms):")), 3, 0); + performance_layout->addWidget(m_perf_samp_window, 3, 1); + performance_layout->addWidget(m_log_render_time, 4, 0); + performance_layout->addWidget(m_show_speed_colors, 4, 1); // Debugging auto* debugging_box = new QGroupBox(tr("Debugging")); @@ -240,10 +246,22 @@ void AdvancedWidget::AddDescriptions() QT_TR_NOOP("Shows the number of distinct frames rendered per second as a measure of " "visual smoothness.

If unsure, leave this " "unchecked."); + static const char TR_SHOW_FTIMES_DESCRIPTION[] = + QT_TR_NOOP("Shows the average time in ms between each distinct rendered frame alongside " + "the standard deviation.

If unsure, leave this " + "unchecked."); static const char TR_SHOW_VPS_DESCRIPTION[] = QT_TR_NOOP("Shows the number of frames rendered per second as a measure of " "emulation speed.

If unsure, leave this " "unchecked."); + static const char TR_SHOW_VTIMES_DESCRIPTION[] = + QT_TR_NOOP("Shows the average time in ms between each rendered frame alongside " + "the standard deviation.

If unsure, leave this " + "unchecked."); + static const char TR_SHOW_GRAPHS_DESCRIPTION[] = + QT_TR_NOOP("Shows frametime graph along with statistics as a representation of " + "emulation performance.

If unsure, leave this " + "unchecked."); static const char TR_SHOW_SPEED_DESCRIPTION[] = QT_TR_NOOP("Shows the % speed of emulation compared to full speed." "

If unsure, leave this " @@ -380,7 +398,10 @@ void AdvancedWidget::AddDescriptions() QT_TR_NOOP("If unsure, leave this unchecked."); m_show_fps->SetDescription(tr(TR_SHOW_FPS_DESCRIPTION)); + m_show_ftimes->SetDescription(tr(TR_SHOW_FTIMES_DESCRIPTION)); m_show_vps->SetDescription(tr(TR_SHOW_VPS_DESCRIPTION)); + m_show_vtimes->SetDescription(tr(TR_SHOW_VTIMES_DESCRIPTION)); + m_show_graphs->SetDescription(tr(TR_SHOW_GRAPHS_DESCRIPTION)); m_show_speed->SetDescription(tr(TR_SHOW_SPEED_DESCRIPTION)); m_log_render_time->SetDescription(tr(TR_LOG_RENDERTIME_DESCRIPTION)); m_show_speed_colors->SetDescription(tr(TR_SHOW_SPEED_COLORS_DESCRIPTION)); diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h index 58b7107cbb..156cf568d8 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h @@ -36,7 +36,10 @@ private: GraphicsBool* m_enable_format_overlay; GraphicsBool* m_enable_api_validation; GraphicsBool* m_show_fps; + GraphicsBool* m_show_ftimes; GraphicsBool* m_show_vps; + GraphicsBool* m_show_vtimes; + GraphicsBool* m_show_graphs; GraphicsBool* m_show_speed; GraphicsBool* m_show_speed_colors; GraphicsInteger* m_perf_samp_window; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index 3150cb49f9..85949f6a5f 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -425,6 +425,7 @@ + diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index fc34f7a378..a7a52c5e40 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -27,8 +27,6 @@ add_library(videocommon DriverDetails.h Fifo.cpp Fifo.h - FPSCounter.cpp - FPSCounter.h FramebufferManager.cpp FramebufferManager.h FramebufferShaderGen.cpp @@ -85,6 +83,10 @@ add_library(videocommon OpcodeDecoding.h PerfQueryBase.cpp PerfQueryBase.h + PerformanceMetrics.cpp + PerformanceMetrics.h + PerformanceTracker.cpp + PerformanceTracker.h PixelEngine.cpp PixelEngine.h PixelShaderGen.cpp @@ -168,6 +170,7 @@ PRIVATE spng xxhash imgui + implot glslang ) diff --git a/Source/Core/VideoCommon/FPSCounter.cpp b/Source/Core/VideoCommon/FPSCounter.cpp deleted file mode 100644 index e5f396a094..0000000000 --- a/Source/Core/VideoCommon/FPSCounter.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2012 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "VideoCommon/FPSCounter.h" - -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/FileUtil.h" -#include "Common/Timer.h" -#include "Core/Core.h" -#include "VideoCommon/VideoConfig.h" - -static constexpr double US_TO_MS = 1000.0; -static constexpr double US_TO_S = 1000000.0; - -static constexpr double FPS_SAMPLE_RC_RATIO = 0.25; - -FPSCounter::FPSCounter(const char* log_name) -{ - m_last_time = Common::Timer::NowUs(); - m_log_name = log_name; - - m_on_state_changed_handle = Core::AddOnStateChangedCallback([this](Core::State state) { - if (state == Core::State::Paused) - SetPaused(true); - else if (state == Core::State::Running) - SetPaused(false); - }); -} - -FPSCounter::~FPSCounter() -{ - Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle); -} - -void FPSCounter::LogRenderTimeToFile(s64 val) -{ - if (!m_bench_file.is_open()) - { - File::OpenFStream(m_bench_file, File::GetUserPath(D_LOGS_IDX) + m_log_name, std::ios_base::out); - } - - m_bench_file << std::fixed << std::setprecision(8) << (val / US_TO_MS) << std::endl; -} - -void FPSCounter::Update() -{ - if (m_paused) - return; - - const s64 time = Common::Timer::NowUs(); - const s64 diff = std::max(0, time - m_last_time); - const s64 window = std::max(1, g_ActiveConfig.iPerfSampleUSec); - - m_raw_dt = diff / US_TO_S; - m_last_time = time; - - m_dt_total += diff; - m_dt_queue.push_back(diff); - - while (window <= m_dt_total - m_dt_queue.front()) - { - m_dt_total -= m_dt_queue.front(); - m_dt_queue.pop_front(); - } - - // This frame count takes into account frames that are partially in the sample window - const double fps = (m_dt_queue.size() * US_TO_S) / m_dt_total; - const double rc = FPS_SAMPLE_RC_RATIO * std::min(window, m_dt_total) / US_TO_S; - const double a = std::max(0.0, 1.0 - std::exp(-m_raw_dt / rc)); - - // Sometimes euler averages can break when the average is inf/nan - // This small check makes sure that if it does break, it gets fixed - if (std::isfinite(m_avg_fps)) - m_avg_fps += a * (fps - m_avg_fps); - else - m_avg_fps = fps; - - if (g_ActiveConfig.bLogRenderTimeToFile) - LogRenderTimeToFile(diff); -} - -void FPSCounter::SetPaused(bool paused) -{ - m_paused = paused; - if (m_paused) - { - m_last_time_pause = Common::Timer::NowUs(); - } - else - { - const s64 time = Common::Timer::NowUs(); - const s64 diff = time - m_last_time_pause; - m_last_time += diff; - } -} diff --git a/Source/Core/VideoCommon/FPSCounter.h b/Source/Core/VideoCommon/FPSCounter.h deleted file mode 100644 index 852c39fd3b..0000000000 --- a/Source/Core/VideoCommon/FPSCounter.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include - -#include "Common/CommonTypes.h" - -class FPSCounter -{ -public: - explicit FPSCounter(const char* log_name = "log.txt"); - ~FPSCounter(); - FPSCounter(const FPSCounter&) = delete; - FPSCounter& operator=(const FPSCounter&) = delete; - FPSCounter(FPSCounter&&) = delete; - FPSCounter& operator=(FPSCounter&&) = delete; - - // Called when a frame is rendered (updated every second). - void Update(); - - double GetFPS() const { return m_avg_fps; } - - double GetDeltaTime() const { return m_raw_dt; } - -private: - void LogRenderTimeToFile(s64 val); - void SetPaused(bool paused); - - const char* m_log_name; - std::ofstream m_bench_file; - - bool m_paused = false; - s64 m_last_time = 0; - - double m_avg_fps = 0.0; - double m_raw_dt = 0.0; - - s64 m_dt_total = 0; - std::deque m_dt_queue; - - int m_on_state_changed_handle = -1; - s64 m_last_time_pause = 0; -}; diff --git a/Source/Core/VideoCommon/PerformanceMetrics.cpp b/Source/Core/VideoCommon/PerformanceMetrics.cpp new file mode 100644 index 0000000000..3b9cc81791 --- /dev/null +++ b/Source/Core/VideoCommon/PerformanceMetrics.cpp @@ -0,0 +1,221 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/PerformanceMetrics.h" + +#include +#include + +#include "Core/HW/VideoInterface.h" +#include "VideoCommon/VideoConfig.h" + +PerformanceMetrics g_perf_metrics; + +void PerformanceMetrics::Reset() +{ + m_fps_counter.Reset(); + m_vps_counter.Reset(); + m_speed_counter.Reset(); +} + +void PerformanceMetrics::CountFrame() +{ + m_fps_counter.Count(); +} + +void PerformanceMetrics::CountVBlank() +{ + m_vps_counter.Count(); + m_speed_counter.Count(); +} + +double PerformanceMetrics::GetFPS() const +{ + return m_fps_counter.GetHzAvg(); +} + +double PerformanceMetrics::GetVPS() const +{ + return m_vps_counter.GetHzAvg(); +} + +double PerformanceMetrics::GetSpeed() const +{ + return 100.0 * m_speed_counter.GetHzAvg() / VideoInterface::GetTargetRefreshRate(); +} + +double PerformanceMetrics::GetLastSpeedDenominator() const +{ + return DT_s(m_speed_counter.GetLastRawDt()).count() * VideoInterface::GetTargetRefreshRate(); +} + +void PerformanceMetrics::DrawImGuiStats(const float backbuffer_scale) const +{ + const float bg_alpha = 0.7f; + const auto imgui_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | + ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing; + + const double fps = GetFPS(); + const double vps = GetVPS(); + const double speed = GetSpeed(); + + // Change Color based on % Speed + float r = 0.0f, g = 1.0f, b = 1.0f; + if (g_ActiveConfig.bShowSpeedColors) + { + r = 1.0 - (speed - 80.0) / 20.0; + g = speed / 80.0; + b = (speed - 90.0) / 10.0; + } + + const float window_padding = 8.f * backbuffer_scale; + const float window_width = 93.f * backbuffer_scale; + float window_y = window_padding; + float window_x = ImGui::GetIO().DisplaySize.x - window_padding; + + const float graph_width = 50.f * backbuffer_scale + 3.f * window_width + 2.f * window_padding; + const float graph_height = + std::min(200.f * backbuffer_scale, ImGui::GetIO().DisplaySize.y - 85.f * backbuffer_scale); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 14.f * backbuffer_scale); + if (g_ActiveConfig.bShowGraphs) + { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 4.f * backbuffer_scale)); + + // Position in the top-right corner of the screen. + ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(graph_width, graph_height)); + ImGui::SetNextWindowBgAlpha(bg_alpha); + window_y += graph_height + window_padding; + + if (ImGui::Begin("PerformanceGraphs", nullptr, imgui_flags)) + { + const static int num_ticks = 17; + const static double tick_marks[num_ticks] = {0.0, + 1000.0 / 360.0, + 1000.0 / 240.0, + 1000.0 / 180.0, + 1000.0 / 120.0, + 1000.0 / 90.00, + 1000.0 / 59.94, + 1000.0 / 40.00, + 1000.0 / 29.97, + 1000.0 / 24.00, + 1000.0 / 20.00, + 1000.0 / 15.00, + 1000.0 / 10.00, + 1000.0 / 5.000, + 1000.0 / 2.000, + 1000.0, + 2000.0}; + + const DT vblank_time = m_vps_counter.GetDtAvg() + m_vps_counter.GetDtStd(); + const DT frame_time = m_fps_counter.GetDtAvg() + m_fps_counter.GetDtStd(); + const double target_max_time = DT_ms(vblank_time + frame_time).count(); + const double a = + std::max(0.0, 1.0 - std::exp(-4000.0 * m_vps_counter.GetLastRawDt().count() / + DT_ms(m_vps_counter.GetSampleWindow()).count())); + + static double max_time = 0.0; + if (std::isfinite(max_time)) + max_time += a * (target_max_time - max_time); + else + max_time = target_max_time; + + const double total_frame_time = std::max(DT_ms(m_fps_counter.GetSampleWindow()).count(), + DT_ms(m_vps_counter.GetSampleWindow()).count()); + + if (ImPlot::BeginPlot("PerformanceGraphs", ImVec2(-1.0, -1.0), + ImPlotFlags_NoFrame | ImPlotFlags_NoTitle | ImPlotFlags_NoMenus)) + { + ImPlot::PushStyleColor(ImPlotCol_PlotBg, {0, 0, 0, 0}); + ImPlot::PushStyleColor(ImPlotCol_LegendBg, {0, 0, 0, 0.2f}); + ImPlot::PushStyleVar(ImPlotStyleVar_FitPadding, ImVec2(0.f, 0.f)); + ImPlot::PushStyleVar(ImPlotStyleVar_LineWeight, 3.f); + ImPlot::SetupAxes(nullptr, nullptr, + ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert | + ImPlotAxisFlags_NoDecorations | ImPlotAxisFlags_NoHighlight, + ImPlotAxisFlags_Lock | ImPlotAxisFlags_Invert | ImPlotAxisFlags_NoLabel | + ImPlotAxisFlags_NoHighlight); + ImPlot::SetupAxisFormat(ImAxis_Y1, "%.1f"); + ImPlot::SetupAxisTicks(ImAxis_Y1, tick_marks, num_ticks); + ImPlot::SetupAxesLimits(0, total_frame_time, 0, max_time, ImGuiCond_Always); + ImPlot::SetupLegend(ImPlotLocation_SouthEast, ImPlotLegendFlags_None); + m_vps_counter.ImPlotPlotLines("V-Blank (ms)"); + m_fps_counter.ImPlotPlotLines("Frame (ms)"); + ImPlot::EndPlot(); + ImPlot::PopStyleVar(2); + ImPlot::PopStyleColor(2); + } + ImGui::PopStyleVar(); + ImGui::End(); + } + } + + if (g_ActiveConfig.bShowFPS || g_ActiveConfig.bShowFTimes) + { + // Position in the top-right corner of the screen. + int count = g_ActiveConfig.bShowFPS + 2 * g_ActiveConfig.bShowFTimes; + ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(window_width, (12.f + 17.f * count) * backbuffer_scale)); + ImGui::SetNextWindowBgAlpha(bg_alpha); + window_x -= window_width + window_padding; + + if (ImGui::Begin("FPSStats", nullptr, imgui_flags)) + { + if (g_ActiveConfig.bShowFPS) + ImGui::TextColored(ImVec4(r, g, b, 1.0f), "FPS:%7.2lf", fps); + if (g_ActiveConfig.bShowFTimes) + { + ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms", + DT_ms(m_fps_counter.GetDtAvg()).count()); + ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms", + DT_ms(m_fps_counter.GetDtStd()).count()); + } + ImGui::End(); + } + } + + if (g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowVTimes) + { + // Position in the top-right corner of the screen. + int count = g_ActiveConfig.bShowVPS + 2 * g_ActiveConfig.bShowVTimes; + ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(window_width, (12.f + 17.f * count) * backbuffer_scale)); + ImGui::SetNextWindowBgAlpha(bg_alpha); + window_x -= window_width + window_padding; + + if (ImGui::Begin("VPSStats", nullptr, imgui_flags)) + { + if (g_ActiveConfig.bShowVPS) + ImGui::TextColored(ImVec4(r, g, b, 1.0f), "VPS:%7.2lf", vps); + if (g_ActiveConfig.bShowVTimes) + { + ImGui::TextColored(ImVec4(r, g, b, 1.0f), "dt:%6.2lfms", + DT_ms(m_vps_counter.GetDtAvg()).count()); + ImGui::TextColored(ImVec4(r, g, b, 1.0f), " ±:%6.2lfms", + DT_ms(m_vps_counter.GetDtStd()).count()); + } + ImGui::End(); + } + } + + if (g_ActiveConfig.bShowSpeed) + { + // Position in the top-right corner of the screen. + ImGui::SetNextWindowPos(ImVec2(window_x, window_y), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(window_width, 29.f * backbuffer_scale)); + ImGui::SetNextWindowBgAlpha(bg_alpha); + + if (ImGui::Begin("SpeedStats", nullptr, imgui_flags)) + { + ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Speed:%4.0lf%%", speed); + ImGui::End(); + } + } + + ImGui::PopStyleVar(2); +} diff --git a/Source/Core/VideoCommon/PerformanceMetrics.h b/Source/Core/VideoCommon/PerformanceMetrics.h new file mode 100644 index 0000000000..e5dc87b801 --- /dev/null +++ b/Source/Core/VideoCommon/PerformanceMetrics.h @@ -0,0 +1,40 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "VideoCommon/PerformanceTracker.h" + +class PerformanceMetrics +{ +public: + PerformanceMetrics() = default; + ~PerformanceMetrics() = default; + + PerformanceMetrics(const PerformanceMetrics&) = delete; + PerformanceMetrics& operator=(const PerformanceMetrics&) = delete; + PerformanceMetrics(PerformanceMetrics&&) = delete; + PerformanceMetrics& operator=(PerformanceMetrics&&) = delete; + +public: // Count Functions + void Reset(); + void CountFrame(); + void CountVBlank(); + +public: // Getter Functions + double GetFPS() const; + double GetVPS() const; + double GetSpeed() const; + + double GetLastSpeedDenominator() const; + +public: // ImGui Functions + void DrawImGuiStats(const float backbuffer_scale) const; + +private: // Member Variables + PerformanceTracker m_fps_counter{"render_times.txt"}; + PerformanceTracker m_vps_counter{"vblank_times.txt"}; + PerformanceTracker m_speed_counter{nullptr, 6000000}; +}; + +extern PerformanceMetrics g_perf_metrics; diff --git a/Source/Core/VideoCommon/PerformanceTracker.cpp b/Source/Core/VideoCommon/PerformanceTracker.cpp new file mode 100644 index 0000000000..ffde157883 --- /dev/null +++ b/Source/Core/VideoCommon/PerformanceTracker.cpp @@ -0,0 +1,252 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "VideoCommon/PerformanceTracker.h" + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/FileUtil.h" +#include "Common/Timer.h" +#include "Core/Core.h" +#include "VideoCommon/VideoConfig.h" + +static constexpr double SAMPLE_RC_RATIO = 0.25; + +PerformanceTracker::PerformanceTracker(const char* log_name, + const std::optional sample_window_us) + : m_on_state_changed_handle{Core::AddOnStateChangedCallback([this](Core::State state) { + if (state == Core::State::Paused) + SetPaused(true); + else if (state == Core::State::Running) + SetPaused(false); + })}, + m_log_name{log_name}, m_sample_window_us{sample_window_us} +{ + Reset(); +} + +PerformanceTracker::~PerformanceTracker() +{ + Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle); +} + +void PerformanceTracker::Reset() +{ + std::lock_guard lock{m_mutex}; + + QueueClear(); + m_last_time = Clock::now(); + m_hz_avg = 0.0; + m_dt_avg = DT::zero(); + m_dt_std = std::nullopt; +} + +void PerformanceTracker::Count() +{ + std::lock_guard lock{m_mutex}; + + if (m_paused) + return; + + const DT window{GetSampleWindow()}; + + const TimePoint time{Clock::now()}; + const DT diff{time - m_last_time}; + + m_last_time = time; + + QueuePush(diff); + m_dt_total += diff; + + if (m_dt_queue_begin == m_dt_queue_end) + m_dt_total -= QueuePop(); + + while (window <= m_dt_total - QueueTop()) + m_dt_total -= QueuePop(); + + // Simple Moving Average Throughout the Window + m_dt_avg = m_dt_total / QueueSize(); + const double hz = DT_s(1.0) / m_dt_avg; + + // Exponential Moving Average + const DT_s rc = SAMPLE_RC_RATIO * std::min(window, m_dt_total); + const double a = 1.0 - std::exp(-(DT_s(diff) / rc)); + + // Sometimes euler averages can break when the average is inf/nan + if (std::isfinite(m_hz_avg)) + m_hz_avg += a * (hz - m_hz_avg); + else + m_hz_avg = hz; + + m_dt_std = std::nullopt; + + if (m_log_name && g_ActiveConfig.bLogRenderTimeToFile) + LogRenderTimeToFile(diff); +} + +DT PerformanceTracker::GetSampleWindow() const +{ + // This reads a constant value and thus does not need a mutex + return std::chrono::duration_cast
( + DT_us(m_sample_window_us.value_or(std::max(1, g_ActiveConfig.iPerfSampleUSec)))); +} + +double PerformanceTracker::GetHzAvg() const +{ + std::lock_guard lock{m_mutex}; + return m_hz_avg; +} + +DT PerformanceTracker::GetDtAvg() const +{ + std::lock_guard lock{m_mutex}; + return m_dt_avg; +} + +DT PerformanceTracker::GetDtStd() const +{ + std::lock_guard lock{m_mutex}; + + if (m_dt_std) + return *m_dt_std; + + if (QueueEmpty()) + return *(m_dt_std = DT::zero()); + + double total = 0.0; + for (std::size_t i = m_dt_queue_begin; i != m_dt_queue_end; i = IncrementIndex(i)) + { + double diff = DT_s(m_dt_queue[i] - m_dt_avg).count(); + total += diff * diff; + } + + // This is a weighted standard deviation + return *(m_dt_std = std::chrono::duration_cast
(DT_s(std::sqrt(total / QueueSize())))); +} + +DT PerformanceTracker::GetLastRawDt() const +{ + std::lock_guard lock{m_mutex}; + + if (QueueEmpty()) + return DT::zero(); + + return QueueBottom(); +} + +void PerformanceTracker::ImPlotPlotLines(const char* label) const +{ + static std::array x, y; + + std::lock_guard lock{m_mutex}; + + if (QueueEmpty()) + return; + + // Decides if there are too many points to plot using rectangles + const bool quality = QueueSize() < MAX_QUALITY_GRAPH_SIZE; + + const DT update_time = Clock::now() - m_last_time; + const float predicted_frame_time = DT_ms(std::max(update_time, QueueBottom())).count(); + + std::size_t points = 0; + if (quality) + { + x[points] = 0.f; + y[points] = predicted_frame_time; + ++points; + } + + x[points] = DT_ms(update_time).count(); + y[points] = predicted_frame_time; + ++points; + + const std::size_t begin = DecrementIndex(m_dt_queue_end); + const std::size_t end = DecrementIndex(m_dt_queue_begin); + for (std::size_t i = begin; i != end; i = DecrementIndex(i)) + { + const float frame_time_ms = DT_ms(m_dt_queue[i]).count(); + + if (quality) + { + x[points] = x[points - 1]; + y[points] = frame_time_ms; + ++points; + } + + x[points] = x[points - 1] + frame_time_ms; + y[points] = frame_time_ms; + ++points; + } + + ImPlot::PlotLine(label, x.data(), y.data(), static_cast(points)); +} + +void PerformanceTracker::QueueClear() +{ + m_dt_total = DT::zero(); + m_dt_queue_begin = 0; + m_dt_queue_end = 0; +} + +void PerformanceTracker::QueuePush(DT dt) +{ + m_dt_queue[m_dt_queue_end] = dt; + m_dt_queue_end = IncrementIndex(m_dt_queue_end); +} + +const DT& PerformanceTracker::QueuePop() +{ + const std::size_t top = m_dt_queue_begin; + m_dt_queue_begin = IncrementIndex(m_dt_queue_begin); + return m_dt_queue[top]; +} + +const DT& PerformanceTracker::QueueTop() const +{ + return m_dt_queue[m_dt_queue_begin]; +} + +const DT& PerformanceTracker::QueueBottom() const +{ + return m_dt_queue[DecrementIndex(m_dt_queue_end)]; +} + +std::size_t PerformanceTracker::QueueSize() const +{ + return GetDifference(m_dt_queue_begin, m_dt_queue_end); +} + +bool PerformanceTracker::QueueEmpty() const +{ + return m_dt_queue_begin == m_dt_queue_end; +} + +void PerformanceTracker::LogRenderTimeToFile(DT val) +{ + if (!m_bench_file.is_open()) + { + File::OpenFStream(m_bench_file, File::GetUserPath(D_LOGS_IDX) + m_log_name, std::ios_base::out); + } + + m_bench_file << std::fixed << std::setprecision(8) << DT_ms(val).count() << std::endl; +} + +void PerformanceTracker::SetPaused(bool paused) +{ + std::lock_guard lock{m_mutex}; + + m_paused = paused; + if (m_paused) + { + m_last_time = TimePoint::max(); + } + else + { + m_last_time = Clock::now(); + } +} diff --git a/Source/Core/VideoCommon/PerformanceTracker.h b/Source/Core/VideoCommon/PerformanceTracker.h new file mode 100644 index 0000000000..d1b6ce01ee --- /dev/null +++ b/Source/Core/VideoCommon/PerformanceTracker.h @@ -0,0 +1,105 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +class PerformanceTracker +{ +private: + // Must be powers of 2 for masking to work + static constexpr u64 MAX_DT_QUEUE_SIZE = 1UL << 12; + static constexpr u64 MAX_QUALITY_GRAPH_SIZE = 1UL << 8; + + static inline std::size_t IncrementIndex(const std::size_t index) + { + return (index + 1) & (MAX_DT_QUEUE_SIZE - 1); + } + + static inline std::size_t DecrementIndex(const std::size_t index) + { + return (index - 1) & (MAX_DT_QUEUE_SIZE - 1); + } + + static inline std::size_t GetDifference(const std::size_t begin, const std::size_t end) + { + return (end - begin) & (MAX_DT_QUEUE_SIZE - 1); + } + +public: + PerformanceTracker(const char* log_name = nullptr, + const std::optional sample_window_us = {}); + ~PerformanceTracker(); + + PerformanceTracker(const PerformanceTracker&) = delete; + PerformanceTracker& operator=(const PerformanceTracker&) = delete; + PerformanceTracker(PerformanceTracker&&) = delete; + PerformanceTracker& operator=(PerformanceTracker&&) = delete; + +public: // Functions for recording and accessing information + void Reset(); + void Count(); + + DT GetSampleWindow() const; + + double GetHzAvg() const; + + DT GetDtAvg() const; + DT GetDtStd() const; + + DT GetLastRawDt() const; + + void ImPlotPlotLines(const char* label) const; + +private: // Functions for managing dt queue + inline void QueueClear(); + inline void QueuePush(DT dt); + inline const DT& QueuePop(); + inline const DT& QueueTop() const; + inline const DT& QueueBottom() const; + + std::size_t inline QueueSize() const; + bool inline QueueEmpty() const; + +private: // Handle pausing and logging + void LogRenderTimeToFile(DT val); + void SetPaused(bool paused); + +private: + // Handle not counting time during pauses + bool m_paused = false; + int m_on_state_changed_handle; + + // Name of log file and file stream + const char* m_log_name; + std::ofstream m_bench_file; + + // Last time Count() was called + TimePoint m_last_time; + + // Amount of time to sample dt's over (defaults to config) + const std::optional m_sample_window_us; + + // Queue + Running Total used to calculate average dt + DT m_dt_total = DT::zero(); + std::array m_dt_queue; + std::size_t m_dt_queue_begin = 0; + std::size_t m_dt_queue_end = 0; + + // Average rate/time throughout the window + DT m_dt_avg = DT::zero(); // Uses Moving Average + double m_hz_avg = 0.0; // Uses Moving Average + Euler Average + + // Used to initialize this on demand instead of on every Count() + mutable std::optional
m_dt_std = std::nullopt; + + // Used to enable thread safety with the performance tracker + mutable std::mutex m_mutex; +}; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index f6322bc36f..43e7de5e10 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -23,6 +23,7 @@ #include #include +#include #include "Common/Assert.h" #include "Common/ChunkFile.h" @@ -63,7 +64,6 @@ #include "VideoCommon/BoundingBox.h" #include "VideoCommon/CPMemory.h" #include "VideoCommon/CommandProcessor.h" -#include "VideoCommon/FPSCounter.h" #include "VideoCommon/FrameDump.h" #include "VideoCommon/FramebufferManager.h" #include "VideoCommon/FramebufferShaderGen.h" @@ -592,46 +592,6 @@ void Renderer::CheckForConfigChanges() // Create On-Screen-Messages void Renderer::DrawDebugText() { - if (g_ActiveConfig.bShowFPS || g_ActiveConfig.bShowVPS || g_ActiveConfig.bShowSpeed) - { - // Position in the top-right corner of the screen. - ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x - (10.0f * m_backbuffer_scale), - 10.f * m_backbuffer_scale), - ImGuiCond_Always, ImVec2(1.0f, 0.0f)); - - int count = g_ActiveConfig.bShowFPS + g_ActiveConfig.bShowVPS + g_ActiveConfig.bShowSpeed; - ImGui::SetNextWindowSize( - ImVec2(94.f * m_backbuffer_scale, (12.f + 17.f * count) * m_backbuffer_scale)); - - if (ImGui::Begin("Performance", nullptr, - ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoInputs | - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | - ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | - ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) - { - const double fps = m_fps_counter.GetFPS(); - const double vps = m_vps_counter.GetFPS(); - const double speed = 100.0 * vps / VideoInterface::GetTargetRefreshRate(); - - // Change Color based on % Speed - float r = 0.0f, g = 1.0f, b = 1.0f; - if (g_ActiveConfig.bShowSpeedColors) - { - r = 1.0 - (speed - 80.0) / 20.0; - g = speed / 80.0; - b = (speed - 90.0) / 10.0; - } - - if (g_ActiveConfig.bShowFPS) - ImGui::TextColored(ImVec4(r, g, b, 1.0f), "FPS:%7.2lf", fps); - if (g_ActiveConfig.bShowVPS) - ImGui::TextColored(ImVec4(r, g, b, 1.0f), "VPS:%7.2lf", vps); - if (g_ActiveConfig.bShowSpeed) - ImGui::TextColored(ImVec4(r, g, b, 1.0f), "Speed:%4.0lf%%", speed); - } - ImGui::End(); - } - const bool show_movie_window = Config::Get(Config::MAIN_SHOW_FRAME_COUNT) || Config::Get(Config::MAIN_SHOW_LAG) || Config::Get(Config::MAIN_MOVIE_SHOW_INPUT_DISPLAY) || @@ -1046,6 +1006,11 @@ bool Renderer::InitializeImGui() PanicAlertFmt("Creating ImGui context failed"); return false; } + if (!ImPlot::CreateContext()) + { + PanicAlertFmt("Creating ImPlot context failed"); + return false; + } // Don't create an ini file. TODO: Do we want this in the future? ImGui::GetIO().IniFilename = nullptr; @@ -1159,6 +1124,7 @@ void Renderer::ShutdownImGui() std::unique_lock imgui_lock(m_imgui_mutex); ImGui::EndFrame(); + ImPlot::DestroyContext(); ImGui::DestroyContext(); m_imgui_pipeline.reset(); m_imgui_vertex_format.reset(); @@ -1390,10 +1356,6 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 g_texture_cache->GetXFBTexture(xfb_addr, fb_width, fb_height, fb_stride, &xfb_rect); const bool is_duplicate_frame = xfb_entry->id == m_last_xfb_id; - m_vps_counter.Update(); - if (!is_duplicate_frame) - m_fps_counter.Update(); - if (xfb_entry && (!g_ActiveConfig.bSkipPresentingDuplicateXFBs || !is_duplicate_frame)) { m_last_xfb_id = xfb_entry->id; @@ -1407,6 +1369,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 { auto lock = GetImGuiLock(); + g_perf_metrics.DrawImGuiStats(m_backbuffer_scale); DrawDebugText(); OSD::DrawMessages(); ImGui::Render(); @@ -1485,8 +1448,7 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6 { // Remove stale EFB/XFB copies. g_texture_cache->Cleanup(m_frame_count); - const double last_speed_denominator = - m_fps_counter.GetDeltaTime() * VideoInterface::GetTargetRefreshRate(); + const double last_speed_denominator = g_perf_metrics.GetLastSpeedDenominator(); // The denominator should always be > 0 but if it's not, just return 1 const double last_speed = last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0; diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index c44e2ebabe..e45cc244c1 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -28,9 +28,9 @@ #include "Common/MathUtil.h" #include "VideoCommon/AsyncShaderCompiler.h" #include "VideoCommon/BPMemory.h" -#include "VideoCommon/FPSCounter.h" #include "VideoCommon/FrameDump.h" #include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h" +#include "VideoCommon/PerformanceMetrics.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/TextureConfig.h" @@ -338,9 +338,6 @@ protected: MathUtil::Rectangle m_target_rectangle = {}; int m_frame_count = 0; - FPSCounter m_fps_counter = FPSCounter("render_times.txt"); - FPSCounter m_vps_counter = FPSCounter("v_blank_times.txt"); - std::unique_ptr m_post_processor; void* m_new_surface_handle = nullptr; diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 9769b46668..66ba09c581 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -64,7 +64,10 @@ void VideoConfig::Refresh() bCrop = Config::Get(Config::GFX_CROP); iSafeTextureCache_ColorSamples = Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES); bShowFPS = Config::Get(Config::GFX_SHOW_FPS); + bShowFTimes = Config::Get(Config::GFX_SHOW_FTIMES); bShowVPS = Config::Get(Config::GFX_SHOW_VPS); + bShowVTimes = Config::Get(Config::GFX_SHOW_VTIMES); + bShowGraphs = Config::Get(Config::GFX_SHOW_GRAPHS); bShowSpeed = Config::Get(Config::GFX_SHOW_SPEED); bShowSpeedColors = Config::Get(Config::GFX_SHOW_SPEED_COLORS); iPerfSampleUSec = Config::Get(Config::GFX_PERF_SAMP_WINDOW) * 1000; diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index b1b776ab80..d60f969b8d 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -89,7 +89,10 @@ struct VideoConfig final // Information bool bShowFPS = false; + bool bShowFTimes = false; bool bShowVPS = false; + bool bShowVTimes = false; + bool bShowGraphs = false; bool bShowSpeed = false; bool bShowSpeedColors = false; int iPerfSampleUSec = 0;