diff --git a/Source/Core/Core/HW/CPU.cpp b/Source/Core/Core/HW/CPU.cpp index 1eae912476..487a33c6ff 100644 --- a/Source/Core/Core/HW/CPU.cpp +++ b/Source/Core/Core/HW/CPU.cpp @@ -10,12 +10,16 @@ #include "AudioCommon/AudioCommon.h" #include "Common/CommonTypes.h" #include "Common/Event.h" +#include "Common/Timer.h" #include "Core/CPUThreadConfigCallback.h" +#include "Core/Config/MainSettings.h" +#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" #include "Core/PowerPC/GDBStub.h" #include "Core/PowerPC/PowerPC.h" #include "Core/System.h" +#include "Core/TimePlayed.h" #include "VideoCommon/Fifo.h" namespace CPU @@ -63,6 +67,41 @@ void CPUManager::ExecutePendingJobs(std::unique_lock& 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(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() { 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. 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); while (m_state != State::PowerDown) { @@ -165,6 +211,13 @@ void CPUManager::Run() break; } } + + if (timing.joinable()) + { + m_time_played_finish_sync.Set(); + timing.join(); + } + state_lock.unlock(); Host_UpdateDisasmDialog(); } @@ -266,6 +319,7 @@ void CPUManager::SetStepping(bool stepping) else if (SetStateLocked(State::Running)) { m_state_cpu_cvar.notify_one(); + m_time_played_finish_sync.Set(); RunAdjacentSystems(true); } } diff --git a/Source/Core/Core/HW/CPU.h b/Source/Core/Core/HW/CPU.h index 57e8a31a29..b73471136d 100644 --- a/Source/Core/Core/HW/CPU.h +++ b/Source/Core/Core/HW/CPU.h @@ -8,6 +8,8 @@ #include #include +#include "Common/Event.h" + namespace Common { class Event; @@ -102,6 +104,7 @@ public: private: void FlushStepSyncEventLocked(); void ExecutePendingJobs(std::unique_lock& state_lock); + void StartTimePlayedTimer(); void RunAdjacentSystems(bool running); bool SetStateLocked(State s); @@ -133,6 +136,7 @@ private: bool m_state_cpu_step_instruction = false; Common::Event* m_state_cpu_step_instruction_sync = nullptr; std::queue> m_pending_jobs; + Common::Event m_time_played_finish_sync; Core::System& m_system; };