mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-01-18 06:25:37 +00:00
HostInterface: Move throttle and perf stats to System class
This commit is contained in:
parent
895cefec60
commit
c820ddba79
|
@ -302,7 +302,7 @@ void GPU::UpdateCRTCConfig()
|
||||||
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
||||||
const double vertical_frequency =
|
const double vertical_frequency =
|
||||||
static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame);
|
static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame);
|
||||||
m_system->GetHostInterface()->SetThrottleFrequency(vertical_frequency);
|
m_system->SetThrottleFrequency(vertical_frequency);
|
||||||
|
|
||||||
const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
|
const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
|
||||||
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
|
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/timer.h"
|
|
||||||
#include "dma.h"
|
#include "dma.h"
|
||||||
#include "game_list.h"
|
#include "game_list.h"
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
|
@ -20,12 +19,6 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
Log_SetChannel(HostInterface);
|
Log_SetChannel(HostInterface);
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include "common/windows_headers.h"
|
|
||||||
#else
|
|
||||||
#include <time.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ANDROID) || (defined(__GNUC__) && __GNUC__ < 8)
|
#if defined(ANDROID) || (defined(__GNUC__) && __GNUC__ < 8)
|
||||||
|
|
||||||
static std::string GetRelativePath(const std::string& path, const char* new_filename)
|
static std::string GetRelativePath(const std::string& path, const char* new_filename)
|
||||||
|
@ -58,7 +51,6 @@ HostInterface::HostInterface()
|
||||||
m_game_list = std::make_unique<GameList>();
|
m_game_list = std::make_unique<GameList>();
|
||||||
m_game_list->SetCacheFilename(GetGameListCacheFileName());
|
m_game_list->SetCacheFilename(GetGameListCacheFileName());
|
||||||
m_game_list->SetDatabaseFilename(GetGameListDatabaseFileName());
|
m_game_list->SetDatabaseFilename(GetGameListDatabaseFileName());
|
||||||
m_last_throttle_time = Common::Timer::GetValue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HostInterface::~HostInterface() = default;
|
HostInterface::~HostInterface() = default;
|
||||||
|
@ -91,7 +83,6 @@ bool HostInterface::BootSystem(const char* filename, const char* state_filename)
|
||||||
void HostInterface::ResetSystem()
|
void HostInterface::ResetSystem()
|
||||||
{
|
{
|
||||||
m_system->Reset();
|
m_system->Reset();
|
||||||
ResetPerformanceCounters();
|
|
||||||
AddOSDMessage("System reset.");
|
AddOSDMessage("System reset.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +129,7 @@ void HostInterface::DrawFPSWindow()
|
||||||
const bool show_vps = true;
|
const bool show_vps = true;
|
||||||
const bool show_speed = true;
|
const bool show_speed = true;
|
||||||
|
|
||||||
if (!(show_fps | show_vps | show_speed))
|
if (!(show_fps | show_vps | show_speed) || !m_system)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const ImVec2 window_size =
|
const ImVec2 window_size =
|
||||||
|
@ -158,7 +149,7 @@ void HostInterface::DrawFPSWindow()
|
||||||
bool first = true;
|
bool first = true;
|
||||||
if (show_fps)
|
if (show_fps)
|
||||||
{
|
{
|
||||||
ImGui::Text("%.2f", m_fps);
|
ImGui::Text("%.2f", m_system->GetFPS());
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
if (show_vps)
|
if (show_vps)
|
||||||
|
@ -174,7 +165,7 @@ void HostInterface::DrawFPSWindow()
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Text("%.2f", m_vps);
|
ImGui::Text("%.2f", m_system->GetVPS());
|
||||||
}
|
}
|
||||||
if (show_speed)
|
if (show_speed)
|
||||||
{
|
{
|
||||||
|
@ -189,10 +180,11 @@ void HostInterface::DrawFPSWindow()
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 rounded_speed = static_cast<u32>(std::round(m_speed));
|
const float speed = m_system->GetEmulationSpeed();
|
||||||
if (m_speed < 90.0f)
|
const u32 rounded_speed = static_cast<u32>(std::round(speed));
|
||||||
|
if (speed < 90.0f)
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||||
else if (m_speed < 110.0f)
|
else if (speed < 110.0f)
|
||||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed);
|
||||||
else
|
else
|
||||||
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||||
|
@ -347,45 +339,6 @@ std::optional<std::vector<u8>> HostInterface::GetBIOSImage(ConsoleRegion region)
|
||||||
return BIOS::LoadImageFromFile(m_settings.bios_path);
|
return BIOS::LoadImageFromFile(m_settings.bios_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostInterface::Throttle()
|
|
||||||
{
|
|
||||||
// Allow variance of up to 40ms either way.
|
|
||||||
constexpr s64 MAX_VARIANCE_TIME = INT64_C(40000000);
|
|
||||||
|
|
||||||
// Don't sleep for <1ms or >=period.
|
|
||||||
constexpr s64 MINIMUM_SLEEP_TIME = INT64_C(1000000);
|
|
||||||
|
|
||||||
// Use unsigned for defined overflow/wrap-around.
|
|
||||||
const u64 time = static_cast<u64>(m_throttle_timer.GetTimeNanoseconds());
|
|
||||||
const s64 sleep_time = static_cast<s64>(m_last_throttle_time - time);
|
|
||||||
if (std::abs(sleep_time) >= MAX_VARIANCE_TIME)
|
|
||||||
{
|
|
||||||
#ifndef _DEBUG
|
|
||||||
// Don't display the slow messages in debug, it'll always be slow...
|
|
||||||
// Limit how often the messages are displayed.
|
|
||||||
if (m_speed_lost_time_timestamp.GetTimeSeconds() >= 1.0f)
|
|
||||||
{
|
|
||||||
Log_WarningPrintf("System too %s, lost %.2f ms", sleep_time < 0 ? "slow" : "fast",
|
|
||||||
static_cast<double>(std::abs(sleep_time) - MAX_VARIANCE_TIME) / 1000000.0);
|
|
||||||
m_speed_lost_time_timestamp.Reset();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
m_last_throttle_time = 0;
|
|
||||||
m_throttle_timer.Reset();
|
|
||||||
}
|
|
||||||
else if (sleep_time >= MINIMUM_SLEEP_TIME && sleep_time <= m_throttle_period)
|
|
||||||
{
|
|
||||||
#ifdef WIN32
|
|
||||||
Sleep(static_cast<u32>(sleep_time / 1000000));
|
|
||||||
#else
|
|
||||||
const struct timespec ts = {0, static_cast<long>(sleep_time)};
|
|
||||||
nanosleep(&ts, nullptr);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
m_last_throttle_time += m_throttle_period;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HostInterface::LoadState(const char* filename)
|
bool HostInterface::LoadState(const char* filename)
|
||||||
{
|
{
|
||||||
std::unique_ptr<ByteStream> stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
std::unique_ptr<ByteStream> stream = FileSystem::OpenFile(filename, BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_STREAMED);
|
||||||
|
@ -441,13 +394,13 @@ void HostInterface::UpdateSpeedLimiterState()
|
||||||
m_audio_stream->EmptyBuffers();
|
m_audio_stream->EmptyBuffers();
|
||||||
|
|
||||||
m_display->SetVSync(video_sync_enabled);
|
m_display->SetVSync(video_sync_enabled);
|
||||||
m_throttle_timer.Reset();
|
if (m_system)
|
||||||
m_last_throttle_time = 0;
|
m_system->ResetPerformanceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostInterface::SwitchGPURenderer() {}
|
void HostInterface::SwitchGPURenderer() {}
|
||||||
|
|
||||||
void HostInterface::OnPerformanceCountersUpdated() {}
|
void HostInterface::OnSystemPerformanceCountersUpdated() {}
|
||||||
|
|
||||||
void HostInterface::OnRunningGameChanged() {}
|
void HostInterface::OnRunningGameChanged() {}
|
||||||
|
|
||||||
|
@ -647,59 +600,3 @@ void HostInterface::ModifyResolutionScale(s32 increment)
|
||||||
GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale,
|
GPU::VRAM_WIDTH * m_settings.gpu_resolution_scale,
|
||||||
GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale);
|
GPU::VRAM_HEIGHT * m_settings.gpu_resolution_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HostInterface::RunFrame()
|
|
||||||
{
|
|
||||||
m_frame_timer.Reset();
|
|
||||||
m_system->RunFrame();
|
|
||||||
UpdatePerformanceCounters();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HostInterface::UpdatePerformanceCounters()
|
|
||||||
{
|
|
||||||
const float frame_time = static_cast<float>(m_frame_timer.GetTimeMilliseconds());
|
|
||||||
m_average_frame_time_accumulator += frame_time;
|
|
||||||
m_worst_frame_time_accumulator = std::max(m_worst_frame_time_accumulator, frame_time);
|
|
||||||
|
|
||||||
// update fps counter
|
|
||||||
const float time = static_cast<float>(m_fps_timer.GetTimeSeconds());
|
|
||||||
if (time < 1.0f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const float frames_presented = static_cast<float>(m_system->GetFrameNumber() - m_last_frame_number);
|
|
||||||
|
|
||||||
m_worst_frame_time = m_worst_frame_time_accumulator;
|
|
||||||
m_worst_frame_time_accumulator = 0.0f;
|
|
||||||
m_average_frame_time = m_average_frame_time_accumulator / frames_presented;
|
|
||||||
m_average_frame_time_accumulator = 0.0f;
|
|
||||||
m_vps = static_cast<float>(frames_presented / time);
|
|
||||||
m_last_frame_number = m_system->GetFrameNumber();
|
|
||||||
m_fps = static_cast<float>(m_system->GetInternalFrameNumber() - m_last_internal_frame_number) / time;
|
|
||||||
m_last_internal_frame_number = m_system->GetInternalFrameNumber();
|
|
||||||
m_speed = static_cast<float>(static_cast<double>(m_system->GetGlobalTickCounter() - m_last_global_tick_counter) /
|
|
||||||
(static_cast<double>(MASTER_CLOCK) * time)) *
|
|
||||||
100.0f;
|
|
||||||
m_last_global_tick_counter = m_system->GetGlobalTickCounter();
|
|
||||||
m_fps_timer.Reset();
|
|
||||||
|
|
||||||
OnPerformanceCountersUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HostInterface::ResetPerformanceCounters()
|
|
||||||
{
|
|
||||||
if (m_system)
|
|
||||||
{
|
|
||||||
m_last_frame_number = m_system->GetFrameNumber();
|
|
||||||
m_last_internal_frame_number = m_system->GetInternalFrameNumber();
|
|
||||||
m_last_global_tick_counter = m_system->GetGlobalTickCounter();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_last_frame_number = 0;
|
|
||||||
m_last_internal_frame_number = 0;
|
|
||||||
m_last_global_tick_counter = 0;
|
|
||||||
}
|
|
||||||
m_average_frame_time_accumulator = 0.0f;
|
|
||||||
m_worst_frame_time_accumulator = 0.0f;
|
|
||||||
m_fps_timer.Reset();
|
|
||||||
}
|
|
||||||
|
|
|
@ -38,9 +38,6 @@ public:
|
||||||
/// Returns the game list.
|
/// Returns the game list.
|
||||||
const GameList* GetGameList() const { return m_game_list.get(); }
|
const GameList* GetGameList() const { return m_game_list.get(); }
|
||||||
|
|
||||||
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
|
|
||||||
void SetThrottleFrequency(double frequency) { m_throttle_period = static_cast<s64>(1000000000.0 / frequency); }
|
|
||||||
|
|
||||||
bool CreateSystem();
|
bool CreateSystem();
|
||||||
bool BootSystem(const char* filename, const char* state_filename);
|
bool BootSystem(const char* filename, const char* state_filename);
|
||||||
void ResetSystem();
|
void ResetSystem();
|
||||||
|
@ -68,12 +65,7 @@ public:
|
||||||
/// Returns a path relative to the user directory.
|
/// Returns a path relative to the user directory.
|
||||||
std::string GetUserDirectoryRelativePath(const char* format, ...) const;
|
std::string GetUserDirectoryRelativePath(const char* format, ...) const;
|
||||||
|
|
||||||
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
|
||||||
void Throttle();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using ThrottleClock = std::chrono::steady_clock;
|
|
||||||
|
|
||||||
enum : u32
|
enum : u32
|
||||||
{
|
{
|
||||||
AUDIO_SAMPLE_RATE = 44100,
|
AUDIO_SAMPLE_RATE = 44100,
|
||||||
|
@ -90,7 +82,7 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual void SwitchGPURenderer();
|
virtual void SwitchGPURenderer();
|
||||||
virtual void OnPerformanceCountersUpdated();
|
virtual void OnSystemPerformanceCountersUpdated();
|
||||||
virtual void OnRunningGameChanged();
|
virtual void OnRunningGameChanged();
|
||||||
|
|
||||||
void SetUserDirectory();
|
void SetUserDirectory();
|
||||||
|
@ -132,8 +124,6 @@ protected:
|
||||||
/// Adjusts the internal (render) resolution of the hardware backends.
|
/// Adjusts the internal (render) resolution of the hardware backends.
|
||||||
void ModifyResolutionScale(s32 increment);
|
void ModifyResolutionScale(s32 increment);
|
||||||
|
|
||||||
void RunFrame();
|
|
||||||
|
|
||||||
void UpdateSpeedLimiterState();
|
void UpdateSpeedLimiterState();
|
||||||
|
|
||||||
void DrawFPSWindow();
|
void DrawFPSWindow();
|
||||||
|
@ -141,9 +131,6 @@ protected:
|
||||||
void DrawDebugWindows();
|
void DrawDebugWindows();
|
||||||
void ClearImGuiFocus();
|
void ClearImGuiFocus();
|
||||||
|
|
||||||
void UpdatePerformanceCounters();
|
|
||||||
void ResetPerformanceCounters();
|
|
||||||
|
|
||||||
std::unique_ptr<HostDisplay> m_display;
|
std::unique_ptr<HostDisplay> m_display;
|
||||||
std::unique_ptr<AudioStream> m_audio_stream;
|
std::unique_ptr<AudioStream> m_audio_stream;
|
||||||
std::unique_ptr<System> m_system;
|
std::unique_ptr<System> m_system;
|
||||||
|
@ -151,29 +138,10 @@ protected:
|
||||||
Settings m_settings;
|
Settings m_settings;
|
||||||
std::string m_user_directory;
|
std::string m_user_directory;
|
||||||
|
|
||||||
u64 m_last_throttle_time = 0;
|
|
||||||
s64 m_throttle_period = INT64_C(1000000000) / 60;
|
|
||||||
Common::Timer m_throttle_timer;
|
|
||||||
Common::Timer m_speed_lost_time_timestamp;
|
|
||||||
|
|
||||||
bool m_paused = false;
|
bool m_paused = false;
|
||||||
bool m_speed_limiter_temp_disabled = false;
|
bool m_speed_limiter_temp_disabled = false;
|
||||||
bool m_speed_limiter_enabled = false;
|
bool m_speed_limiter_enabled = false;
|
||||||
|
|
||||||
float m_average_frame_time_accumulator = 0.0f;
|
|
||||||
float m_worst_frame_time_accumulator = 0.0f;
|
|
||||||
|
|
||||||
float m_vps = 0.0f;
|
|
||||||
float m_fps = 0.0f;
|
|
||||||
float m_speed = 0.0f;
|
|
||||||
float m_worst_frame_time = 0.0f;
|
|
||||||
float m_average_frame_time = 0.0f;
|
|
||||||
u32 m_last_frame_number = 0;
|
|
||||||
u32 m_last_internal_frame_number = 0;
|
|
||||||
u32 m_last_global_tick_counter = 0;
|
|
||||||
Common::Timer m_fps_timer;
|
|
||||||
Common::Timer m_frame_timer;
|
|
||||||
|
|
||||||
std::deque<OSDMessage> m_osd_messages;
|
std::deque<OSDMessage> m_osd_messages;
|
||||||
std::mutex m_osd_messages_lock;
|
std::mutex m_osd_messages_lock;
|
||||||
};
|
};
|
||||||
|
|
|
@ -23,6 +23,12 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
Log_SetChannel(System);
|
Log_SetChannel(System);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "common/windows_headers.h"
|
||||||
|
#else
|
||||||
|
#include <time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
System::System(HostInterface* host_interface) : m_host_interface(host_interface)
|
||||||
{
|
{
|
||||||
m_cpu = std::make_unique<CPU::Core>();
|
m_cpu = std::make_unique<CPU::Core>();
|
||||||
|
@ -350,6 +356,7 @@ void System::Reset()
|
||||||
m_internal_frame_number = 0;
|
m_internal_frame_number = 0;
|
||||||
m_global_tick_counter = 0;
|
m_global_tick_counter = 0;
|
||||||
m_last_event_run_time = 0;
|
m_last_event_run_time = 0;
|
||||||
|
ResetPerformanceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::LoadState(ByteStream* state)
|
bool System::LoadState(ByteStream* state)
|
||||||
|
@ -366,8 +373,10 @@ bool System::SaveState(ByteStream* state)
|
||||||
|
|
||||||
void System::RunFrame()
|
void System::RunFrame()
|
||||||
{
|
{
|
||||||
// Duplicated to avoid branch in the while loop, as the downcount can be quite low at times.
|
m_frame_timer.Reset();
|
||||||
m_frame_done = false;
|
m_frame_done = false;
|
||||||
|
|
||||||
|
// Duplicated to avoid branch in the while loop, as the downcount can be quite low at times.
|
||||||
if (m_cpu_execution_mode == CPUExecutionMode::Interpreter)
|
if (m_cpu_execution_mode == CPUExecutionMode::Interpreter)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
|
@ -389,6 +398,89 @@ void System::RunFrame()
|
||||||
|
|
||||||
// Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
|
// Generate any pending samples from the SPU before sleeping, this way we reduce the chances of underruns.
|
||||||
m_spu->GeneratePendingSamples();
|
m_spu->GeneratePendingSamples();
|
||||||
|
|
||||||
|
UpdatePerformanceCounters();
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::Throttle()
|
||||||
|
{
|
||||||
|
// Allow variance of up to 40ms either way.
|
||||||
|
constexpr s64 MAX_VARIANCE_TIME = INT64_C(40000000);
|
||||||
|
|
||||||
|
// Don't sleep for <1ms or >=period.
|
||||||
|
constexpr s64 MINIMUM_SLEEP_TIME = INT64_C(1000000);
|
||||||
|
|
||||||
|
// Use unsigned for defined overflow/wrap-around.
|
||||||
|
const u64 time = static_cast<u64>(m_throttle_timer.GetTimeNanoseconds());
|
||||||
|
const s64 sleep_time = static_cast<s64>(m_last_throttle_time - time);
|
||||||
|
if (std::abs(sleep_time) >= MAX_VARIANCE_TIME)
|
||||||
|
{
|
||||||
|
#ifndef _DEBUG
|
||||||
|
// Don't display the slow messages in debug, it'll always be slow...
|
||||||
|
// Limit how often the messages are displayed.
|
||||||
|
if (m_speed_lost_time_timestamp.GetTimeSeconds() >= 1.0f)
|
||||||
|
{
|
||||||
|
Log_WarningPrintf("System too %s, lost %.2f ms", sleep_time < 0 ? "slow" : "fast",
|
||||||
|
static_cast<double>(std::abs(sleep_time) - MAX_VARIANCE_TIME) / 1000000.0);
|
||||||
|
m_speed_lost_time_timestamp.Reset();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
m_last_throttle_time = 0;
|
||||||
|
m_throttle_timer.Reset();
|
||||||
|
}
|
||||||
|
else if (sleep_time >= MINIMUM_SLEEP_TIME && sleep_time <= m_throttle_period)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
Sleep(static_cast<u32>(sleep_time / 1000000));
|
||||||
|
#else
|
||||||
|
const struct timespec ts = {0, static_cast<long>(sleep_time)};
|
||||||
|
nanosleep(&ts, nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_throttle_time += m_throttle_period;
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::UpdatePerformanceCounters()
|
||||||
|
{
|
||||||
|
const float frame_time = static_cast<float>(m_frame_timer.GetTimeMilliseconds());
|
||||||
|
m_average_frame_time_accumulator += frame_time;
|
||||||
|
m_worst_frame_time_accumulator = std::max(m_worst_frame_time_accumulator, frame_time);
|
||||||
|
|
||||||
|
// update fps counter
|
||||||
|
const float time = static_cast<float>(m_fps_timer.GetTimeSeconds());
|
||||||
|
if (time < 1.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const float frames_presented = static_cast<float>(m_frame_number - m_last_frame_number);
|
||||||
|
|
||||||
|
m_worst_frame_time = m_worst_frame_time_accumulator;
|
||||||
|
m_worst_frame_time_accumulator = 0.0f;
|
||||||
|
m_average_frame_time = m_average_frame_time_accumulator / frames_presented;
|
||||||
|
m_average_frame_time_accumulator = 0.0f;
|
||||||
|
m_vps = static_cast<float>(frames_presented / time);
|
||||||
|
m_last_frame_number = m_frame_number;
|
||||||
|
m_fps = static_cast<float>(m_internal_frame_number - m_last_internal_frame_number) / time;
|
||||||
|
m_last_internal_frame_number = m_internal_frame_number;
|
||||||
|
m_speed = static_cast<float>(static_cast<double>(m_global_tick_counter - m_last_global_tick_counter) /
|
||||||
|
(static_cast<double>(MASTER_CLOCK) * time)) *
|
||||||
|
100.0f;
|
||||||
|
m_last_global_tick_counter = m_global_tick_counter;
|
||||||
|
m_fps_timer.Reset();
|
||||||
|
|
||||||
|
m_host_interface->OnSystemPerformanceCountersUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void System::ResetPerformanceCounters()
|
||||||
|
{
|
||||||
|
m_last_frame_number = m_frame_number;
|
||||||
|
m_last_internal_frame_number = m_internal_frame_number;
|
||||||
|
m_last_global_tick_counter = m_global_tick_counter;
|
||||||
|
m_average_frame_time_accumulator = 0.0f;
|
||||||
|
m_worst_frame_time_accumulator = 0.0f;
|
||||||
|
m_fps_timer.Reset();
|
||||||
|
m_throttle_timer.Reset();
|
||||||
|
m_last_throttle_time = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::LoadEXE(const char* filename, std::vector<u8>& bios_image)
|
bool System::LoadEXE(const char* filename, std::vector<u8>& bios_image)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common/timer.h"
|
||||||
#include "host_interface.h"
|
#include "host_interface.h"
|
||||||
#include "timing_event.h"
|
#include "timing_event.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
@ -68,6 +69,12 @@ public:
|
||||||
const std::string& GetRunningCode() const { return m_running_game_code; }
|
const std::string& GetRunningCode() const { return m_running_game_code; }
|
||||||
const std::string& GetRunningTitle() const { return m_running_game_title; }
|
const std::string& GetRunningTitle() const { return m_running_game_title; }
|
||||||
|
|
||||||
|
float GetFPS() const { return m_fps; }
|
||||||
|
float GetVPS() const { return m_vps; }
|
||||||
|
float GetEmulationSpeed() const { return m_speed; }
|
||||||
|
float GetAverageFrameTime() const { return m_average_frame_time; }
|
||||||
|
float GetWorstFrameTime() const { return m_worst_frame_time; }
|
||||||
|
|
||||||
bool Boot(const char* filename);
|
bool Boot(const char* filename);
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
@ -85,6 +92,15 @@ public:
|
||||||
|
|
||||||
void RunFrame();
|
void RunFrame();
|
||||||
|
|
||||||
|
/// Adjusts the throttle frequency, i.e. how many times we should sleep per second.
|
||||||
|
void SetThrottleFrequency(double frequency) { m_throttle_period = static_cast<s64>(1000000000.0 / frequency); }
|
||||||
|
|
||||||
|
/// Throttles the system, i.e. sleeps until it's time to execute the next frame.
|
||||||
|
void Throttle();
|
||||||
|
|
||||||
|
void UpdatePerformanceCounters();
|
||||||
|
void ResetPerformanceCounters();
|
||||||
|
|
||||||
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
||||||
bool SetExpansionROM(const char* filename);
|
bool SetExpansionROM(const char* filename);
|
||||||
|
|
||||||
|
@ -104,8 +120,6 @@ public:
|
||||||
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
std::unique_ptr<TimingEvent> CreateTimingEvent(std::string name, TickCount period, TickCount interval,
|
||||||
TimingEventCallback callback, bool activate);
|
TimingEventCallback callback, bool activate);
|
||||||
|
|
||||||
bool RUNNING_EVENTS() const { return m_running_events; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
System(HostInterface* host_interface);
|
System(HostInterface* host_interface);
|
||||||
|
|
||||||
|
@ -171,4 +185,23 @@ private:
|
||||||
std::string m_running_game_path;
|
std::string m_running_game_path;
|
||||||
std::string m_running_game_code;
|
std::string m_running_game_code;
|
||||||
std::string m_running_game_title;
|
std::string m_running_game_title;
|
||||||
|
|
||||||
|
u64 m_last_throttle_time = 0;
|
||||||
|
s64 m_throttle_period = INT64_C(1000000000) / 60;
|
||||||
|
Common::Timer m_throttle_timer;
|
||||||
|
Common::Timer m_speed_lost_time_timestamp;
|
||||||
|
|
||||||
|
float m_average_frame_time_accumulator = 0.0f;
|
||||||
|
float m_worst_frame_time_accumulator = 0.0f;
|
||||||
|
|
||||||
|
float m_vps = 0.0f;
|
||||||
|
float m_fps = 0.0f;
|
||||||
|
float m_speed = 0.0f;
|
||||||
|
float m_worst_frame_time = 0.0f;
|
||||||
|
float m_average_frame_time = 0.0f;
|
||||||
|
u32 m_last_frame_number = 0;
|
||||||
|
u32 m_last_internal_frame_number = 0;
|
||||||
|
u32 m_last_global_tick_counter = 0;
|
||||||
|
Common::Timer m_fps_timer;
|
||||||
|
Common::Timer m_frame_timer;
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,8 +124,8 @@ void MainWindow::recreateDisplayWidget(bool create_device_context)
|
||||||
updateDebugMenuGPURenderer();
|
updateDebugMenuGPURenderer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::onPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time,
|
void MainWindow::onSystemPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time,
|
||||||
float worst_frame_time)
|
float worst_frame_time)
|
||||||
{
|
{
|
||||||
m_status_speed_widget->setText(QStringLiteral("%1%").arg(speed, 0, 'f', 0));
|
m_status_speed_widget->setText(QStringLiteral("%1%").arg(speed, 0, 'f', 0));
|
||||||
m_status_fps_widget->setText(
|
m_status_fps_widget->setText(
|
||||||
|
@ -345,8 +345,8 @@ void MainWindow::connectSignals()
|
||||||
connect(m_host_interface, &QtHostInterface::toggleFullscreenRequested, this, &MainWindow::toggleFullscreen);
|
connect(m_host_interface, &QtHostInterface::toggleFullscreenRequested, this, &MainWindow::toggleFullscreen);
|
||||||
connect(m_host_interface, &QtHostInterface::recreateDisplayWidgetRequested, this, &MainWindow::recreateDisplayWidget,
|
connect(m_host_interface, &QtHostInterface::recreateDisplayWidgetRequested, this, &MainWindow::recreateDisplayWidget,
|
||||||
Qt::BlockingQueuedConnection);
|
Qt::BlockingQueuedConnection);
|
||||||
connect(m_host_interface, &QtHostInterface::performanceCountersUpdated, this,
|
connect(m_host_interface, &QtHostInterface::systemPerformanceCountersUpdated, this,
|
||||||
&MainWindow::onPerformanceCountersUpdated);
|
&MainWindow::onSystemPerformanceCountersUpdated);
|
||||||
connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
connect(m_host_interface, &QtHostInterface::runningGameChanged, this, &MainWindow::onRunningGameChanged);
|
||||||
|
|
||||||
connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameListEntry* entry) {
|
connect(m_game_list_widget, &GameListWidget::bootEntryRequested, [this](const GameListEntry* entry) {
|
||||||
|
|
|
@ -29,8 +29,8 @@ private Q_SLOTS:
|
||||||
void onEmulationPaused(bool paused);
|
void onEmulationPaused(bool paused);
|
||||||
void toggleFullscreen();
|
void toggleFullscreen();
|
||||||
void recreateDisplayWidget(bool create_device_context);
|
void recreateDisplayWidget(bool create_device_context);
|
||||||
void onPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time,
|
void onSystemPerformanceCountersUpdated(float speed, float fps, float vps, float average_frame_time,
|
||||||
float worst_frame_time);
|
float worst_frame_time);
|
||||||
void onRunningGameChanged(QString filename, QString game_code, QString game_title);
|
void onRunningGameChanged(QString filename, QString game_code, QString game_title);
|
||||||
|
|
||||||
void onStartDiscActionTriggered();
|
void onStartDiscActionTriggered();
|
||||||
|
|
|
@ -52,9 +52,7 @@ void QtHostInterface::ReportMessage(const char* message)
|
||||||
|
|
||||||
void QtHostInterface::setDefaultSettings()
|
void QtHostInterface::setDefaultSettings()
|
||||||
{
|
{
|
||||||
HostInterface::UpdateSettings([this]() {
|
HostInterface::UpdateSettings([this]() { HostInterface::SetDefaultSettings(); });
|
||||||
HostInterface::SetDefaultSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
// default input settings for Qt
|
// default input settings for Qt
|
||||||
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
|
||||||
|
@ -263,16 +261,18 @@ void QtHostInterface::SwitchGPURenderer()
|
||||||
m_audio_stream->PauseOutput(false);
|
m_audio_stream->PauseOutput(false);
|
||||||
UpdateSpeedLimiterState();
|
UpdateSpeedLimiterState();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ResetPerformanceCounters();
|
m_system->ResetPerformanceCounters();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::OnPerformanceCountersUpdated()
|
void QtHostInterface::OnSystemPerformanceCountersUpdated()
|
||||||
{
|
{
|
||||||
HostInterface::OnPerformanceCountersUpdated();
|
HostInterface::OnSystemPerformanceCountersUpdated();
|
||||||
|
|
||||||
emit performanceCountersUpdated(m_speed, m_fps, m_vps, m_average_frame_time, m_worst_frame_time);
|
DebugAssert(m_system);
|
||||||
|
emit systemPerformanceCountersUpdated(m_system->GetEmulationSpeed(), m_system->GetFPS(), m_system->GetVPS(),
|
||||||
|
m_system->GetAverageFrameTime(), m_system->GetWorstFrameTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QtHostInterface::OnRunningGameChanged()
|
void QtHostInterface::OnRunningGameChanged()
|
||||||
|
@ -592,7 +592,7 @@ void QtHostInterface::threadEntryPoint()
|
||||||
|
|
||||||
// execute the system, polling events inbetween frames
|
// execute the system, polling events inbetween frames
|
||||||
// simulate the system if not paused
|
// simulate the system if not paused
|
||||||
RunFrame();
|
m_system->RunFrame();
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
|
@ -611,7 +611,7 @@ void QtHostInterface::threadEntryPoint()
|
||||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
if (m_speed_limiter_enabled)
|
if (m_speed_limiter_enabled)
|
||||||
Throttle();
|
m_system->Throttle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,8 @@ Q_SIGNALS:
|
||||||
void gameListRefreshed();
|
void gameListRefreshed();
|
||||||
void toggleFullscreenRequested();
|
void toggleFullscreenRequested();
|
||||||
void recreateDisplayWidgetRequested(bool create_device_context);
|
void recreateDisplayWidgetRequested(bool create_device_context);
|
||||||
void performanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, float worst_frame_time);
|
void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time,
|
||||||
|
float worst_frame_time);
|
||||||
void runningGameChanged(QString filename, QString game_code, QString game_title);
|
void runningGameChanged(QString filename, QString game_code, QString game_title);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
|
@ -90,7 +91,7 @@ private Q_SLOTS:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SwitchGPURenderer() override;
|
void SwitchGPURenderer() override;
|
||||||
void OnPerformanceCountersUpdated() override;
|
void OnSystemPerformanceCountersUpdated() override;
|
||||||
void OnRunningGameChanged() override;
|
void OnRunningGameChanged() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -200,7 +200,8 @@ void SDLHostInterface::SwitchGPURenderer()
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateFullscreen();
|
UpdateFullscreen();
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -886,19 +887,20 @@ void SDLHostInterface::DrawMainMenuBar()
|
||||||
{
|
{
|
||||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 210.0f);
|
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 210.0f);
|
||||||
|
|
||||||
const u32 rounded_speed = static_cast<u32>(std::round(m_speed));
|
const float speed = m_system->GetEmulationSpeed();
|
||||||
if (m_speed < 90.0f)
|
const u32 rounded_speed = static_cast<u32>(std::round(speed));
|
||||||
|
if (speed < 90.0f)
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||||
else if (m_speed < 110.0f)
|
else if (speed < 110.0f)
|
||||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%u%%", rounded_speed);
|
||||||
else
|
else
|
||||||
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%u%%", rounded_speed);
|
||||||
|
|
||||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 165.0f);
|
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 165.0f);
|
||||||
ImGui::Text("FPS: %.2f", m_fps);
|
ImGui::Text("FPS: %.2f", m_system->GetFPS());
|
||||||
|
|
||||||
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 80.0f);
|
ImGui::SetCursorPosX(ImGui::GetIO().DisplaySize.x - 80.0f);
|
||||||
ImGui::Text("VPS: %.2f", m_vps);
|
ImGui::Text("VPS: %.2f", m_system->GetVPS());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1458,7 +1460,8 @@ void SDLHostInterface::DoResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControllerMapping();
|
UpdateControllerMapping();
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,7 +1481,8 @@ void SDLHostInterface::DoStartDisc()
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControllerMapping();
|
UpdateControllerMapping();
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1494,7 +1498,8 @@ void SDLHostInterface::DoStartBIOS()
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControllerMapping();
|
UpdateControllerMapping();
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1511,7 +1516,8 @@ void SDLHostInterface::DoChangeDisc()
|
||||||
else
|
else
|
||||||
AddOSDMessage("Failed to switch CD. The log may contain further information.");
|
AddOSDMessage("Failed to switch CD. The log may contain further information.");
|
||||||
|
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1531,7 +1537,8 @@ void SDLHostInterface::DoLoadState(u32 index)
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControllerMapping();
|
UpdateControllerMapping();
|
||||||
ResetPerformanceCounters();
|
if (m_system)
|
||||||
|
m_system->ResetPerformanceCounters();
|
||||||
ClearImGuiFocus();
|
ClearImGuiFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1549,7 +1556,7 @@ void SDLHostInterface::DoTogglePause()
|
||||||
|
|
||||||
m_paused = !m_paused;
|
m_paused = !m_paused;
|
||||||
if (!m_paused)
|
if (!m_paused)
|
||||||
m_fps_timer.Reset();
|
m_system->ResetPerformanceCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDLHostInterface::DoFrameStep()
|
void SDLHostInterface::DoFrameStep()
|
||||||
|
@ -1584,7 +1591,7 @@ void SDLHostInterface::Run()
|
||||||
|
|
||||||
if (m_system && !m_paused)
|
if (m_system && !m_paused)
|
||||||
{
|
{
|
||||||
RunFrame();
|
m_system->RunFrame();
|
||||||
if (m_frame_step_request)
|
if (m_frame_step_request)
|
||||||
{
|
{
|
||||||
m_frame_step_request = false;
|
m_frame_step_request = false;
|
||||||
|
@ -1611,7 +1618,7 @@ void SDLHostInterface::Run()
|
||||||
m_system->GetGPU()->RestoreGraphicsAPIState();
|
m_system->GetGPU()->RestoreGraphicsAPIState();
|
||||||
|
|
||||||
if (m_speed_limiter_enabled)
|
if (m_speed_limiter_enabled)
|
||||||
Throttle();
|
m_system->Throttle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue