diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ee052d70a..07a2740fc 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -102,6 +102,8 @@ add_library(core
pad.h
pcdrv.cpp
pcdrv.h
+ performance_counters.cpp
+ performance_counters.h
playstation_mouse.cpp
playstation_mouse.h
psf_loader.cpp
diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj
index 1aa26de27..8b12fec78 100644
--- a/src/core/core.vcxproj
+++ b/src/core/core.vcxproj
@@ -79,6 +79,7 @@
Create
+
@@ -160,6 +161,7 @@
+
diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters
index 38904fbac..b0d8a5369 100644
--- a/src/core/core.vcxproj.filters
+++ b/src/core/core.vcxproj.filters
@@ -70,6 +70,7 @@
+
@@ -146,6 +147,7 @@
+
diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp
index c5a12b650..405a6ce20 100644
--- a/src/core/gpu.cpp
+++ b/src/core/gpu.cpp
@@ -8,6 +8,7 @@
#include "gpu_sw_rasterizer.h"
#include "host.h"
#include "interrupt_controller.h"
+#include "performance_counters.h"
#include "settings.h"
#include "system.h"
#include "timers.h"
@@ -146,10 +147,10 @@ void GPU::UpdateSettings(const Settings& old_settings)
if (g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode)
DestroyDeinterlaceTextures();
- if (!CompileDisplayPipelines(g_settings.display_scaling != old_settings.display_scaling,
- g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode,
- g_settings.display_24bit_chroma_smoothing !=
- old_settings.display_24bit_chroma_smoothing, nullptr))
+ if (!CompileDisplayPipelines(
+ g_settings.display_scaling != old_settings.display_scaling,
+ g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode,
+ g_settings.display_24bit_chroma_smoothing != old_settings.display_24bit_chroma_smoothing, nullptr))
{
Panic("Failed to compile display pipeline on settings change.");
}
@@ -707,7 +708,7 @@ void GPU::UpdateCRTCConfig()
cs.current_scanline %= cs.vertical_total;
- System::SetThrottleFrequency(ComputeVerticalFrequency());
+ System::SetVideoFrameRate(ComputeVerticalFrequency());
UpdateCRTCDisplayParameters();
UpdateCRTCTickEvent();
@@ -2915,8 +2916,9 @@ bool GPU::StartRecordingGPUDump(const char* path, u32 num_frames /* = 1 */)
// +1 because we want to actually see the buffer swap...
if (num_frames != 0)
{
- num_frames = std::max(num_frames, static_cast(static_cast(num_frames + 1) *
- std::ceil(System::GetVPS() / System::GetFPS())));
+ num_frames =
+ std::max(num_frames, static_cast(static_cast(num_frames + 1) *
+ std::ceil(PerformanceCounters::GetVPS() / PerformanceCounters::GetFPS())));
}
// ensure vram is up to date
diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp
index 7c15b7ad8..a366751c9 100644
--- a/src/core/imgui_overlays.cpp
+++ b/src/core/imgui_overlays.cpp
@@ -11,6 +11,7 @@
#include "gpu.h"
#include "host.h"
#include "mdec.h"
+#include "performance_counters.h"
#include "settings.h"
#include "spu.h"
#include "system.h"
@@ -346,9 +347,9 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, float
const System::State state = System::GetState();
if (state == System::State::Running)
{
- const float speed = System::GetEmulationSpeed();
+ const float speed = PerformanceCounters::GetEmulationSpeed();
if (g_settings.display_show_fps)
- text.append_format("G: {:.2f} | V: {:.2f}", System::GetFPS(), System::GetVPS());
+ text.append_format("G: {:.2f} | V: {:.2f}", PerformanceCounters::GetFPS(), PerformanceCounters::GetVPS());
if (g_settings.display_show_speed)
{
text.append_format("{}{}%", text.empty() ? "" : " | ", static_cast(std::round(speed)));
@@ -400,8 +401,8 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, float
if (g_settings.display_show_cpu_usage)
{
- text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", System::GetMinimumFrameTime(), System::GetAverageFrameTime(),
- System::GetMaximumFrameTime());
+ text.format("{:.2f}ms | {:.2f}ms | {:.2f}ms", PerformanceCounters::GetMinimumFrameTime(),
+ PerformanceCounters::GetAverageFrameTime(), PerformanceCounters::GetMaximumFrameTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
if (g_settings.cpu_overclock_active || CPU::g_state.using_interpreter ||
@@ -450,13 +451,15 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, float
{
text.assign("CPU: ");
}
- FormatProcessorStat(text, System::GetCPUThreadUsage(), System::GetCPUThreadAverageTime());
+ FormatProcessorStat(text, PerformanceCounters::GetCPUThreadUsage(),
+ PerformanceCounters::GetCPUThreadAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
if (g_gpu->GetSWThread())
{
text.assign("SW: ");
- FormatProcessorStat(text, System::GetSWThreadUsage(), System::GetSWThreadAverageTime());
+ FormatProcessorStat(text, PerformanceCounters::GetSWThreadUsage(),
+ PerformanceCounters::GetSWThreadAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
@@ -473,7 +476,7 @@ void ImGuiManager::DrawPerformanceOverlay(float& position_y, float scale, float
if (g_settings.display_show_gpu_usage && g_gpu_device->IsGPUTimingEnabled())
{
text.assign("GPU: ");
- FormatProcessorStat(text, System::GetGPUUsage(), System::GetGPUAverageTime());
+ FormatProcessorStat(text, PerformanceCounters::GetGPUUsage(), PerformanceCounters::GetGPUAverageTime());
DRAW_LINE(fixed_font, text, IM_COL32(255, 255, 255, 255));
}
@@ -645,7 +648,7 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
{
ImGui::PushFont(fixed_font);
- auto [min, max] = GetMinMax(System::GetFrameTimeHistory());
+ auto [min, max] = GetMinMax(PerformanceCounters::GetFrameTimeHistory());
// add a little bit of space either side, so we're not constantly resizing
if ((max - min) < 4.0f)
@@ -659,10 +662,10 @@ void ImGuiManager::DrawFrameTimeOverlay(float& position_y, float scale, float ma
ImGui::PlotEx(
ImGuiPlotType_Lines, "##frame_times",
[](void*, int idx) -> float {
- return System::GetFrameTimeHistory()[((System::GetFrameTimeHistoryPos() + idx) %
- System::NUM_FRAME_TIME_SAMPLES)];
+ return PerformanceCounters::GetFrameTimeHistory()[((PerformanceCounters::GetFrameTimeHistoryPos() + idx) %
+ PerformanceCounters::NUM_FRAME_TIME_SAMPLES)];
},
- nullptr, System::NUM_FRAME_TIME_SAMPLES, 0, nullptr, min, max, history_size);
+ nullptr, PerformanceCounters::NUM_FRAME_TIME_SAMPLES, 0, nullptr, min, max, history_size);
ImDrawList* win_dl = ImGui::GetCurrentWindow()->DrawList;
const ImVec2 wpos(ImGui::GetCurrentWindow()->Pos);
diff --git a/src/core/performance_counters.cpp b/src/core/performance_counters.cpp
new file mode 100644
index 000000000..44059c886
--- /dev/null
+++ b/src/core/performance_counters.cpp
@@ -0,0 +1,243 @@
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+#include "performance_counters.h"
+#include "gpu.h"
+#include "system.h"
+
+#include "util/media_capture.h"
+
+#include "common/log.h"
+#include "common/threading.h"
+#include "common/timer.h"
+
+#include
+
+LOG_CHANNEL(PerfMon);
+
+namespace PerformanceCounters {
+
+namespace {
+
+struct State
+{
+ Common::Timer::Value last_update_time;
+ Common::Timer::Value last_frame_time;
+
+ u32 last_frame_number;
+ u32 last_internal_frame_number;
+ u32 presents_since_last_update;
+
+ float average_frame_time_accumulator;
+ float minimum_frame_time_accumulator;
+ float maximum_frame_time_accumulator;
+
+ float vps;
+ float fps;
+ float speed;
+
+ float minimum_frame_time;
+ float maximum_frame_time;
+ float average_frame_time;
+
+ u64 last_cpu_time;
+ float cpu_thread_usage;
+ float cpu_thread_time;
+
+ u64 last_sw_time;
+ float sw_thread_usage;
+ float sw_thread_time;
+
+ float average_gpu_time;
+ float accumulated_gpu_time;
+ float gpu_usage;
+
+ FrameTimeHistory frame_time_history;
+ u32 frame_time_history_pos;
+};
+
+} // namespace
+
+static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
+
+ALIGN_TO_CACHE_LINE State s_state = {};
+
+} // namespace PerformanceCounters
+
+float PerformanceCounters::GetFPS()
+{
+ return s_state.fps;
+}
+
+float PerformanceCounters::GetVPS()
+{
+ return s_state.vps;
+}
+
+float PerformanceCounters::GetEmulationSpeed()
+{
+ return s_state.speed;
+}
+
+float PerformanceCounters::GetAverageFrameTime()
+{
+ return s_state.average_frame_time;
+}
+
+float PerformanceCounters::GetMinimumFrameTime()
+{
+ return s_state.minimum_frame_time;
+}
+
+float PerformanceCounters::GetMaximumFrameTime()
+{
+ return s_state.maximum_frame_time;
+}
+
+float PerformanceCounters::GetCPUThreadUsage()
+{
+ return s_state.cpu_thread_usage;
+}
+
+float PerformanceCounters::GetCPUThreadAverageTime()
+{
+ return s_state.cpu_thread_time;
+}
+
+float PerformanceCounters::GetSWThreadUsage()
+{
+ return s_state.sw_thread_usage;
+}
+
+float PerformanceCounters::GetSWThreadAverageTime()
+{
+ return s_state.sw_thread_time;
+}
+
+float PerformanceCounters::GetGPUUsage()
+{
+ return s_state.gpu_usage;
+}
+
+float PerformanceCounters::GetGPUAverageTime()
+{
+ return s_state.average_gpu_time;
+}
+
+const PerformanceCounters::FrameTimeHistory& PerformanceCounters::GetFrameTimeHistory()
+{
+ return s_state.frame_time_history;
+}
+
+u32 PerformanceCounters::GetFrameTimeHistoryPos()
+{
+ return s_state.frame_time_history_pos;
+}
+
+void PerformanceCounters::Clear()
+{
+ s_state = {};
+}
+
+void PerformanceCounters::Reset()
+{
+ const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue();
+
+ s_state.last_frame_time = now_ticks;
+ s_state.last_update_time = now_ticks;
+
+ s_state.last_frame_number = System::GetFrameNumber();
+ s_state.last_internal_frame_number = System::GetInternalFrameNumber();
+ s_state.last_cpu_time = System::Internal::GetCPUThreadHandle().GetCPUTime();
+ if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
+ s_state.last_sw_time = sw_thread->GetCPUTime();
+ else
+ s_state.last_sw_time = 0;
+
+ s_state.average_frame_time_accumulator = 0.0f;
+ s_state.minimum_frame_time_accumulator = 0.0f;
+ s_state.maximum_frame_time_accumulator = 0.0f;
+}
+
+void PerformanceCounters::Update(u32 frame_number, u32 internal_frame_number)
+{
+ const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue();
+
+ const float frame_time = static_cast(
+ Common::Timer::ConvertValueToMilliseconds(now_ticks - std::exchange(s_state.last_frame_time, now_ticks)));
+ s_state.minimum_frame_time_accumulator = (s_state.minimum_frame_time_accumulator == 0.0f) ?
+ frame_time :
+ std::min(s_state.minimum_frame_time_accumulator, frame_time);
+ s_state.average_frame_time_accumulator += frame_time;
+ s_state.maximum_frame_time_accumulator = std::max(s_state.maximum_frame_time_accumulator, frame_time);
+ s_state.frame_time_history[s_state.frame_time_history_pos] = frame_time;
+ s_state.frame_time_history_pos = (s_state.frame_time_history_pos + 1) % NUM_FRAME_TIME_SAMPLES;
+
+ // update fps counter
+ const Common::Timer::Value ticks_diff = now_ticks - s_state.last_update_time;
+ const float time = static_cast(Common::Timer::ConvertValueToSeconds(ticks_diff));
+ if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL)
+ return;
+
+ s_state.last_update_time = now_ticks;
+
+ const u32 frames_run = frame_number - std::exchange(s_state.last_frame_number, frame_number);
+ const u32 internal_frames_run =
+ internal_frame_number - std::exchange(s_state.last_internal_frame_number, internal_frame_number);
+ const float frames_runf = static_cast(frames_run);
+
+ // TODO: Make the math here less rubbish
+ const double pct_divider =
+ 100.0 * (1.0 / ((static_cast(ticks_diff) * static_cast(Threading::GetThreadTicksPerSecond())) /
+ Common::Timer::GetFrequency() / 1000000000.0));
+ const double time_divider = 1000.0 * (1.0 / static_cast(Threading::GetThreadTicksPerSecond())) *
+ (1.0 / static_cast(frames_runf));
+
+ s_state.minimum_frame_time = std::exchange(s_state.minimum_frame_time_accumulator, 0.0f);
+ s_state.average_frame_time = std::exchange(s_state.average_frame_time_accumulator, 0.0f) / frames_runf;
+ s_state.maximum_frame_time = std::exchange(s_state.maximum_frame_time_accumulator, 0.0f);
+
+ s_state.vps = static_cast(frames_runf / time);
+ s_state.fps = static_cast(internal_frames_run) / time;
+ s_state.speed = (s_state.vps / System::GetVideoFrameRate()) * 100.0f;
+
+ const Threading::Thread* sw_thread = g_gpu->GetSWThread();
+ const u64 cpu_time = System::Internal::GetCPUThreadHandle().GetCPUTime();
+ const u64 sw_time = sw_thread ? sw_thread->GetCPUTime() : 0;
+ const u64 cpu_delta = cpu_time - s_state.last_cpu_time;
+ const u64 sw_delta = sw_time - s_state.last_sw_time;
+ s_state.last_cpu_time = cpu_time;
+ s_state.last_sw_time = sw_time;
+
+ s_state.cpu_thread_usage = static_cast(static_cast(cpu_delta) * pct_divider);
+ s_state.cpu_thread_time = static_cast(static_cast(cpu_delta) * time_divider);
+ s_state.sw_thread_usage = static_cast(static_cast(sw_delta) * pct_divider);
+ s_state.sw_thread_time = static_cast(static_cast(sw_delta) * time_divider);
+
+ if (MediaCapture* cap = System::GetMediaCapture())
+ cap->UpdateCaptureThreadUsage(pct_divider, time_divider);
+
+ if (g_gpu_device->IsGPUTimingEnabled())
+ {
+ s_state.average_gpu_time =
+ s_state.accumulated_gpu_time / static_cast(std::max(s_state.presents_since_last_update, 1u));
+ s_state.gpu_usage = s_state.accumulated_gpu_time / (time * 10.0f);
+ }
+ s_state.accumulated_gpu_time = 0.0f;
+ s_state.presents_since_last_update = 0;
+
+ if (g_settings.display_show_gpu_stats)
+ g_gpu->UpdateStatistics(frames_run);
+
+ VERBOSE_LOG("FPS: {:.2f} VPS: {:.2f} CPU: {:.2f} GPU: {:.2f} Avg: {:.2f}ms Min: {:.2f}ms Max: {:.2f}ms",
+ s_state.fps, s_state.vps, s_state.cpu_thread_usage, s_state.gpu_usage, s_state.average_frame_time,
+ s_state.minimum_frame_time, s_state.maximum_frame_time);
+
+ Host::OnPerformanceCountersUpdated();
+}
+
+void PerformanceCounters::AccumulateGPUTime()
+{
+ s_state.accumulated_gpu_time += g_gpu_device->GetAndResetAccumulatedGPUTime();
+ s_state.presents_since_last_update++;
+}
diff --git a/src/core/performance_counters.h b/src/core/performance_counters.h
new file mode 100644
index 000000000..db3827b5b
--- /dev/null
+++ b/src/core/performance_counters.h
@@ -0,0 +1,33 @@
+// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin
+// SPDX-License-Identifier: CC-BY-NC-ND-4.0
+
+#pragma once
+
+#include "common/types.h"
+
+namespace PerformanceCounters
+{
+static constexpr u32 NUM_FRAME_TIME_SAMPLES = 150;
+using FrameTimeHistory = std::array;
+
+float GetFPS();
+float GetVPS();
+float GetEmulationSpeed();
+float GetAverageFrameTime();
+float GetMinimumFrameTime();
+float GetMaximumFrameTime();
+float GetCPUThreadUsage();
+float GetCPUThreadAverageTime();
+float GetSWThreadUsage();
+float GetSWThreadAverageTime();
+float GetGPUUsage();
+float GetGPUAverageTime();
+const FrameTimeHistory& GetFrameTimeHistory();
+u32 GetFrameTimeHistoryPos();
+
+void Clear();
+void Reset();
+void Update(u32 frame_number, u32 internal_frame_number);
+void AccumulateGPUTime();
+
+} // namespace Host
diff --git a/src/core/system.cpp b/src/core/system.cpp
index 37240dfc6..6740df10d 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -28,6 +28,7 @@
#include "multitap.h"
#include "pad.h"
#include "pcdrv.h"
+#include "performance_counters.h"
#include "psf_loader.h"
#include "save_state_version.h"
#include "sio.h"
@@ -111,8 +112,10 @@ SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std
SystemBootParameters::~SystemBootParameters() = default;
namespace System {
+
/// Memory save states - only for internal use.
namespace {
+
struct SaveStateBuffer
{
std::string serial;
@@ -132,6 +135,7 @@ struct MemorySaveState
size_t state_size;
#endif
};
+
} // namespace
static void CheckCacheLineSize();
@@ -180,11 +184,8 @@ static void ResetThrottler();
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
static void Throttle(Common::Timer::Value current_time);
-static void UpdatePerformanceCounters();
-static void AccumulatePreFrameSleepTime();
-static void UpdatePreFrameSleepTime();
+static void AccumulatePreFrameSleepTime(Common::Timer::Value current_time);
static void UpdateDisplayVSync();
-static void ResetPerformanceCounters();
static bool UpdateGameSettingsLayer();
static void UpdateRunningGame(const std::string_view path, CDImage* image, bool booting);
@@ -239,7 +240,7 @@ static void PollDiscordPresence();
#endif
} // namespace System
-static constexpr const float PERFORMANCE_COUNTER_UPDATE_INTERVAL = 1.0f;
+static constexpr float PRE_FRAME_SLEEP_UPDATE_INTERVAL = 1.0f;
static constexpr const char FALLBACK_EXE_NAME[] = "PSX.EXE";
static constexpr u32 MAX_SKIPPED_DUPLICATE_FRAME_COUNT = 2; // 20fps minimum
static constexpr u32 MAX_SKIPPED_TIMEOUT_FRAME_COUNT = 1; // 30fps minimum
@@ -285,7 +286,7 @@ static bool s_skip_presenting_duplicate_frames = false;
static u32 s_skipped_frame_count = 0;
static u32 s_last_presented_internal_frame_number = 0;
-static float s_throttle_frequency = 0.0f;
+static float s_video_frame_rate = 0.0f;
static float s_target_speed = 0.0f;
static Common::Timer::Value s_frame_period = 0;
@@ -295,33 +296,8 @@ static Common::Timer::Value s_frame_start_time = 0;
static Common::Timer::Value s_last_active_frame_time = 0;
static Common::Timer::Value s_pre_frame_sleep_time = 0;
static Common::Timer::Value s_max_active_frame_time = 0;
+static Common::Timer::Value s_last_pre_frame_sleep_update_time = 0;
-static float s_average_frame_time_accumulator = 0.0f;
-static float s_minimum_frame_time_accumulator = 0.0f;
-static float s_maximum_frame_time_accumulator = 0.0f;
-
-static float s_vps = 0.0f;
-static float s_fps = 0.0f;
-static float s_speed = 0.0f;
-static float s_minimum_frame_time = 0.0f;
-static float s_maximum_frame_time = 0.0f;
-static float s_average_frame_time = 0.0f;
-static float s_cpu_thread_usage = 0.0f;
-static float s_cpu_thread_time = 0.0f;
-static float s_sw_thread_usage = 0.0f;
-static float s_sw_thread_time = 0.0f;
-static float s_average_gpu_time = 0.0f;
-static float s_accumulated_gpu_time = 0.0f;
-static float s_gpu_usage = 0.0f;
-static System::FrameTimeHistory s_frame_time_history;
-static u32 s_frame_time_history_pos = 0;
-static u32 s_last_frame_number = 0;
-static u32 s_last_internal_frame_number = 0;
-static u64 s_last_cpu_time = 0;
-static u64 s_last_sw_time = 0;
-static u32 s_presents_since_last_update = 0;
-static Common::Timer s_fps_timer;
-static Common::Timer s_frame_timer;
static Threading::ThreadHandle s_cpu_thread_handle;
static std::unique_ptr s_media_capture;
@@ -559,6 +535,11 @@ void System::Internal::CPUThreadShutdown()
#endif
}
+const Threading::ThreadHandle& System::Internal::GetCPUThreadHandle()
+{
+ return s_cpu_thread_handle;
+}
+
void System::Internal::IdlePollUpdate()
{
InputManager::PollSources();
@@ -795,67 +776,6 @@ const BIOS::ImageInfo* System::GetBIOSImageInfo()
return s_bios_image_info;
}
-float System::GetFPS()
-{
- return s_fps;
-}
-float System::GetVPS()
-{
- return s_vps;
-}
-float System::GetEmulationSpeed()
-{
- return s_speed;
-}
-float System::GetAverageFrameTime()
-{
- return s_average_frame_time;
-}
-float System::GetMinimumFrameTime()
-{
- return s_minimum_frame_time;
-}
-float System::GetMaximumFrameTime()
-{
- return s_maximum_frame_time;
-}
-float System::GetThrottleFrequency()
-{
- return s_throttle_frequency;
-}
-float System::GetCPUThreadUsage()
-{
- return s_cpu_thread_usage;
-}
-float System::GetCPUThreadAverageTime()
-{
- return s_cpu_thread_time;
-}
-float System::GetSWThreadUsage()
-{
- return s_sw_thread_usage;
-}
-float System::GetSWThreadAverageTime()
-{
- return s_sw_thread_time;
-}
-float System::GetGPUUsage()
-{
- return s_gpu_usage;
-}
-float System::GetGPUAverageTime()
-{
- return s_average_gpu_time;
-}
-const System::FrameTimeHistory& System::GetFrameTimeHistory()
-{
- return s_frame_time_history;
-}
-u32 System::GetFrameTimeHistoryPos()
-{
- return s_frame_time_history_pos;
-}
-
bool System::IsExePath(std::string_view path)
{
return (StringUtil::EndsWithNoCase(path, ".exe") || StringUtil::EndsWithNoCase(path, ".psexe") ||
@@ -1622,7 +1542,8 @@ void System::ResetSystem()
Host::AddIconOSDMessage("SystemReset", ICON_FA_POWER_OFF, TRANSLATE_STR("OSDMessage", "System reset."),
Host::OSD_QUICK_DURATION);
- ResetPerformanceCounters();
+ PerformanceCounters::Reset();
+ ResetThrottler();
InterruptExecution();
}
@@ -1677,7 +1598,7 @@ void System::PauseSystem(bool paused)
Host::OnIdleStateChanged();
UpdateDisplayVSync();
- ResetPerformanceCounters();
+ PerformanceCounters::Reset();
ResetThrottler();
}
}
@@ -1927,7 +1848,8 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error)
UpdateSpeedLimiterState();
ImGuiManager::UpdateDebugWindowConfig();
- ResetPerformanceCounters();
+ PerformanceCounters::Reset();
+ ResetThrottler();
return true;
}
@@ -1940,7 +1862,7 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b
s_internal_frame_number = 0;
s_target_speed = g_settings.emulation_speed;
- s_throttle_frequency = 60.0f;
+ s_video_frame_rate = 60.0f;
s_frame_period = 0;
s_next_frame_time = 0;
s_turbo_enabled = false;
@@ -1950,32 +1872,6 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b
s_rewind_load_counter = -1;
s_rewinding_first_save = true;
- s_average_frame_time_accumulator = 0.0f;
- s_minimum_frame_time_accumulator = 0.0f;
- s_maximum_frame_time_accumulator = 0.0f;
-
- s_vps = 0.0f;
- s_fps = 0.0f;
- s_speed = 0.0f;
- s_minimum_frame_time = 0.0f;
- s_maximum_frame_time = 0.0f;
- s_average_frame_time = 0.0f;
- s_cpu_thread_usage = 0.0f;
- s_cpu_thread_time = 0.0f;
- s_sw_thread_usage = 0.0f;
- s_sw_thread_time = 0.0f;
- s_average_gpu_time = 0.0f;
- s_accumulated_gpu_time = 0.0f;
- s_gpu_usage = 0.0f;
- s_last_frame_number = 0;
- s_last_internal_frame_number = 0;
- s_presents_since_last_update = 0;
- s_last_cpu_time = 0;
- s_fps_timer.Reset();
- s_frame_timer.Reset();
- s_frame_time_history.fill(0.0f);
- s_frame_time_history_pos = 0;
-
TimingEvents::Initialize();
Bus::Initialize();
@@ -2014,6 +1910,9 @@ bool System::Initialize(std::unique_ptr disc, DiscRegion disc_region, b
UpdateThrottlePeriod();
UpdateMemorySaveStateSettings();
+
+ PerformanceCounters::Clear();
+
return true;
}
@@ -2050,8 +1949,6 @@ void System::DestroySystem()
if (g_settings.inhibit_screensaver)
PlatformMisc::ResumeScreensaver();
- s_cpu_thread_usage = {};
-
ClearMemorySaveStates();
Cheats::UnloadAll();
@@ -2224,7 +2121,7 @@ void System::FrameDone()
// Kick off media capture early, might take a while.
if (s_media_capture && s_media_capture->IsCapturingVideo()) [[unlikely]]
{
- if (s_media_capture->GetVideoFPS() != GetThrottleFrequency()) [[unlikely]]
+ if (s_media_capture->GetVideoFPS() != s_video_frame_rate) [[unlikely]]
{
const std::string next_capture_path = s_media_capture->GetNextCapturePath();
INFO_LOG("Video frame rate changed, switching to new capture file {}", Path::GetFileName(next_capture_path));
@@ -2250,7 +2147,7 @@ void System::FrameDone()
const Common::Timer::Value pre_frame_sleep_until = s_next_frame_time + s_pre_frame_sleep_time;
s_last_active_frame_time = current_time - s_frame_start_time;
if (s_pre_frame_sleep)
- AccumulatePreFrameSleepTime();
+ AccumulatePreFrameSleepTime(current_time);
// explicit present (frame pacing)
const bool is_unique_frame = (s_last_presented_internal_frame_number != s_internal_frame_number);
@@ -2328,15 +2225,20 @@ void System::FrameDone()
// Update perf counters *after* throttling, we want to measure from start-of-frame
// to start-of-frame, not end-of-frame to end-of-frame (will be noisy due to different
// amounts of computation happening in each frame).
- System::UpdatePerformanceCounters();
+ PerformanceCounters::Update(s_frame_number, s_internal_frame_number);
}
-void System::SetThrottleFrequency(float frequency)
+float System::GetVideoFrameRate()
{
- if (s_throttle_frequency == frequency)
+ return s_video_frame_rate;
+}
+
+void System::SetVideoFrameRate(float frequency)
+{
+ if (s_video_frame_rate == frequency)
return;
- s_throttle_frequency = frequency;
+ s_video_frame_rate = frequency;
UpdateThrottlePeriod();
}
@@ -2346,7 +2248,7 @@ void System::UpdateThrottlePeriod()
{
const double target_speed = std::max(static_cast(s_target_speed), std::numeric_limits::epsilon());
s_frame_period =
- Common::Timer::ConvertSecondsToValue(1.0 / (static_cast(s_throttle_frequency) * target_speed));
+ Common::Timer::ConvertSecondsToValue(1.0 / (static_cast(s_video_frame_rate) * target_speed));
}
else
{
@@ -2790,17 +2692,11 @@ bool System::LoadState(const char* path, Error* error, bool save_undo_state)
return false;
}
- ResetPerformanceCounters();
- ResetThrottler();
-
- if (IsPaused())
- InvalidateDisplay();
-
VERBOSE_LOG("Loading state took {:.2f} msec", load_timer.GetTimeMilliseconds());
return true;
}
-bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bool update_display)
+bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bool update_display_if_paused)
{
Assert(IsValid());
@@ -2866,15 +2762,20 @@ bool System::LoadStateFromBuffer(const SaveStateBuffer& buffer, Error* error, bo
Achievements::DisableHardcoreMode();
StateWrapper sw(buffer.state_data.cspan(0, buffer.state_size), StateWrapper::Mode::Read, buffer.version);
- if (!DoState(sw, nullptr, update_display, false))
+ if (!DoState(sw, nullptr, update_display_if_paused && IsPaused(), false))
{
Error::SetStringView(error, "Save state stream is corrupted.");
return false;
}
InterruptExecution();
- ResetPerformanceCounters();
+
+ PerformanceCounters::Reset();
ResetThrottler();
+
+ if (update_display_if_paused && IsPaused())
+ InvalidateDisplay();
+
return true;
}
@@ -3340,100 +3241,7 @@ float System::GetAudioNominalRate()
return (s_throttler_enabled || s_syncing_to_host_with_vsync) ? s_target_speed : 1.0f;
}
-void System::UpdatePerformanceCounters()
-{
- const float frame_time = static_cast(s_frame_timer.GetTimeMillisecondsAndReset());
- s_minimum_frame_time_accumulator =
- (s_minimum_frame_time_accumulator == 0.0f) ? frame_time : std::min(s_minimum_frame_time_accumulator, frame_time);
- s_average_frame_time_accumulator += frame_time;
- s_maximum_frame_time_accumulator = std::max(s_maximum_frame_time_accumulator, frame_time);
- s_frame_time_history[s_frame_time_history_pos] = frame_time;
- s_frame_time_history_pos = (s_frame_time_history_pos + 1) % NUM_FRAME_TIME_SAMPLES;
-
- // update fps counter
- const Common::Timer::Value now_ticks = Common::Timer::GetCurrentValue();
- const Common::Timer::Value ticks_diff = now_ticks - s_fps_timer.GetStartValue();
- const float time = static_cast(Common::Timer::ConvertValueToSeconds(ticks_diff));
- if (time < PERFORMANCE_COUNTER_UPDATE_INTERVAL)
- return;
-
- const u32 frames_run = s_frame_number - s_last_frame_number;
- const float frames_runf = static_cast(frames_run);
-
- // TODO: Make the math here less rubbish
- const double pct_divider =
- 100.0 * (1.0 / ((static_cast(ticks_diff) * static_cast(Threading::GetThreadTicksPerSecond())) /
- Common::Timer::GetFrequency() / 1000000000.0));
- const double time_divider = 1000.0 * (1.0 / static_cast(Threading::GetThreadTicksPerSecond())) *
- (1.0 / static_cast(frames_runf));
-
- s_minimum_frame_time = std::exchange(s_minimum_frame_time_accumulator, 0.0f);
- s_average_frame_time = std::exchange(s_average_frame_time_accumulator, 0.0f) / frames_runf;
- s_maximum_frame_time = std::exchange(s_maximum_frame_time_accumulator, 0.0f);
-
- s_vps = static_cast(frames_runf / time);
- s_last_frame_number = s_frame_number;
- s_fps = static_cast(s_internal_frame_number - s_last_internal_frame_number) / time;
- s_last_internal_frame_number = s_internal_frame_number;
- s_speed = (s_vps / s_throttle_frequency) * 100.0f;
-
- const Threading::Thread* sw_thread = g_gpu->GetSWThread();
- const u64 cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
- const u64 sw_time = sw_thread ? sw_thread->GetCPUTime() : 0;
- const u64 cpu_delta = cpu_time - s_last_cpu_time;
- const u64 sw_delta = sw_time - s_last_sw_time;
- s_last_cpu_time = cpu_time;
- s_last_sw_time = sw_time;
-
- s_cpu_thread_usage = static_cast(static_cast(cpu_delta) * pct_divider);
- s_cpu_thread_time = static_cast(static_cast(cpu_delta) * time_divider);
- s_sw_thread_usage = static_cast(static_cast(sw_delta) * pct_divider);
- s_sw_thread_time = static_cast(static_cast(sw_delta) * time_divider);
-
- if (s_media_capture)
- s_media_capture->UpdateCaptureThreadUsage(pct_divider, time_divider);
-
- s_fps_timer.ResetTo(now_ticks);
-
- if (g_gpu_device->IsGPUTimingEnabled())
- {
- s_average_gpu_time = s_accumulated_gpu_time / static_cast(std::max(s_presents_since_last_update, 1u));
- s_gpu_usage = s_accumulated_gpu_time / (time * 10.0f);
- }
- s_accumulated_gpu_time = 0.0f;
- s_presents_since_last_update = 0;
-
- if (g_settings.display_show_gpu_stats)
- g_gpu->UpdateStatistics(frames_run);
-
- if (s_pre_frame_sleep)
- UpdatePreFrameSleepTime();
-
- VERBOSE_LOG("FPS: {:.2f} VPS: {:.2f} CPU: {:.2f} GPU: {:.2f} Average: {:.2f}ms Min: {:.2f}ms Max: {:.2f}ms", s_fps,
- s_vps, s_cpu_thread_usage, s_gpu_usage, s_average_frame_time, s_minimum_frame_time, s_maximum_frame_time);
-
- Host::OnPerformanceCountersUpdated();
-}
-
-void System::ResetPerformanceCounters()
-{
- s_last_frame_number = s_frame_number;
- s_last_internal_frame_number = s_internal_frame_number;
- s_last_cpu_time = s_cpu_thread_handle ? s_cpu_thread_handle.GetCPUTime() : 0;
- if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
- s_last_sw_time = sw_thread->GetCPUTime();
- else
- s_last_sw_time = 0;
-
- s_average_frame_time_accumulator = 0.0f;
- s_minimum_frame_time_accumulator = 0.0f;
- s_maximum_frame_time_accumulator = 0.0f;
- s_frame_timer.Reset();
- s_fps_timer.Reset();
- ResetThrottler();
-}
-
-void System::AccumulatePreFrameSleepTime()
+void System::AccumulatePreFrameSleepTime(Common::Timer::Value current_time)
{
DebugAssert(s_pre_frame_sleep);
@@ -3450,21 +3258,22 @@ void System::AccumulatePreFrameSleepTime()
Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
Common::Timer::ConvertValueToMilliseconds(s_last_active_frame_time));
}
-}
-void System::UpdatePreFrameSleepTime()
-{
- DebugAssert(s_pre_frame_sleep);
+ if (Common::Timer::ConvertValueToSeconds(current_time - s_last_pre_frame_sleep_update_time) >=
+ PRE_FRAME_SLEEP_UPDATE_INTERVAL)
+ {
+ s_last_pre_frame_sleep_update_time = current_time;
- const Common::Timer::Value expected_frame_time =
- s_max_active_frame_time + Common::Timer::ConvertMillisecondsToValue(g_settings.display_pre_frame_sleep_buffer);
- s_pre_frame_sleep_time = Common::AlignDown(s_frame_period - std::min(expected_frame_time, s_frame_period),
- static_cast(Common::Timer::ConvertMillisecondsToValue(1)));
- DEV_LOG("Set pre-frame time to {} ms (expected frame time of {} ms)",
- Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
- Common::Timer::ConvertValueToMilliseconds(expected_frame_time));
+ const Common::Timer::Value expected_frame_time =
+ s_max_active_frame_time + Common::Timer::ConvertMillisecondsToValue(g_settings.display_pre_frame_sleep_buffer);
+ s_pre_frame_sleep_time = Common::AlignDown(s_frame_period - std::min(expected_frame_time, s_frame_period),
+ static_cast(Common::Timer::ConvertMillisecondsToValue(1)));
+ DEV_LOG("Set pre-frame time to {} ms (expected frame time of {} ms)",
+ Common::Timer::ConvertValueToMilliseconds(s_pre_frame_sleep_time),
+ Common::Timer::ConvertValueToMilliseconds(expected_frame_time));
- s_max_active_frame_time = 0;
+ s_max_active_frame_time = 0;
+ }
}
void System::FormatLatencyStats(SmallStringBase& str)
@@ -3505,10 +3314,10 @@ void System::UpdateSpeedLimiterState()
const float host_refresh_rate = g_gpu_device->GetMainSwapChain()->GetWindowInfo().surface_refresh_rate;
if (host_refresh_rate > 0.0f)
{
- const float ratio = host_refresh_rate / System::GetThrottleFrequency();
+ const float ratio = host_refresh_rate / s_video_frame_rate;
s_can_sync_to_host = (ratio >= 0.95f && ratio <= 1.05f);
- INFO_LOG("Refresh rate: Host={}hz Guest={}hz Ratio={} - {}", host_refresh_rate, System::GetThrottleFrequency(),
- ratio, s_can_sync_to_host ? "can sync" : "can't sync");
+ INFO_LOG("Refresh rate: Host={}hz Guest={}hz Ratio={} - {}", host_refresh_rate, s_video_frame_rate, ratio,
+ s_can_sync_to_host ? "can sync" : "can't sync");
s_syncing_to_host = (s_can_sync_to_host && g_settings.sync_to_host_refresh_rate && s_target_speed == 1.0f);
if (s_syncing_to_host)
@@ -4911,7 +4720,7 @@ void System::UpdateMemorySaveStateSettings()
if (g_settings.rewind_enable)
{
- s_rewind_save_frequency = static_cast(std::ceil(g_settings.rewind_save_frequency * s_throttle_frequency));
+ s_rewind_save_frequency = static_cast(std::ceil(g_settings.rewind_save_frequency * s_video_frame_rate));
s_rewind_save_counter = 0;
u64 ram_usage, vram_usage;
@@ -5024,6 +4833,9 @@ bool System::LoadRewindState(u32 skip_saves /*= 0*/, bool consume_state /*=true
if (consume_state)
s_rewind_states.pop_back();
+ // back in time, need to reset perf counters
+ PerformanceCounters::Reset();
+
#ifdef PROFILE_MEMORY_SAVE_STATES
DEV_LOG("Rewind load took {:.4f} ms", load_timer.GetTimeMilliseconds());
#endif
@@ -5044,7 +4856,7 @@ void System::SetRewinding(bool enabled)
// Try to rewind at the replay speed, or one per second maximum.
const float load_frequency = std::min(g_settings.rewind_save_frequency, 1.0f);
- s_rewind_load_frequency = static_cast(std::ceil(load_frequency * s_throttle_frequency));
+ s_rewind_load_frequency = static_cast(std::ceil(load_frequency * s_video_frame_rate));
s_rewind_load_counter = 0;
if (!was_enabled && s_system_executing)
@@ -5065,7 +4877,6 @@ void System::DoRewind()
const u32 skip_saves = BoolToUInt32(!s_rewinding_first_save);
s_rewinding_first_save = false;
LoadRewindState(skip_saves, false);
- ResetPerformanceCounters();
s_rewind_load_counter = s_rewind_load_frequency;
}
else
@@ -5394,7 +5205,6 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
Host::GetUIntSettingValue("MediaCapture", "VideoHeight", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_HEIGHT);
const GPUTexture::Format capture_format =
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetFormat() : GPUTexture::Format::RGBA8;
- const float fps = System::GetThrottleFrequency();
if (capture_video)
{
// TODO: This will be a mess with GPU thread.
@@ -5429,8 +5239,8 @@ bool System::StartMediaCapture(std::string path, bool capture_video, bool captur
s_media_capture = MediaCapture::Create(backend, &error);
if (!s_media_capture ||
!s_media_capture->BeginCapture(
- fps, aspect, capture_width, capture_height, capture_format, SPU::SAMPLE_RATE, std::move(path), capture_video,
- Host::GetSmallStringSettingValue("MediaCapture", "VideoCodec"),
+ s_video_frame_rate, aspect, capture_width, capture_height, capture_format, SPU::SAMPLE_RATE, std::move(path),
+ capture_video, Host::GetSmallStringSettingValue("MediaCapture", "VideoCodec"),
Host::GetUIntSettingValue("MediaCapture", "VideoBitrate", Settings::DEFAULT_MEDIA_CAPTURE_VIDEO_BITRATE),
Host::GetBoolSettingValue("MediaCapture", "VideoCodecUseArgs", false) ?
Host::GetStringSettingValue("MediaCapture", "AudioCodecArgs") :
@@ -5779,11 +5589,8 @@ void System::ToggleSoftwareRendering()
Host::OSD_QUICK_DURATION);
RecreateGPU(new_renderer);
- // Might have a thread change.
- if (const Threading::Thread* sw_thread = g_gpu->GetSWThread(); sw_thread)
- s_last_sw_time = sw_thread->GetCPUTime();
- else
- s_last_sw_time = 0;
+ // TODO: GPU-THREAD: Drop this
+ PerformanceCounters::Reset();
g_gpu->UpdateResolutionScale();
}
@@ -5852,10 +5659,7 @@ bool System::PresentDisplay(bool explicit_present, u64 present_time)
g_gpu_device->EndPresent(g_gpu_device->GetMainSwapChain(), explicit_present, present_time);
if (g_gpu_device->IsGPUTimingEnabled())
- {
- s_accumulated_gpu_time += g_gpu_device->GetAndResetAccumulatedGPUTime();
- s_presents_since_last_update++;
- }
+ PerformanceCounters::AccumulateGPUTime();
}
else
{
diff --git a/src/core/system.h b/src/core/system.h
index dfa730eae..781ac8bae 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -13,7 +13,10 @@
#include
#include
-class ByteStream;
+namespace Threading {
+class ThreadHandle;
+}
+
class CDImage;
class Error;
class SmallStringBase;
@@ -231,24 +234,6 @@ u64 GetSessionPlayedTime();
const BIOS::ImageInfo* GetBIOSImageInfo();
-static constexpr u32 NUM_FRAME_TIME_SAMPLES = 150;
-using FrameTimeHistory = std::array;
-
-float GetFPS();
-float GetVPS();
-float GetEmulationSpeed();
-float GetAverageFrameTime();
-float GetMinimumFrameTime();
-float GetMaximumFrameTime();
-float GetThrottleFrequency();
-float GetCPUThreadUsage();
-float GetCPUThreadAverageTime();
-float GetSWThreadUsage();
-float GetSWThreadAverageTime();
-float GetGPUUsage();
-float GetGPUAverageTime();
-const FrameTimeHistory& GetFrameTimeHistory();
-u32 GetFrameTimeHistoryPos();
void FormatLatencyStats(SmallStringBase& str);
/// Loads global settings (i.e. EmuConfig).
@@ -292,7 +277,8 @@ float GetAudioNominalRate();
bool IsRunningAtNonStandardSpeed();
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
-void SetThrottleFrequency(float frequency);
+float GetVideoFrameRate();
+void SetVideoFrameRate(float frequency);
// Access controllers for simulating input.
Controller* GetController(u32 slot);
@@ -467,6 +453,9 @@ bool CPUThreadInitialize(Error* error);
/// Called on CPU thread shutdown.
void CPUThreadShutdown();
+/// Returns a handle to the CPU thread.
+const Threading::ThreadHandle& GetCPUThreadHandle();
+
/// Polls input, updates subsystems which are present while paused/inactive.
void IdlePollUpdate();
} // namespace Internal
diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp
index 50fd7eac3..8b755f25f 100644
--- a/src/duckstation-qt/qthost.cpp
+++ b/src/duckstation-qt/qthost.cpp
@@ -23,6 +23,7 @@
#include "core/host.h"
#include "core/imgui_overlays.h"
#include "core/memory_card.h"
+#include "core/performance_counters.h"
#include "core/spu.h"
#include "core/system.h"
@@ -2111,7 +2112,7 @@ void EmuThread::updatePerformanceCounters()
m_last_render_height = render_height;
}
- const float gfps = System::GetFPS();
+ const float gfps = PerformanceCounters::GetFPS();
if (gfps != m_last_game_fps)
{
QMetaObject::invokeMethod(g_main_window->getStatusFPSWidget(), "setText", Qt::QueuedConnection,
@@ -2119,8 +2120,8 @@ void EmuThread::updatePerformanceCounters()
m_last_game_fps = gfps;
}
- const float speed = System::GetEmulationSpeed();
- const float vfps = System::GetVPS();
+ const float speed = PerformanceCounters::GetEmulationSpeed();
+ const float vfps = PerformanceCounters::GetVPS();
if (speed != m_last_speed || vfps != m_last_video_fps)
{
QMetaObject::invokeMethod(