Merge pull request #9417 from Filoppi/input-1
Fix FPS counter and Game Window speed % breaking on pause/unpause
This commit is contained in:
commit
049b92b7ef
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
|
@ -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); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue