diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index fe06a3329d..261b5b55e6 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -50,38 +50,46 @@ static bool operator<(const Event& left, const Event& right) return std::tie(left.time, left.fifo_order) < std::tie(right.time, right.fifo_order); } -// unordered_map stores each element separately as a linked list node so pointers to elements -// remain stable regardless of rehashes/resizing. -static std::unordered_map s_event_types; - -// STATE_TO_SAVE -// The queue is a min-heap using std::make_heap/push_heap/pop_heap. -// We don't use std::priority_queue because we need to be able to serialize, unserialize and -// erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated -// by the standard adaptor class. -static std::vector s_event_queue; -static u64 s_event_fifo_id; -static std::mutex s_ts_write_lock; -static Common::SPSCQueue s_ts_queue; - -static float s_last_OC_factor; static constexpr int MAX_SLICE_LENGTH = 20000; -static s64 s_idled_cycles; -static u32 s_fake_dec_start_value; -static u64 s_fake_dec_start_ticks; +struct CoreTimingState::Data +{ + // unordered_map stores each element separately as a linked list node so pointers to elements + // remain stable regardless of rehashes/resizing. + std::unordered_map event_types; -// Are we in a function that has been called from Advance() -static bool s_is_global_timer_sane; + // STATE_TO_SAVE + // The queue is a min-heap using std::make_heap/push_heap/pop_heap. + // We don't use std::priority_queue because we need to be able to serialize, unserialize and + // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't accomodated + // by the standard adaptor class. + std::vector event_queue; + u64 event_fifo_id; + std::mutex ts_write_lock; + Common::SPSCQueue ts_queue; -Globals g; + float last_oc_factor; -static EventType* s_ev_lost = nullptr; + s64 idled_cycles; + u32 fake_dec_start_value; + u64 fake_dec_start_ticks; -static size_t s_registered_config_callback_id; -static float s_config_OC_factor; -static float s_config_OC_inv_factor; -static bool s_config_sync_on_skip_idle; + // Are we in a function that has been called from Advance() + bool is_global_timer_sane; + + EventType* ev_lost = nullptr; + + size_t registered_config_callback_id; + float config_oc_factor; + float config_oc_inv_factor; + bool config_sync_on_skip_idle; +}; + +CoreTimingState::CoreTimingState() : m_data(std::make_unique()) +{ +} + +CoreTimingState::~CoreTimingState() = default; static void EmptyTimedCallback(Core::System& system, u64 userdata, s64 cyclesLate) { @@ -94,26 +102,28 @@ static void EmptyTimedCallback(Core::System& system, u64 userdata, s64 cyclesLat // // Technically it might be more accurate to call this changing the IPC instead of the CPU speed, // but the effect is largely the same. -static int DowncountToCycles(int downcount) +static int DowncountToCycles(CoreTiming::Globals& g, int downcount) { return static_cast(downcount * g.last_OC_factor_inverted); } -static int CyclesToDowncount(int cycles) +static int CyclesToDowncount(CoreTiming::CoreTimingState::Data& state, int cycles) { - return static_cast(cycles * s_last_OC_factor); + return static_cast(cycles * state.last_oc_factor); } EventType* RegisterEvent(const std::string& name, TimedCallback callback) { + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + // check for existing type with same name. // we want event type names to remain unique so that we can use them for serialization. - ASSERT_MSG(POWERPC, s_event_types.find(name) == s_event_types.end(), + ASSERT_MSG(POWERPC, state.event_types.find(name) == state.event_types.end(), "CoreTiming Event \"{}\" is already registered. Events should only be registered " "during Init to avoid breaking save states.", name); - auto info = s_event_types.emplace(name, EventType{callback, nullptr}); + auto info = state.event_types.emplace(name, EventType{callback, nullptr}); EventType* event_type = &info.first->second; event_type->name = &info.first->first; return event_type; @@ -121,68 +131,80 @@ EventType* RegisterEvent(const std::string& name, TimedCallback callback) void UnregisterAllEvents() { - ASSERT_MSG(POWERPC, s_event_queue.empty(), "Cannot unregister events with events pending"); - s_event_types.clear(); + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + + ASSERT_MSG(POWERPC, state.event_queue.empty(), "Cannot unregister events with events pending"); + state.event_types.clear(); } void Init() { - s_registered_config_callback_id = + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + + state.registered_config_callback_id = Config::AddConfigChangedCallback([]() { Core::RunAsCPUThread([]() { RefreshConfig(); }); }); RefreshConfig(); - s_last_OC_factor = s_config_OC_factor; - g.last_OC_factor_inverted = s_config_OC_inv_factor; - PowerPC::ppcState.downcount = CyclesToDowncount(MAX_SLICE_LENGTH); + state.last_oc_factor = state.config_oc_factor; + g.last_OC_factor_inverted = state.config_oc_inv_factor; + PowerPC::ppcState.downcount = CyclesToDowncount(state, MAX_SLICE_LENGTH); g.slice_length = MAX_SLICE_LENGTH; g.global_timer = 0; - s_idled_cycles = 0; + state.idled_cycles = 0; // The time between CoreTiming being intialized and the first call to Advance() is considered // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before // executing the first PPC cycle of each slice to prepare the slice length and downcount for // that slice. - s_is_global_timer_sane = true; + state.is_global_timer_sane = true; - s_event_fifo_id = 0; - s_ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); + state.event_fifo_id = 0; + state.ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback); } void Shutdown() { - std::lock_guard lk(s_ts_write_lock); + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + std::lock_guard lk(state.ts_write_lock); MoveEvents(); ClearPendingEvents(); UnregisterAllEvents(); - Config::RemoveConfigChangedCallback(s_registered_config_callback_id); + Config::RemoveConfigChangedCallback(state.registered_config_callback_id); } void RefreshConfig() { - s_config_OC_factor = + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + state.config_oc_factor = Config::Get(Config::MAIN_OVERCLOCK_ENABLE) ? Config::Get(Config::MAIN_OVERCLOCK) : 1.0f; - s_config_OC_inv_factor = 1.0f / s_config_OC_factor; - s_config_sync_on_skip_idle = Config::Get(Config::MAIN_SYNC_ON_SKIP_IDLE); + state.config_oc_inv_factor = 1.0f / state.config_oc_factor; + state.config_sync_on_skip_idle = Config::Get(Config::MAIN_SYNC_ON_SKIP_IDLE); } void DoState(PointerWrap& p) { - std::lock_guard lk(s_ts_write_lock); + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + + std::lock_guard lk(state.ts_write_lock); p.Do(g.slice_length); p.Do(g.global_timer); - p.Do(s_idled_cycles); - p.Do(s_fake_dec_start_value); - p.Do(s_fake_dec_start_ticks); + p.Do(state.idled_cycles); + p.Do(state.fake_dec_start_value); + p.Do(state.fake_dec_start_ticks); p.Do(g.fake_TB_start_value); p.Do(g.fake_TB_start_ticks); - p.Do(s_last_OC_factor); - g.last_OC_factor_inverted = 1.0f / s_last_OC_factor; - p.Do(s_event_fifo_id); + p.Do(state.last_oc_factor); + g.last_OC_factor_inverted = 1.0f / state.last_oc_factor; + p.Do(state.event_fifo_id); p.DoMarker("CoreTimingData"); MoveEvents(); - p.DoEachElement(s_event_queue, [](PointerWrap& pw, Event& ev) { + p.DoEachElement(state.event_queue, [&state](PointerWrap& pw, Event& ev) { pw.Do(ev.time); pw.Do(ev.fifo_order); @@ -199,8 +221,8 @@ void DoState(PointerWrap& p) pw.Do(name); if (pw.IsReadMode()) { - auto itr = s_event_types.find(name); - if (itr != s_event_types.end()) + auto itr = state.event_types.find(name); + if (itr != state.event_types.end()) { ev.type = &itr->second; } @@ -209,7 +231,7 @@ void DoState(PointerWrap& p) WARN_LOG_FMT(POWERPC, "Lost event from savestate because its type, \"{}\", has not been registered.", name); - ev.type = s_ev_lost; + ev.type = state.ev_lost; } } }); @@ -219,17 +241,21 @@ void DoState(PointerWrap& p) // The exact layout of the heap in memory is implementation defined, therefore it is platform // and library version specific. if (p.IsReadMode()) - std::make_heap(s_event_queue.begin(), s_event_queue.end(), std::greater()); + std::make_heap(state.event_queue.begin(), state.event_queue.end(), std::greater()); } // This should only be called from the CPU thread. If you are calling // it from any other thread, you are doing something evil u64 GetTicks() { + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + u64 ticks = static_cast(g.global_timer); - if (!s_is_global_timer_sane) + if (!state.is_global_timer_sane) { - int downcount = DowncountToCycles(PowerPC::ppcState.downcount); + int downcount = DowncountToCycles(g, PowerPC::ppcState.downcount); ticks += g.slice_length - downcount; } return ticks; @@ -237,18 +263,24 @@ u64 GetTicks() u64 GetIdleTicks() { - return static_cast(s_idled_cycles); + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + return static_cast(state.idled_cycles); } void ClearPendingEvents() { - s_event_queue.clear(); + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + state.event_queue.clear(); } void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata, FromThread from) { ASSERT_MSG(POWERPC, event_type, "Event type is nullptr, will crash now."); + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + bool from_cpu_thread; if (from == FromThread::ANY) { @@ -267,11 +299,11 @@ void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata, s64 timeout = GetTicks() + cycles_into_future; // If this event needs to be scheduled before the next advance(), force one early - if (!s_is_global_timer_sane) + if (!state.is_global_timer_sane) ForceExceptionCheck(cycles_into_future); - s_event_queue.emplace_back(Event{timeout, s_event_fifo_id++, userdata, event_type}); - std::push_heap(s_event_queue.begin(), s_event_queue.end(), std::greater()); + state.event_queue.emplace_back(Event{timeout, state.event_fifo_id++, userdata, event_type}); + std::push_heap(state.event_queue.begin(), state.event_queue.end(), std::greater()); } else { @@ -283,21 +315,23 @@ void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata, *event_type->name); } - std::lock_guard lk(s_ts_write_lock); - s_ts_queue.Push(Event{g.global_timer + cycles_into_future, 0, userdata, event_type}); + std::lock_guard lk(state.ts_write_lock); + state.ts_queue.Push(Event{g.global_timer + cycles_into_future, 0, userdata, event_type}); } } void RemoveEvent(EventType* event_type) { - auto itr = std::remove_if(s_event_queue.begin(), s_event_queue.end(), + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + + auto itr = std::remove_if(state.event_queue.begin(), state.event_queue.end(), [&](const Event& e) { return e.type == event_type; }); // Removing random items breaks the invariant so we have to re-establish it. - if (itr != s_event_queue.end()) + if (itr != state.event_queue.end()) { - s_event_queue.erase(itr, s_event_queue.end()); - std::make_heap(s_event_queue.begin(), s_event_queue.end(), std::greater()); + state.event_queue.erase(itr, state.event_queue.end()); + std::make_heap(state.event_queue.begin(), state.event_queue.end(), std::greater()); } } @@ -309,56 +343,65 @@ void RemoveAllEvents(EventType* event_type) void ForceExceptionCheck(s64 cycles) { + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + cycles = std::max(0, cycles); - if (DowncountToCycles(PowerPC::ppcState.downcount) > cycles) + if (DowncountToCycles(g, PowerPC::ppcState.downcount) > cycles) { // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int here. // Account for cycles already executed by adjusting the g.slice_length - g.slice_length -= DowncountToCycles(PowerPC::ppcState.downcount) - static_cast(cycles); - PowerPC::ppcState.downcount = CyclesToDowncount(static_cast(cycles)); + g.slice_length -= DowncountToCycles(g, PowerPC::ppcState.downcount) - static_cast(cycles); + PowerPC::ppcState.downcount = CyclesToDowncount(state, static_cast(cycles)); } } void MoveEvents() { - for (Event ev; s_ts_queue.Pop(ev);) + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + for (Event ev; state.ts_queue.Pop(ev);) { - ev.fifo_order = s_event_fifo_id++; - s_event_queue.emplace_back(std::move(ev)); - std::push_heap(s_event_queue.begin(), s_event_queue.end(), std::greater()); + ev.fifo_order = state.event_fifo_id++; + state.event_queue.emplace_back(std::move(ev)); + std::push_heap(state.event_queue.begin(), state.event_queue.end(), std::greater()); } } void Advance() { + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + MoveEvents(); - int cyclesExecuted = g.slice_length - DowncountToCycles(PowerPC::ppcState.downcount); + int cyclesExecuted = g.slice_length - DowncountToCycles(g, PowerPC::ppcState.downcount); g.global_timer += cyclesExecuted; - s_last_OC_factor = s_config_OC_factor; - g.last_OC_factor_inverted = s_config_OC_inv_factor; + state.last_oc_factor = state.config_oc_factor; + g.last_OC_factor_inverted = state.config_oc_inv_factor; g.slice_length = MAX_SLICE_LENGTH; - s_is_global_timer_sane = true; + state.is_global_timer_sane = true; - while (!s_event_queue.empty() && s_event_queue.front().time <= g.global_timer) + while (!state.event_queue.empty() && state.event_queue.front().time <= g.global_timer) { - Event evt = std::move(s_event_queue.front()); - std::pop_heap(s_event_queue.begin(), s_event_queue.end(), std::greater()); - s_event_queue.pop_back(); - evt.type->callback(Core::System::GetInstance(), evt.userdata, g.global_timer - evt.time); + Event evt = std::move(state.event_queue.front()); + std::pop_heap(state.event_queue.begin(), state.event_queue.end(), std::greater()); + state.event_queue.pop_back(); + evt.type->callback(system, evt.userdata, g.global_timer - evt.time); } - s_is_global_timer_sane = false; + state.is_global_timer_sane = false; // Still events left (scheduled in the future) - if (!s_event_queue.empty()) + if (!state.event_queue.empty()) { g.slice_length = static_cast( - std::min(s_event_queue.front().time - g.global_timer, MAX_SLICE_LENGTH)); + std::min(state.event_queue.front().time - g.global_timer, MAX_SLICE_LENGTH)); } - PowerPC::ppcState.downcount = CyclesToDowncount(g.slice_length); + PowerPC::ppcState.downcount = CyclesToDowncount(state, g.slice_length); // Check for any external exceptions. // It's important to do this after processing events otherwise any exceptions will be delayed @@ -369,7 +412,11 @@ void Advance() void LogPendingEvents() { - auto clone = s_event_queue; + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + + auto clone = state.event_queue; std::sort(clone.begin(), clone.end()); for (const Event& ev : clone) { @@ -381,7 +428,11 @@ void LogPendingEvents() // Should only be called from the CPU thread after the PPC clock has changed void AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock) { - for (Event& ev : s_event_queue) + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + + for (Event& ev : state.event_queue) { const s64 ticks = (ev.time - g.global_timer) * new_ppc_clock / old_ppc_clock; ev.time = g.global_timer + ticks; @@ -390,7 +441,11 @@ void AdjustEventQueueTimes(u32 new_ppc_clock, u32 old_ppc_clock) void Idle() { - if (s_config_sync_on_skip_idle) + auto& system = Core::System::GetInstance(); + auto& state = system.GetCoreTimingState().GetData(); + auto& g = system.GetCoreTimingGlobals(); + + if (state.config_sync_on_skip_idle) { // When the FIFO is processing data we must not advance because in this way // the VI will be desynchronized. So, We are waiting until the FIFO finish and @@ -399,16 +454,18 @@ void Idle() } PowerPC::UpdatePerformanceMonitor(PowerPC::ppcState.downcount, 0, 0); - s_idled_cycles += DowncountToCycles(PowerPC::ppcState.downcount); + state.idled_cycles += DowncountToCycles(g, PowerPC::ppcState.downcount); PowerPC::ppcState.downcount = 0; } std::string GetScheduledEventsSummary() { + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + std::string text = "Scheduled events\n"; text.reserve(1000); - auto clone = s_event_queue; + auto clone = state.event_queue; std::sort(clone.begin(), clone.end()); for (const Event& ev : clone) { @@ -419,41 +476,49 @@ std::string GetScheduledEventsSummary() u32 GetFakeDecStartValue() { - return s_fake_dec_start_value; + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + return state.fake_dec_start_value; } void SetFakeDecStartValue(u32 val) { - s_fake_dec_start_value = val; + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + state.fake_dec_start_value = val; } u64 GetFakeDecStartTicks() { - return s_fake_dec_start_ticks; + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + return state.fake_dec_start_ticks; } void SetFakeDecStartTicks(u64 val) { - s_fake_dec_start_ticks = val; + auto& state = Core::System::GetInstance().GetCoreTimingState().GetData(); + state.fake_dec_start_ticks = val; } u64 GetFakeTBStartValue() { + auto& g = Core::System::GetInstance().GetCoreTimingGlobals(); return g.fake_TB_start_value; } void SetFakeTBStartValue(u64 val) { + auto& g = Core::System::GetInstance().GetCoreTimingGlobals(); g.fake_TB_start_value = val; } u64 GetFakeTBStartTicks() { + auto& g = Core::System::GetInstance().GetCoreTimingGlobals(); return g.fake_TB_start_ticks; } void SetFakeTBStartTicks(u64 val) { + auto& g = Core::System::GetInstance().GetCoreTimingGlobals(); g.fake_TB_start_ticks = val; } diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 64a5e81a74..41360598f0 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -16,7 +16,9 @@ // inside callback: // ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever") +#include #include + #include "Common/CommonTypes.h" class PointerWrap; @@ -28,6 +30,23 @@ class System; namespace CoreTiming { +class CoreTimingState +{ +public: + CoreTimingState(); + CoreTimingState(const CoreTimingState&) = delete; + CoreTimingState(CoreTimingState&&) = delete; + CoreTimingState& operator=(const CoreTimingState&) = delete; + CoreTimingState& operator=(CoreTimingState&&) = delete; + ~CoreTimingState(); + + struct Data; + Data& GetData() { return *m_data; } + +private: + std::unique_ptr m_data; +}; + // These really shouldn't be global, but jit64 accesses them directly struct Globals { @@ -37,7 +56,6 @@ struct Globals u64 fake_TB_start_ticks; float last_OC_factor_inverted; }; -extern Globals g; // CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is // required to end slice -1 and start slice 0 before the first cycle of code is executed. diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp index 597a07c9c0..ec3d0229b9 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp @@ -212,13 +212,15 @@ int Interpreter::SingleStepInner() void Interpreter::SingleStep() { + auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals(); + // Declare start of new slice CoreTiming::Advance(); SingleStepInner(); // The interpreter ignores instruction timing information outside the 'fast runloop'. - CoreTiming::g.slice_length = 1; + core_timing_globals.slice_length = 1; PowerPC::ppcState.downcount = 0; if (PowerPC::ppcState.Exceptions != 0) diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp index f611fd7ee1..eb2e938648 100644 --- a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -14,6 +14,7 @@ #include "Core/PowerPC/Jit64/RegCache/JitRegCache.h" #include "Core/PowerPC/Jit64Common/Jit64PowerPCState.h" #include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" using namespace Gen; @@ -322,7 +323,8 @@ void Jit64::mfspr(UGeckoInstruction inst) RCX64Reg rax = gpr.Scratch(RAX); RCX64Reg rcx = gpr.Scratch(RCX); - MOV(64, rcx, ImmPtr(&CoreTiming::g)); + auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals(); + MOV(64, rcx, ImmPtr(&core_timing_globals)); // An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the // cost of calling out to C for this is actually significant. diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp index 6a8245b8bb..d93700e742 100644 --- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp @@ -13,6 +13,7 @@ #include "Core/PowerPC/Interpreter/ExceptionUtils.h" #include "Core/PowerPC/PPCTables.h" #include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" using namespace Arm64Gen; @@ -306,7 +307,8 @@ void JitArm64::mfspr(UGeckoInstruction inst) // An inline implementation of CoreTiming::GetFakeTimeBase, since in timer-heavy games the // cost of calling out to C for this is actually significant. - MOVP2R(Xg, &CoreTiming::g); + auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals(); + MOVP2R(Xg, &core_timing_globals); LDR(IndexType::Unsigned, WA, PPC_REG, PPCSTATE_OFF(downcount)); m_float_emit.SCVTF(SC, WA); diff --git a/Source/Core/Core/System.cpp b/Source/Core/Core/System.cpp index 62d119f071..aa7bc23b0e 100644 --- a/Source/Core/Core/System.cpp +++ b/Source/Core/Core/System.cpp @@ -7,6 +7,7 @@ #include "AudioCommon/SoundStream.h" #include "Core/Config/MainSettings.h" +#include "Core/CoreTiming.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/DSP.h" #include "Core/HW/DVD/DVDInterface.h" @@ -26,6 +27,8 @@ struct System::Impl bool m_audio_dump_started = false; AudioInterface::AudioInterfaceState m_audio_interface_state; + CoreTiming::CoreTimingState m_core_timing_state; + CoreTiming::Globals m_core_timing_globals; DSP::DSPState m_dsp_state; DVDInterface::DVDInterfaceState m_dvd_interface_state; DVDThread::DVDThreadState m_dvd_thread_state; @@ -84,6 +87,16 @@ AudioInterface::AudioInterfaceState& System::GetAudioInterfaceState() const return m_impl->m_audio_interface_state; } +CoreTiming::CoreTimingState& System::GetCoreTimingState() const +{ + return m_impl->m_core_timing_state; +} + +CoreTiming::Globals& System::GetCoreTimingGlobals() const +{ + return m_impl->m_core_timing_globals; +} + DSP::DSPState& System::GetDSPState() const { return m_impl->m_dsp_state; diff --git a/Source/Core/Core/System.h b/Source/Core/Core/System.h index 3e70f215e4..71e69cd845 100644 --- a/Source/Core/Core/System.h +++ b/Source/Core/Core/System.h @@ -12,6 +12,11 @@ namespace AudioInterface { class AudioInterfaceState; }; +namespace CoreTiming +{ +class CoreTimingState; +struct Globals; +} // namespace CoreTiming namespace DSP { class DSPState; @@ -76,6 +81,8 @@ public: void SetAudioDumpStarted(bool started); AudioInterface::AudioInterfaceState& GetAudioInterfaceState() const; + CoreTiming::CoreTimingState& GetCoreTimingState() const; + CoreTiming::Globals& GetCoreTimingGlobals() const; DSP::DSPState& GetDSPState() const; DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const; DVDThread::DVDThreadState& GetDVDThreadState() const; diff --git a/Source/UnitTests/Core/CoreTimingTest.cpp b/Source/UnitTests/Core/CoreTimingTest.cpp index b8ca8cc01b..83bb06097e 100644 --- a/Source/UnitTests/Core/CoreTimingTest.cpp +++ b/Source/UnitTests/Core/CoreTimingTest.cpp @@ -14,6 +14,7 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" #include "UICommon/UICommon.h" // Numbers are chosen randomly to make sure the correct one is given. @@ -279,9 +280,10 @@ TEST(CoreTiming, ScheduleIntoPast) // the stale value, i.e. effectively half-way through the previous slice. // NOTE: We're only testing that the scheduler doesn't break, not whether this makes sense. Core::UndeclareAsCPUThread(); - CoreTiming::g.global_timer -= 1000; + auto& core_timing_globals = Core::System::GetInstance().GetCoreTimingGlobals(); + core_timing_globals.global_timer -= 1000; CoreTiming::ScheduleEvent(0, cb_b, CB_IDS[1], CoreTiming::FromThread::NON_CPU); - CoreTiming::g.global_timer += 1000; + core_timing_globals.global_timer += 1000; Core::DeclareAsCPUThread(); AdvanceAndCheck(1, MAX_SLICE_LENGTH, MAX_SLICE_LENGTH + 1000);