Merge pull request #9417 from Filoppi/input-1

Fix FPS counter and Game Window speed % breaking on pause/unpause
This commit is contained in:
Léo Lam 2021-05-07 15:08:01 +02:00 committed by GitHub
commit 049b92b7ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 125 additions and 31 deletions

View File

@ -103,11 +103,12 @@ static bool s_hardware_initialized = false;
static bool s_is_started = false; static bool s_is_started = false;
static Common::Flag s_is_booting; static Common::Flag s_is_booting;
static std::thread s_emu_thread; static std::thread s_emu_thread;
static StateChangedCallbackFunc s_on_state_changed_callback; static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
static std::thread s_cpu_thread; static std::thread s_cpu_thread;
static bool s_request_refresh_info = false; static bool s_request_refresh_info = false;
static bool s_is_throttler_temp_disabled = false; static bool s_is_throttler_temp_disabled = false;
static std::atomic<double> s_last_actual_emulation_speed{1.0};
static bool s_frame_step = false; static bool s_frame_step = false;
static std::atomic<bool> s_stop_frame_step; static std::atomic<bool> s_stop_frame_step;
@ -138,6 +139,11 @@ void SetIsThrottlerTempDisabled(bool disable)
s_is_throttler_temp_disabled = disable; s_is_throttler_temp_disabled = disable;
} }
double GetActualEmulationSpeed()
{
return s_last_actual_emulation_speed;
}
void FrameUpdateOnCPUThread() void FrameUpdateOnCPUThread()
{ {
if (NetPlay::IsNetPlayRunning()) if (NetPlay::IsNetPlayRunning())
@ -267,9 +273,9 @@ void Stop() // - Hammertime!
s_is_stopping = true; s_is_stopping = true;
// Notify state changed callback s_timer.Stop();
if (s_on_state_changed_callback)
s_on_state_changed_callback(State::Stopping); CallOnStateChangedCallbacks(State::Stopping);
// Dump left over jobs // Dump left over jobs
HostDispatchJobs(); HostDispatchJobs();
@ -291,6 +297,8 @@ void Stop() // - Hammertime!
g_video_backend->Video_ExitLoop(); g_video_backend->Video_ExitLoop();
} }
s_last_actual_emulation_speed = 1.0;
} }
void DeclareAsCPUThread() void DeclareAsCPUThread()
@ -422,16 +430,14 @@ static void FifoPlayerThread(const std::optional<std::string>& savestate_path,
static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi) static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi)
{ {
const SConfig& core_parameter = SConfig::GetInstance(); const SConfig& core_parameter = SConfig::GetInstance();
if (s_on_state_changed_callback) CallOnStateChangedCallbacks(State::Starting);
s_on_state_changed_callback(State::Starting);
Common::ScopeGuard flag_guard{[] { Common::ScopeGuard flag_guard{[] {
s_is_booting.Clear(); s_is_booting.Clear();
s_is_started = false; s_is_started = false;
s_is_stopping = false; s_is_stopping = false;
s_wants_determinism = false; s_wants_determinism = false;
if (s_on_state_changed_callback) CallOnStateChangedCallbacks(State::Uninitialized);
s_on_state_changed_callback(State::Uninitialized);
INFO_LOG_FMT(CONSOLE, "Stop\t\t---- Shutdown complete ----"); INFO_LOG_FMT(CONSOLE, "Stop\t\t---- Shutdown complete ----");
}}; }};
@ -662,18 +668,27 @@ void SetState(State state)
CPU::EnableStepping(true); // Break CPU::EnableStepping(true); // Break
Wiimote::Pause(); Wiimote::Pause();
ResetRumble(); ResetRumble();
s_timer.Update();
break; break;
case State::Running: case State::Running:
CPU::EnableStepping(false); CPU::EnableStepping(false);
Wiimote::Resume(); Wiimote::Resume();
if (!s_timer.IsRunning())
{
s_timer.Start();
}
else
{
// Add time difference from the last pause
s_timer.AddTimeDifference();
}
break; break;
default: default:
PanicAlertFmt("Invalid state"); PanicAlertFmt("Invalid state");
break; break;
} }
if (s_on_state_changed_callback) CallOnStateChangedCallbacks(GetState());
s_on_state_changed_callback(GetState());
} }
State GetState() State GetState()
@ -844,13 +859,13 @@ void RunOnCPUThread(std::function<void()> function, bool wait_for_completion)
void VideoThrottle() void VideoThrottle()
{ {
// Update info per second // Update info per second
u32 ElapseTime = (u32)s_timer.GetTimeDifference(); u32 ElapseTime = (u32)s_timer.GetTimeElapsed();
if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info) if ((ElapseTime >= 1000 && s_drawn_video.load() > 0) || s_request_refresh_info)
{ {
UpdateTitle(); s_timer.Start();
UpdateTitle(ElapseTime);
// Reset counter
s_timer.Update();
s_drawn_frame.store(0); s_drawn_frame.store(0);
s_drawn_video.store(0); s_drawn_video.store(0);
} }
@ -862,8 +877,10 @@ void VideoThrottle()
// Called from Renderer::Swap (GPU thread) when a new (non-duplicate) // Called from Renderer::Swap (GPU thread) when a new (non-duplicate)
// frame is presented to the host screen // frame is presented to the host screen
void Callback_FramePresented() void Callback_FramePresented(double actual_emulation_speed)
{ {
s_last_actual_emulation_speed = actual_emulation_speed;
s_drawn_frame++; s_drawn_frame++;
s_stop_frame_step.store(true); s_stop_frame_step.store(true);
} }
@ -884,15 +901,13 @@ void Callback_NewField()
{ {
s_frame_step = false; s_frame_step = false;
CPU::Break(); CPU::Break();
if (s_on_state_changed_callback) CallOnStateChangedCallbacks(Core::GetState());
s_on_state_changed_callback(Core::GetState());
} }
} }
} }
void UpdateTitle() void UpdateTitle(u32 ElapseTime)
{ {
u32 ElapseTime = (u32)s_timer.GetTimeDifference();
s_request_refresh_info = false; s_request_refresh_info = false;
SConfig& _CoreParameter = SConfig::GetInstance(); SConfig& _CoreParameter = SConfig::GetInstance();
@ -982,9 +997,38 @@ void Shutdown()
HostDispatchJobs(); HostDispatchJobs();
} }
void SetOnStateChangedCallback(StateChangedCallbackFunc callback) int AddOnStateChangedCallback(StateChangedCallbackFunc callback)
{ {
s_on_state_changed_callback = std::move(callback); for (size_t i = 0; i < s_on_state_changed_callbacks.size(); ++i)
{
if (!s_on_state_changed_callbacks[i])
{
s_on_state_changed_callbacks[i] = std::move(callback);
return int(i);
}
}
s_on_state_changed_callbacks.emplace_back(std::move(callback));
return int(s_on_state_changed_callbacks.size()) - 1;
}
bool RemoveOnStateChangedCallback(int* handle)
{
if (handle && *handle >= 0 && s_on_state_changed_callbacks.size() > *handle)
{
s_on_state_changed_callbacks[*handle] = StateChangedCallbackFunc();
*handle = -1;
return true;
}
return false;
}
void CallOnStateChangedCallbacks(Core::State state)
{
for (const StateChangedCallbackFunc& on_state_changed_callback : s_on_state_changed_callbacks)
{
if (on_state_changed_callback)
on_state_changed_callback(state);
}
} }
void UpdateWantDeterminism(bool initial) void UpdateWantDeterminism(bool initial)

