System: Move perf counters to separate namespace

This commit is contained in:
Stenzek 2024-10-27 21:17:28 +10:00
parent 875ccecb90
commit 21d19a6297
No known key found for this signature in database
10 changed files with 387 additions and 306 deletions

View File

@ -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

View File

@ -79,6 +79,7 @@
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="cpu_pgxp.cpp" />
<ClCompile Include="performance_counters.cpp" />
<ClCompile Include="playstation_mouse.cpp" />
<ClCompile Include="psf_loader.cpp" />
<ClCompile Include="settings.cpp" />
@ -160,6 +161,7 @@
<ClInclude Include="pcdrv.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="cpu_pgxp.h" />
<ClInclude Include="performance_counters.h" />
<ClInclude Include="playstation_mouse.h" />
<ClInclude Include="psf_loader.h" />
<ClInclude Include="save_state_version.h" />

View File

@ -70,6 +70,7 @@
<ClCompile Include="memory_scanner.cpp" />
<ClCompile Include="gpu_dump.cpp" />
<ClCompile Include="cdrom_subq_replacement.cpp" />
<ClCompile Include="performance_counters.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="types.h" />
@ -146,6 +147,7 @@
<ClInclude Include="memory_scanner.h" />
<ClInclude Include="gpu_dump.h" />
<ClInclude Include="cdrom_subq_replacement.h" />
<ClInclude Include="performance_counters.h" />
</ItemGroup>
<ItemGroup>
<None Include="gpu_sw_rasterizer.inl" />

View File

@ -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<u32>(static_cast<float>(num_frames + 1) *
std::ceil(System::GetVPS() / System::GetFPS())));
num_frames =
std::max(num_frames, static_cast<u32>(static_cast<float>(num_frames + 1) *
std::ceil(PerformanceCounters::GetVPS() / PerformanceCounters::GetFPS())));
}
// ensure vram is up to date

View File

@ -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<u32>(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);

View File

@ -0,0 +1,243 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// 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 <utility>
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<float>(
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<float>(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<float>(frames_run);
// TODO: Make the math here less rubbish
const double pct_divider =
100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) /
Common::Timer::GetFrequency() / 1000000000.0));
const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) *
(1.0 / static_cast<double>(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<float>(frames_runf / time);
s_state.fps = static_cast<float>(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<float>(static_cast<double>(cpu_delta) * pct_divider);
s_state.cpu_thread_time = static_cast<float>(static_cast<double>(cpu_delta) * time_divider);
s_state.sw_thread_usage = static_cast<float>(static_cast<double>(sw_delta) * pct_divider);
s_state.sw_thread_time = static_cast<float>(static_cast<double>(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<float>(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++;
}

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
// 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, NUM_FRAME_TIME_SAMPLES>;
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

View File

@ -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<MediaCapture> 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<CDImage> 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<CDImage> 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<CDImage> 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<double>(s_target_speed), std::numeric_limits<double>::epsilon());
s_frame_period =
Common::Timer::ConvertSecondsToValue(1.0 / (static_cast<double>(s_throttle_frequency) * target_speed));
Common::Timer::ConvertSecondsToValue(1.0 / (static_cast<double>(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<float>(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<float>(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<float>(frames_run);
// TODO: Make the math here less rubbish
const double pct_divider =
100.0 * (1.0 / ((static_cast<double>(ticks_diff) * static_cast<double>(Threading::GetThreadTicksPerSecond())) /
Common::Timer::GetFrequency() / 1000000000.0));
const double time_divider = 1000.0 * (1.0 / static_cast<double>(Threading::GetThreadTicksPerSecond())) *
(1.0 / static_cast<double>(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<float>(frames_runf / time);
s_last_frame_number = s_frame_number;
s_fps = static_cast<float>(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<float>(static_cast<double>(cpu_delta) * pct_divider);
s_cpu_thread_time = static_cast<float>(static_cast<double>(cpu_delta) * time_divider);
s_sw_thread_usage = static_cast<float>(static_cast<double>(sw_delta) * pct_divider);
s_sw_thread_time = static_cast<float>(static_cast<double>(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<float>(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<unsigned int>(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<unsigned int>(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<s32>(std::ceil(g_settings.rewind_save_frequency * s_throttle_frequency));
s_rewind_save_frequency = static_cast<s32>(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<s32>(std::ceil(load_frequency * s_throttle_frequency));
s_rewind_load_frequency = static_cast<s32>(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
{

View File

@ -13,7 +13,10 @@
#include <span>
#include <string>
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, NUM_FRAME_TIME_SAMPLES>;
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

View File

@ -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(