Core/HW: Add time tracking thread in CPU manager

Introduce method to track the time played for a game via time differences and TimePlayed methods. Threads are synchronized via Common::Event.
This commit is contained in:
Aneesh Maganti 2025-02-10 15:28:32 -05:00 committed by Admiral H. Curtiss
parent 3c44fe592b
commit fffb499da2
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
2 changed files with 58 additions and 0 deletions

View File

@ -10,12 +10,16 @@
#include "AudioCommon/AudioCommon.h" #include "AudioCommon/AudioCommon.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Event.h" #include "Common/Event.h"
#include "Common/Timer.h"
#include "Core/CPUThreadConfigCallback.h" #include "Core/CPUThreadConfigCallback.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/Host.h" #include "Core/Host.h"
#include "Core/PowerPC/GDBStub.h" #include "Core/PowerPC/GDBStub.h"
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "Core/System.h" #include "Core/System.h"
#include "Core/TimePlayed.h"
#include "VideoCommon/Fifo.h" #include "VideoCommon/Fifo.h"
namespace CPU namespace CPU
@ -63,6 +67,41 @@ void CPUManager::ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock)
} }
} }
void CPUManager::StartTimePlayedTimer()
{
// Steady clock for greater accuracy of timing
std::chrono::steady_clock timer;
auto prev_time = timer.now();
while (true)
{
const std::string game_id = SConfig::GetInstance().GetGameID();
TimePlayed time_played(game_id);
auto curr_time = timer.now();
// Check that emulation is not paused
// If the emulation is paused, wait for SetStepping() to reactivate
if (m_state == State::Running)
{
const auto diff_time =
std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - prev_time);
time_played.AddTime(diff_time);
}
else if (m_state == State::Stepping)
{
m_time_played_finish_sync.Wait();
curr_time = timer.now();
}
prev_time = curr_time;
if (m_state == State::PowerDown)
return;
m_time_played_finish_sync.WaitFor(std::chrono::seconds(30));
}
}
void CPUManager::Run() void CPUManager::Run()
{ {
auto& power_pc = m_system.GetPowerPC(); auto& power_pc = m_system.GetPowerPC();
@ -71,6 +110,13 @@ void CPUManager::Run()
// We can't rely on PowerPC::Init doing it, since it's called from EmuThread. // We can't rely on PowerPC::Init doing it, since it's called from EmuThread.
PowerPC::RoundingModeUpdated(power_pc.GetPPCState()); PowerPC::RoundingModeUpdated(power_pc.GetPPCState());
// Start a separate time tracker thread
std::thread timing;
if (Config::Get(Config::MAIN_TIME_TRACKING))
{
timing = std::thread(&CPUManager::StartTimePlayedTimer, this);
}
std::unique_lock state_lock(m_state_change_lock); std::unique_lock state_lock(m_state_change_lock);
while (m_state != State::PowerDown) while (m_state != State::PowerDown)
{ {
@ -165,6 +211,13 @@ void CPUManager::Run()
break; break;
} }
} }
if (timing.joinable())
{
m_time_played_finish_sync.Set();
timing.join();
}
state_lock.unlock(); state_lock.unlock();
Host_UpdateDisasmDialog(); Host_UpdateDisasmDialog();
} }
@ -266,6 +319,7 @@ void CPUManager::SetStepping(bool stepping)
else if (SetStateLocked(State::Running)) else if (SetStateLocked(State::Running))
{ {
m_state_cpu_cvar.notify_one(); m_state_cpu_cvar.notify_one();
m_time_played_finish_sync.Set();
RunAdjacentSystems(true); RunAdjacentSystems(true);
} }
} }

View File

@ -8,6 +8,8 @@
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include "Common/Event.h"
namespace Common namespace Common
{ {
class Event; class Event;
@ -102,6 +104,7 @@ public:
private: private:
void FlushStepSyncEventLocked(); void FlushStepSyncEventLocked();
void ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock); void ExecutePendingJobs(std::unique_lock<std::mutex>& state_lock);
void StartTimePlayedTimer();
void RunAdjacentSystems(bool running); void RunAdjacentSystems(bool running);
bool SetStateLocked(State s); bool SetStateLocked(State s);
@ -133,6 +136,7 @@ private:
bool m_state_cpu_step_instruction = false; bool m_state_cpu_step_instruction = false;
Common::Event* m_state_cpu_step_instruction_sync = nullptr; Common::Event* m_state_cpu_step_instruction_sync = nullptr;
std::queue<std::function<void()>> m_pending_jobs; std::queue<std::function<void()>> m_pending_jobs;
Common::Event m_time_played_finish_sync;
Core::System& m_system; Core::System& m_system;
}; };