View File

@ -25,7 +25,10 @@ namespace Core
bool GetIsThrottlerTempDisabled(); bool GetIsThrottlerTempDisabled();
void SetIsThrottlerTempDisabled(bool disable); void SetIsThrottlerTempDisabled(bool disable);
void Callback_FramePresented(); // Returns the latest emulation speed (1 is full speed) (swings a lot)
double GetActualEmulationSpeed();
void Callback_FramePresented(double actual_emulation_speed = 1.0);
void Callback_NewField(); void Callback_NewField();
enum class State enum class State
@ -123,7 +126,7 @@ void OnFrameEnd();
void VideoThrottle(); void VideoThrottle();
void RequestRefreshInfo(); void RequestRefreshInfo();
void UpdateTitle(); void UpdateTitle(u32 ElapseTime);
// Run a function as the CPU thread. // Run a function as the CPU thread.
// //
@ -140,7 +143,11 @@ void RunOnCPUThread(std::function<void()> function, bool wait_for_completion);
// for calling back into UI code without introducing a dependency on it in core // for calling back into UI code without introducing a dependency on it in core
using StateChangedCallbackFunc = std::function<void(Core::State)>; using StateChangedCallbackFunc = std::function<void(Core::State)>;
void SetOnStateChangedCallback(StateChangedCallbackFunc callback); // Returns a handle
int AddOnStateChangedCallback(StateChangedCallbackFunc callback);
// Also invalidates the handle
bool RemoveOnStateChangedCallback(int* handle);
void CallOnStateChangedCallbacks(Core::State state);
// Run on the Host thread when the factors change. [NOT THREADSAFE] // Run on the Host thread when the factors change. [NOT THREADSAFE]
void UpdateWantDeterminism(bool initial = false); void UpdateWantDeterminism(bool initial = false);

View File

@ -224,7 +224,7 @@ int main(int argc, char* argv[])
return 1; return 1;
} }
Core::SetOnStateChangedCallback([](Core::State state) { Core::AddOnStateChangedCallback([](Core::State state) {
if (state == Core::State::Uninitialized) if (state == Core::State::Uninitialized)
s_platform->Stop(); s_platform->Stop();
}); });

View File

@ -36,7 +36,7 @@
Settings::Settings() Settings::Settings()
{ {
qRegisterMetaType<Core::State>(); qRegisterMetaType<Core::State>();
Core::SetOnStateChangedCallback([this](Core::State new_state) { Core::AddOnStateChangedCallback([this](Core::State new_state) {
QueueOnObject(this, [this, new_state] { emit EmulationStateChanged(new_state); }); QueueOnObject(this, [this, new_state] { emit EmulationStateChanged(new_state); });
}); });

View File

@ -8,6 +8,7 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/Timer.h" #include "Common/Timer.h"
#include "Core/Core.h"
#include "VideoCommon/FPSCounter.h" #include "VideoCommon/FPSCounter.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
@ -16,6 +17,18 @@ static constexpr u64 FPS_REFRESH_INTERVAL = 250000;
FPSCounter::FPSCounter() FPSCounter::FPSCounter()
{ {
m_last_time = Common::Timer::GetTimeUs(); m_last_time = Common::Timer::GetTimeUs();
m_on_state_changed_handle = Core::AddOnStateChangedCallback([this](Core::State state) {
if (state == Core::State::Paused)
SetPaused(true);
else if (state == Core::State::Running)
SetPaused(false);
});
}
FPSCounter::~FPSCounter()
{
Core::RemoveOnStateChangedCallback(&m_on_state_changed_handle);
} }
void FPSCounter::LogRenderTimeToFile(u64 val) void FPSCounter::LogRenderTimeToFile(u64 val)
@ -31,8 +44,9 @@ void FPSCounter::LogRenderTimeToFile(u64 val)
void FPSCounter::Update() void FPSCounter::Update()
{ {
u64 time = Common::Timer::GetTimeUs(); const u64 time = Common::Timer::GetTimeUs();
u64 diff = time - m_last_time; const u64 diff = time - m_last_time;
m_time_diff_secs = static_cast<double>(diff / 1000000.0);
if (g_ActiveConfig.bLogRenderTimeToFile) if (g_ActiveConfig.bLogRenderTimeToFile)
LogRenderTimeToFile(diff); LogRenderTimeToFile(diff);
@ -47,3 +61,17 @@ void FPSCounter::Update()
m_time_since_update = 0; m_time_since_update = 0;
} }
} }
void FPSCounter::SetPaused(bool paused)
{
if (paused)
{
m_last_time_pause = Common::Timer::GetTimeUs();
}
else
{
const u64 time = Common::Timer::GetTimeUs();
const u64 diff = time - m_last_time_pause;
m_last_time += diff;
}
}

View File

@ -11,20 +11,30 @@
class FPSCounter class FPSCounter
{ {
public: public:
// Initializes the FPS counter.
FPSCounter(); FPSCounter();
~FPSCounter();
FPSCounter(const FPSCounter&) = delete;
FPSCounter& operator=(const FPSCounter&) = delete;
FPSCounter(FPSCounter&&) = delete;
FPSCounter& operator=(FPSCounter&&) = delete;
// Called when a frame is rendered (updated every second). // Called when a frame is rendered (updated every second).
void Update(); void Update();
float GetFPS() const { return m_fps; } float GetFPS() const { return m_fps; }
double GetDeltaTime() const { return m_time_diff_secs; }
private: private:
void SetPaused(bool paused);
u64 m_last_time = 0; u64 m_last_time = 0;
u64 m_time_since_update = 0; u64 m_time_since_update = 0;
u64 m_last_time_pause = 0;
u32 m_frame_counter = 0; u32 m_frame_counter = 0;
float m_fps = 0; int m_on_state_changed_handle = -1;
float m_fps = 0.f;
std::ofstream m_bench_file; std::ofstream m_bench_file;
double m_time_diff_secs = 0.0;
void LogRenderTimeToFile(u64 val); void LogRenderTimeToFile(u64 val);
}; };

View File

@ -1335,7 +1335,12 @@ void Renderer::Swap(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_height, u6
{ {
// Remove stale EFB/XFB copies. // Remove stale EFB/XFB copies.
g_texture_cache->Cleanup(m_frame_count); g_texture_cache->Cleanup(m_frame_count);
Core::Callback_FramePresented(); const double last_speed_denominator =
m_fps_counter.GetDeltaTime() * VideoInterface::GetTargetRefreshRate();
// The denominator should always be > 0 but if it's not, just return 1
const double last_speed =
last_speed_denominator > 0.0 ? (1.0 / last_speed_denominator) : 1.0;
Core::Callback_FramePresented(last_speed);
} }
// Handle any config changes, this gets propagated to the backend. // Handle any config changes, this gets propagated to the backend.