summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorScott Mansell <phiren@gmail.com>2016-09-04 11:48:54 +1200
committerGitHub <noreply@github.com>2016-09-04 11:48:54 +1200
commitf5fa5a7d325bb0a7dd46b5f1723e52c370eb93b8 (patch)
tree83d3623ee2567c0277dffb37b07a841b44a169e4 /Source/Core
parent0f359000c54234a9a4728a432287b6a7e2389f0f (diff)
parent9d8f37301624681685c479e976d26f3d266c1129 (diff)
Merge pull request #4168 from EmptyChaos/coretiming-cleanup
Core: CoreTiming Cleanup (Add UnitTests)
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Common/ChunkFile.h85
-rw-r--r--Source/Core/Core/CoreTiming.cpp435
-rw-r--r--Source/Core/Core/CoreTiming.h34
-rw-r--r--Source/Core/Core/HW/AudioInterface.cpp2
-rw-r--r--Source/Core/Core/HW/DSP.cpp4
-rw-r--r--Source/Core/Core/HW/DVDInterface.cpp8
-rw-r--r--Source/Core/Core/HW/DVDThread.cpp2
-rw-r--r--Source/Core/Core/HW/EXI.cpp8
-rw-r--r--Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp58
-rw-r--r--Source/Core/Core/HW/EXI_DeviceMemoryCard.h7
-rw-r--r--Source/Core/Core/HW/ProcessorInterface.cpp4
-rw-r--r--Source/Core/Core/HW/SI.cpp4
-rw-r--r--Source/Core/Core/HW/SystemTimers.cpp15
-rw-r--r--Source/Core/Core/HW/WII_IPC.cpp10
-rw-r--r--Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp4
-rw-r--r--Source/Core/Core/MemoryWatcher.cpp2
-rw-r--r--Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp2
-rw-r--r--Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp10
-rw-r--r--Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp6
-rw-r--r--Source/Core/Core/PowerPC/PowerPC.cpp2
-rw-r--r--Source/Core/Core/State.cpp2
-rw-r--r--Source/Core/VideoBackends/Software/SWmain.cpp1
-rw-r--r--Source/Core/VideoCommon/CommandProcessor.cpp2
-rw-r--r--Source/Core/VideoCommon/Fifo.cpp2
-rw-r--r--Source/Core/VideoCommon/PixelEngine.cpp2
25 files changed, 308 insertions, 403 deletions
diff --git a/Source/Core/Common/ChunkFile.h b/Source/Core/Common/ChunkFile.h
index 4253bbf968..64e2a10b50 100644
--- a/Source/Core/Common/ChunkFile.h
+++ b/Source/Core/Common/ChunkFile.h
@@ -51,12 +51,6 @@
#error No version of is_trivially_copyable
#endif
-template <class T>
-struct LinkedListItem : public T
-{
- LinkedListItem<T>* next;
-};
-
// Wrapper class
class PointerWrap
{
@@ -244,67 +238,6 @@ public:
}
}
- // Let's pretend std::list doesn't exist!
- template <class T, LinkedListItem<T>* (*TNew)(), void (*TFree)(LinkedListItem<T>*),
- void (*TDo)(PointerWrap&, T*)>
- void DoLinkedList(LinkedListItem<T>*& list_start, LinkedListItem<T>** list_end = 0)
- {
- LinkedListItem<T>* list_cur = list_start;
- LinkedListItem<T>* prev = nullptr;
-
- while (true)
- {
- u8 shouldExist = !!list_cur;
- Do(shouldExist);
- if (shouldExist == 1)
- {
- LinkedListItem<T>* cur = list_cur ? list_cur : TNew();
- TDo(*this, (T*)cur);
- if (!list_cur)
- {
- if (mode == MODE_READ)
- {
- cur->next = nullptr;
- list_cur = cur;
- if (prev)
- prev->next = cur;
- else
- list_start = cur;
- }
- else
- {
- TFree(cur);
- continue;
- }
- }
- }
- else
- {
- if (mode == MODE_READ)
- {
- if (prev)
- prev->next = nullptr;
- if (list_end)
- *list_end = prev;
- if (list_cur)
- {
- if (list_start == list_cur)
- list_start = nullptr;
- do
- {
- LinkedListItem<T>* next = list_cur->next;
- TFree(list_cur);
- list_cur = next;
- } while (list_cur);
- }
- }
- break;
- }
- prev = list_cur;
- list_cur = list_cur->next;
- }
- }
-
void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42)
{
u32 cookie = arbitraryNumber;
@@ -319,16 +252,22 @@ public:
}
}
+ template <typename T, typename Functor>
+ void DoEachElement(T& container, Functor member)
+ {
+ u32 size = static_cast<u32>(container.size());
+ Do(size);
+ container.resize(size);
+
+ for (auto& elem : container)
+ member(*this, elem);
+ }
+
private:
template <typename T>
void DoContainer(T& x)
{
- u32 size = (u32)x.size();
- Do(size);
- x.resize(size);
-
- for (auto& elem : x)
- Do(elem);
+ DoEachElement(x, [](PointerWrap& p, typename T::value_type& elem) { p.Do(elem); });
}
__forceinline void DoVoid(void* data, u32 size)
diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp
index 0accb2e66c..e2ab497e86 100644
--- a/Source/Core/Core/CoreTiming.cpp
+++ b/Source/Core/Core/CoreTiming.cpp
@@ -2,13 +2,17 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include <algorithm>
#include <cinttypes>
#include <mutex>
#include <string>
+#include <unordered_map>
#include <vector>
+#include "Common/Assert.h"
#include "Common/ChunkFile.h"
#include "Common/FifoQueue.h"
+#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
@@ -20,68 +24,60 @@
#include "VideoCommon/Fifo.h"
#include "VideoCommon/VideoBackendBase.h"
-#define MAX_SLICE_LENGTH 20000
-
namespace CoreTiming
{
struct EventType
{
TimedCallback callback;
- std::string name;
+ const std::string* name;
};
-static std::vector<EventType> event_types;
-
-struct BaseEvent
+struct Event
{
s64 time;
u64 userdata;
- int type;
+ EventType* type;
};
-typedef LinkedListItem<BaseEvent> Event;
-
-// STATE_TO_SAVE
-static Event* first;
-static std::mutex tsWriteLock;
-static Common::FifoQueue<BaseEvent, false> tsQueue;
-
-// event pools
-static Event* eventPool = nullptr;
+constexpr bool operator>(const Event& left, const Event& right)
+{
+ return left.time > right.time;
+}
+constexpr bool operator<(const Event& left, const Event& right)
+{
+ return left.time < right.time;
+}
-static float s_lastOCFactor;
-float g_lastOCFactor_inverted;
-int g_slicelength;
-static int maxslicelength = MAX_SLICE_LENGTH;
+// 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<std::string, EventType> s_event_types;
-static s64 idledCycles;
-static u32 fakeDecStartValue;
-static u64 fakeDecStartTicks;
+// 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<Event> s_event_queue;
+static std::mutex s_ts_write_lock;
+static Common::FifoQueue<Event, false> s_ts_queue;
+
+static float s_last_OC_factor;
+float g_last_OC_factor_inverted;
+int g_slice_length;
+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;
// Are we in a function that has been called from Advance()
-static bool globalTimerIsSane;
-
-s64 g_globalTimer;
-u64 g_fakeTBStartValue;
-u64 g_fakeTBStartTicks;
-
-static int ev_lost;
-
-static Event* GetNewEvent()
-{
- if (!eventPool)
- return new Event;
+static bool s_is_global_timer_sane;
- Event* ev = eventPool;
- eventPool = ev->next;
- return ev;
-}
+s64 g_global_timer;
+u64 g_fake_TB_start_value;
+u64 g_fake_TB_start_ticks;
-static void FreeEvent(Event* ev)
-{
- ev->next = eventPool;
- eventPool = ev;
-}
+static EventType* s_ev_lost = nullptr;
static void EmptyTimedCallback(u64 userdata, s64 cyclesLate)
{
@@ -96,183 +92,143 @@ static void EmptyTimedCallback(u64 userdata, s64 cyclesLate)
// but the effect is largely the same.
static int DowncountToCycles(int downcount)
{
- return (int)(downcount * g_lastOCFactor_inverted);
+ return static_cast<int>(downcount * g_last_OC_factor_inverted);
}
static int CyclesToDowncount(int cycles)
{
- return (int)(cycles * s_lastOCFactor);
+ return static_cast<int>(cycles * s_last_OC_factor);
}
-int RegisterEvent(const std::string& name, TimedCallback callback)
+EventType* RegisterEvent(const std::string& name, TimedCallback callback)
{
- EventType type;
- type.name = name;
- type.callback = callback;
-
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
- for (auto& event_type : event_types)
- {
- if (name == event_type.name)
- {
- WARN_LOG(
- POWERPC,
- "Discarded old event type \"%s\" because a new type with the same name was registered.",
- name.c_str());
- // we don't know if someone might be holding on to the type index,
- // so we gut the old event type instead of actually removing it.
- event_type.name = "_discarded_event";
- event_type.callback = &EmptyTimedCallback;
- }
- }
+ _assert_msg_(POWERPC, s_event_types.find(name) == s_event_types.end(),
+ "CoreTiming Event \"%s\" is already registered. Events should only be registered "
+ "during Init to avoid breaking save states.",
+ name.c_str());
- event_types.push_back(type);
- return (int)event_types.size() - 1;
+ auto info = s_event_types.emplace(name, EventType{callback, nullptr});
+ EventType* event_type = &info.first->second;
+ event_type->name = &info.first->first;
+ return event_type;
}
void UnregisterAllEvents()
{
- if (first)
- PanicAlert("Cannot unregister events with events pending");
- event_types.clear();
+ _assert_msg_(POWERPC, s_event_queue.empty(), "Cannot unregister events with events pending");
+ s_event_types.clear();
}
void Init()
{
- s_lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
- g_lastOCFactor_inverted = 1.0f / s_lastOCFactor;
- PowerPC::ppcState.downcount = CyclesToDowncount(maxslicelength);
- g_slicelength = maxslicelength;
- g_globalTimer = 0;
- idledCycles = 0;
- globalTimerIsSane = true;
+ s_last_OC_factor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
+ g_last_OC_factor_inverted = 1.0f / s_last_OC_factor;
+ PowerPC::ppcState.downcount = CyclesToDowncount(MAX_SLICE_LENGTH);
+ g_slice_length = MAX_SLICE_LENGTH;
+ g_global_timer = 0;
+ s_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;
- ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
+ s_ev_lost = RegisterEvent("_lost_event", &EmptyTimedCallback);
}
void Shutdown()
{
- std::lock_guard<std::mutex> lk(tsWriteLock);
+ std::lock_guard<std::mutex> lk(s_ts_write_lock);
MoveEvents();
ClearPendingEvents();
UnregisterAllEvents();
-
- while (eventPool)
- {
- Event* ev = eventPool;
- eventPool = ev->next;
- delete ev;
- }
}
-static void EventDoState(PointerWrap& p, BaseEvent* ev)
+void DoState(PointerWrap& p)
{
- p.Do(ev->time);
+ std::lock_guard<std::mutex> lk(s_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(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.DoMarker("CoreTimingData");
- // this is why we can't have (nice things) pointers as userdata
- p.Do(ev->userdata);
+ MoveEvents();
+ p.DoEachElement(s_event_queue, [](PointerWrap& pw, Event& ev) {
+ pw.Do(ev.time);
- // we can't savestate ev->type directly because events might not get registered in the same order
- // (or at all) every time.
- // so, we savestate the event's type's name, and derive ev->type from that when loading.
- std::string name;
- if (p.GetMode() != PointerWrap::MODE_READ)
- name = event_types[ev->type].name;
+ // this is why we can't have (nice things) pointers as userdata
+ pw.Do(ev.userdata);
- p.Do(name);
- if (p.GetMode() == PointerWrap::MODE_READ)
- {
- bool foundMatch = false;
- for (unsigned int i = 0; i < event_types.size(); ++i)
+ // we can't savestate ev.type directly because events might not get registered in the same
+ // order (or at all) every time.
+ // so, we savestate the event's type's name, and derive ev.type from that when loading.
+ std::string name;
+ if (pw.GetMode() != PointerWrap::MODE_READ)
+ name = *ev.type->name;
+
+ pw.Do(name);
+ if (pw.GetMode() == PointerWrap::MODE_READ)
{
- if (name == event_types[i].name)
+ auto itr = s_event_types.find(name);
+ if (itr != s_event_types.end())
{
- ev->type = i;
- foundMatch = true;
- break;
+ ev.type = &itr->second;
+ }
+ else
+ {
+ WARN_LOG(POWERPC,
+ "Lost event from savestate because its type, \"%s\", has not been registered.",
+ name.c_str());
+ ev.type = s_ev_lost;
}
}
- if (!foundMatch)
- {
- WARN_LOG(POWERPC,
- "Lost event from savestate because its type, \"%s\", has not been registered.",
- name.c_str());
- ev->type = ev_lost;
- }
- }
-}
+ });
+ p.DoMarker("CoreTimingEvents");
-void DoState(PointerWrap& p)
-{
- std::lock_guard<std::mutex> lk(tsWriteLock);
- p.Do(g_slicelength);
- p.Do(g_globalTimer);
- p.Do(idledCycles);
- p.Do(fakeDecStartValue);
- p.Do(fakeDecStartTicks);
- p.Do(g_fakeTBStartValue);
- p.Do(g_fakeTBStartTicks);
- p.Do(s_lastOCFactor);
+ // When loading from a save state, we must assume the Event order is random and meaningless.
+ // The exact layout of the heap in memory is implementation defined, therefore it is platform
+ // and library version specific.
if (p.GetMode() == PointerWrap::MODE_READ)
- g_lastOCFactor_inverted = 1.0f / s_lastOCFactor;
-
- p.DoMarker("CoreTimingData");
-
- MoveEvents();
-
- p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, EventDoState>(first);
- p.DoMarker("CoreTimingEvents");
+ std::make_heap(s_event_queue.begin(), s_event_queue.end(), std::greater<Event>());
}
// 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()
{
- u64 ticks = (u64)g_globalTimer;
- if (!globalTimerIsSane)
+ u64 ticks = static_cast<u64>(g_global_timer);
+ if (!s_is_global_timer_sane)
{
int downcount = DowncountToCycles(PowerPC::ppcState.downcount);
- ticks += g_slicelength - downcount;
+ ticks += g_slice_length - downcount;
}
return ticks;
}
u64 GetIdleTicks()
{
- return (u64)idledCycles;
+ return static_cast<u64>(s_idled_cycles);
}
void ClearPendingEvents()
{
- while (first)
- {
- Event* e = first->next;
- FreeEvent(first);
- first = e;
- }
+ s_event_queue.clear();
}
-static void AddEventToQueue(Event* ne)
+void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata, FromThread from)
{
- Event* prev = nullptr;
- Event** pNext = &first;
- for (;;)
- {
- Event*& next = *pNext;
- if (!next || ne->time < next->time)
- {
- ne->next = next;
- next = ne;
- break;
- }
- prev = next;
- pNext = &prev->next;
- }
-}
+ _assert_msg_(POWERPC, event_type, "Event type is nullptr, will crash now.");
-void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata, FromThread from)
-{
bool from_cpu_thread;
if (from == FromThread::ANY)
{
@@ -287,16 +243,14 @@ void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata, FromThr
if (from_cpu_thread)
{
- Event* ne = GetNewEvent();
- ne->time = GetTicks() + cycles_into_future;
- ne->userdata = userdata;
- ne->type = event_type;
+ s64 timeout = GetTicks() + cycles_into_future;
// If this event needs to be scheduled before the next advance(), force one early
- if (!globalTimerIsSane)
+ if (!s_is_global_timer_sane)
ForceExceptionCheck(cycles_into_future);
- AddEventToQueue(ne);
+ s_event_queue.emplace_back(Event{timeout, userdata, event_type});
+ std::push_heap(s_event_queue.begin(), s_event_queue.end(), std::greater<Event>());
}
else
{
@@ -304,49 +258,28 @@ void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata, FromThr
{
ERROR_LOG(POWERPC, "Someone scheduled an off-thread \"%s\" event while netplay or "
"movie play/record was active. This is likely to cause a desync.",
- event_types[event_type].name.c_str());
+ event_type->name->c_str());
}
- std::lock_guard<std::mutex> lk(tsWriteLock);
- Event ne;
- ne.time = g_globalTimer + cycles_into_future;
- ne.type = event_type;
- ne.userdata = userdata;
- tsQueue.Push(ne);
+ std::lock_guard<std::mutex> lk(s_ts_write_lock);
+ s_ts_queue.Push(Event{g_global_timer + cycles_into_future, userdata, event_type});
}
}
-void RemoveEvent(int event_type)
+void RemoveEvent(EventType* event_type)
{
- while (first && first->type == event_type)
- {
- Event* next = first->next;
- FreeEvent(first);
- first = next;
- }
+ auto itr = std::remove_if(s_event_queue.begin(), s_event_queue.end(),
+ [&](const Event& e) { return e.type == event_type; });
- if (!first)
- return;
-
- Event* prev = first;
- Event* ptr = prev->next;
- while (ptr)
+ // Removing random items breaks the invariant so we have to re-establish it.
+ if (itr != s_event_queue.end())
{
- if (ptr->type == event_type)
- {
- prev->next = ptr->next;
- FreeEvent(ptr);
- ptr = prev->next;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
- }
+ s_event_queue.erase(itr, s_event_queue.end());
+ std::make_heap(s_event_queue.begin(), s_event_queue.end(), std::greater<Event>());
}
}
-void RemoveAllEvents(int event_type)
+void RemoveAllEvents(EventType* event_type)
{
MoveEvents();
RemoveEvent(event_type);
@@ -354,26 +287,22 @@ void RemoveAllEvents(int event_type)
void ForceExceptionCheck(s64 cycles)
{
- if (s64(DowncountToCycles(PowerPC::ppcState.downcount)) > cycles)
+ cycles = std::max<s64>(0, cycles);
+ if (DowncountToCycles(PowerPC::ppcState.downcount) > cycles)
{
// downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int here.
- g_slicelength -=
- (DowncountToCycles(PowerPC::ppcState.downcount) -
- (int)cycles); // Account for cycles already executed by adjusting the g_slicelength
- PowerPC::ppcState.downcount = CyclesToDowncount((int)cycles);
+ // Account for cycles already executed by adjusting the g_slice_length
+ g_slice_length -= DowncountToCycles(PowerPC::ppcState.downcount) - static_cast<int>(cycles);
+ PowerPC::ppcState.downcount = CyclesToDowncount(static_cast<int>(cycles));
}
}
void MoveEvents()
{
- BaseEvent sevt;
- while (tsQueue.Pop(sevt))
+ for (Event ev; s_ts_queue.Pop(ev);)
{
- Event* evt = GetNewEvent();
- evt->time = sevt.time;
- evt->userdata = sevt.userdata;
- evt->type = sevt.type;
- AddEventToQueue(evt);
+ s_event_queue.emplace_back(std::move(ev));
+ std::push_heap(s_event_queue.begin(), s_event_queue.end(), std::greater<Event>());
}
}
@@ -381,35 +310,34 @@ void Advance()
{
MoveEvents();
- int cyclesExecuted = g_slicelength - DowncountToCycles(PowerPC::ppcState.downcount);
- g_globalTimer += cyclesExecuted;
- s_lastOCFactor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
- g_lastOCFactor_inverted = 1.0f / s_lastOCFactor;
- g_slicelength = maxslicelength;
+ int cyclesExecuted = g_slice_length - DowncountToCycles(PowerPC::ppcState.downcount);
+ g_global_timer += cyclesExecuted;
+ s_last_OC_factor = SConfig::GetInstance().m_OCEnable ? SConfig::GetInstance().m_OCFactor : 1.0f;
+ g_last_OC_factor_inverted = 1.0f / s_last_OC_factor;
+ g_slice_length = MAX_SLICE_LENGTH;
- globalTimerIsSane = true;
+ s_is_global_timer_sane = true;
- while (first && first->time <= g_globalTimer)
+ while (!s_event_queue.empty() && s_event_queue.front().time <= g_global_timer)
{
- // LOG(POWERPC, "[Scheduler] %s (%lld, %lld) ",
- // event_types[first->type].name ? event_types[first->type].name : "?",
- // (u64)g_globalTimer, (u64)first->time);
- Event* evt = first;
- first = first->next;
- event_types[evt->type].callback(evt->userdata, (int)(g_globalTimer - evt->time));
- FreeEvent(evt);
+ Event evt = std::move(s_event_queue.front());
+ std::pop_heap(s_event_queue.begin(), s_event_queue.end(), std::greater<Event>());
+ s_event_queue.pop_back();
+ // NOTICE_LOG(POWERPC, "[Scheduler] %-20s (%lld, %lld)", evt.type->name->c_str(),
+ // g_global_timer, evt.time);
+ evt.type->callback(evt.userdata, g_global_timer - evt.time);
}
- globalTimerIsSane = false;
+ s_is_global_timer_sane = false;
- if (first)
+ // Still events left (scheduled in the future)
+ if (!s_event_queue.empty())
{
- g_slicelength = (int)(first->time - g_globalTimer);
- if (g_slicelength > maxslicelength)
- g_slicelength = maxslicelength;
+ g_slice_length = static_cast<int>(
+ std::min<s64>(s_event_queue.front().time - g_global_timer, MAX_SLICE_LENGTH));
}
- PowerPC::ppcState.downcount = CyclesToDowncount(g_slicelength);
+ PowerPC::ppcState.downcount = CyclesToDowncount(g_slice_length);
// Check for any external exceptions.
// It's important to do this after processing events otherwise any exceptions will be delayed
@@ -420,12 +348,12 @@ void Advance()
void LogPendingEvents()
{
- Event* ptr = first;
- while (ptr)
+ auto clone = s_event_queue;
+ std::sort(clone.begin(), clone.end());
+ for (const Event& ev : clone)
{
- INFO_LOG(POWERPC, "PENDING: Now: %" PRId64 " Pending: %" PRId64 " Type: %d", g_globalTimer,
- ptr->time, ptr->type);
- ptr = ptr->next;
+ INFO_LOG(POWERPC, "PENDING: Now: %" PRId64 " Pending: %" PRId64 " Type: %s", g_global_timer,
+ ev.time, ev.type->name->c_str());
}
}
@@ -439,68 +367,63 @@ void Idle()
Fifo::FlushGpu();
}
- idledCycles += DowncountToCycles(PowerPC::ppcState.downcount);
+ s_idled_cycles += DowncountToCycles(PowerPC::ppcState.downcount);
PowerPC::ppcState.downcount = 0;
}
std::string GetScheduledEventsSummary()
{
- Event* ptr = first;
std::string text = "Scheduled events\n";
text.reserve(1000);
- while (ptr)
- {
- unsigned int t = ptr->type;
- if (t >= event_types.size())
- PanicAlertT("Invalid event type %i", t);
- const std::string& name = event_types[ptr->type].name;
-
- text += StringFromFormat("%s : %" PRIi64 " %016" PRIx64 "\n", name.c_str(), ptr->time,
- ptr->userdata);
- ptr = ptr->next;
+ auto clone = s_event_queue;
+ std::sort(clone.begin(), clone.end());
+ for (const Event& ev : clone)
+ {
+ text += StringFromFormat("%s : %" PRIi64 " %016" PRIx64 "\n", ev.type->name->c_str(), ev.time,
+ ev.userdata);
}
return text;
}
u32 GetFakeDecStartValue()
{
- return fakeDecStartValue;
+ return s_fake_dec_start_value;
}
void SetFakeDecStartValue(u32 val)
{
- fakeDecStartValue = val;
+ s_fake_dec_start_value = val;
}
u64 GetFakeDecStartTicks()
{
- return fakeDecStartTicks;
+ return s_fake_dec_start_ticks;
}
void SetFakeDecStartTicks(u64 val)
{
- fakeDecStartTicks = val;
+ s_fake_dec_start_ticks = val;
}
u64 GetFakeTBStartValue()
{
- return g_fakeTBStartValue;
+ return g_fake_TB_start_value;
}
void SetFakeTBStartValue(u64 val)
{
- g_fakeTBStartValue = val;
+ g_fake_TB_start_value = val;
}
u64 GetFakeTBStartTicks()
{
- return g_fakeTBStartTicks;
+ return g_fake_TB_start_ticks;
}
void SetFakeTBStartTicks(u64 val)
{
- g_fakeTBStartTicks = val;
+ g_fake_TB_start_ticks = val;
}
} // namespace
diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h
index 210aa7c5e6..28263be94c 100644
--- a/Source/Core/Core/CoreTiming.h
+++ b/Source/Core/Core/CoreTiming.h
@@ -25,12 +25,14 @@ class PointerWrap;
namespace CoreTiming
{
// These really shouldn't be global, but jit64 accesses them directly
-extern s64 g_globalTimer;
-extern u64 g_fakeTBStartValue;
-extern u64 g_fakeTBStartTicks;
-extern int g_slicelength;
-extern float g_lastOCFactor_inverted;
-
+extern s64 g_global_timer;
+extern u64 g_fake_TB_start_value;
+extern u64 g_fake_TB_start_ticks;
+extern int g_slice_length;
+extern float g_last_OC_factor_inverted;
+
+// 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.
void Init();
void Shutdown();
@@ -43,9 +45,11 @@ u64 GetIdleTicks();
void DoState(PointerWrap& p);
+struct EventType;
+
// Returns the event_type identifier. if name is not unique, an existing event_type will be
// discarded.
-int RegisterEvent(const std::string& name, TimedCallback callback);
+EventType* RegisterEvent(const std::string& name, TimedCallback callback);
void UnregisterAllEvents();
enum class FromThread
@@ -58,12 +62,22 @@ enum class FromThread
};
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from savestates.
-void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata = 0,
+// After the first Advance, the slice lengths and the downcount will be reduced whenever an event
+// is scheduled earlier than the current values (when scheduled from the CPU Thread only).
+// Scheduling from a callback will not update the downcount until the Advance() completes.
+void ScheduleEvent(s64 cycles_into_future, EventType* event_type, u64 userdata = 0,
FromThread from = FromThread::CPU);
// We only permit one event of each type in the queue at a time.
-void RemoveEvent(int event_type);
-void RemoveAllEvents(int event_type);
+void RemoveEvent(EventType* event_type);
+void RemoveAllEvents(EventType* event_type);
+
+// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
+// the previous timing slice and begins the next one, you must Advance from the previous
+// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
+// Advance() is required to initialize the slice length before the first cycle of emulated
+// instructions is executed.
+// NOTE: Advance updates the PowerPC downcount and performs a PPC external exception check.
void Advance();
void MoveEvents();
diff --git a/Source/Core/Core/HW/AudioInterface.cpp b/Source/Core/Core/HW/AudioInterface.cpp
index de062df4aa..92e0e1610b 100644
--- a/Source/Core/Core/HW/AudioInterface.cpp
+++ b/Source/Core/Core/HW/AudioInterface.cpp
@@ -130,7 +130,7 @@ static void GenerateAudioInterrupt();
static void UpdateInterrupts();
static void IncreaseSampleCount(const u32 _uAmount);
static int GetAIPeriod();
-static int et_AI;
+static CoreTiming::EventType* et_AI;
static void Update(u64 userdata, s64 cyclesLate);
void Init()
diff --git a/Source/Core/Core/HW/DSP.cpp b/Source/Core/Core/HW/DSP.cpp
index 9ad397901c..9fab707330 100644
--- a/Source/Core/Core/HW/DSP.cpp
+++ b/Source/Core/Core/HW/DSP.cpp
@@ -188,8 +188,8 @@ static void UpdateInterrupts();
static void Do_ARAM_DMA();
static void GenerateDSPInterrupt(u64 DSPIntType, s64 cyclesLate = 0);
-static int et_GenerateDSPInterrupt;
-static int et_CompleteARAM;
+static CoreTiming::EventType* et_GenerateDSPInterrupt;
+static CoreTiming::EventType* et_CompleteARAM;
static void CompleteARAM(u64 userdata, s64 cyclesLate)
{
diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp
index 1fdef1a313..ad3236d1b0 100644
--- a/Source/Core/Core/HW/DVDInterface.cpp
+++ b/Source/Core/Core/HW/DVDInterface.cpp
@@ -238,14 +238,14 @@ static u32 s_error_code = 0;
static bool s_disc_inside = false;
static bool s_stream = false;
static bool s_stop_at_track_end = false;
-static int s_finish_executing_command = 0;
-static int s_dtk = 0;
+static CoreTiming::EventType* s_finish_executing_command;
+static CoreTiming::EventType* s_dtk;
static u64 s_last_read_offset;
static u64 s_last_read_time;
-static int s_eject_disc;
-static int s_insert_disc;
+static CoreTiming::EventType* s_eject_disc;
+static CoreTiming::EventType* s_insert_disc;
static void EjectDiscCallback(u64 userdata, s64 cyclesLate);
static void InsertDiscCallback(u64 userdata, s64 cyclesLate);
diff --git a/Source/Core/Core/HW/DVDThread.cpp b/Source/Core/Core/HW/DVDThread.cpp
index 3601f5bafd..e79de14019 100644
--- a/Source/Core/Core/HW/DVDThread.cpp
+++ b/Source/Core/Core/HW/DVDThread.cpp
@@ -30,7 +30,7 @@ namespace DVDThread
static void DVDThread();
static void FinishRead(u64 userdata, s64 cycles_late);
-static int s_finish_read;
+static CoreTiming::EventType* s_finish_read;
static std::thread s_dvd_thread;
static Common::Event s_dvd_thread_start_working;
diff --git a/Source/Core/Core/HW/EXI.cpp b/Source/Core/Core/HW/EXI.cpp
index 9db0a83382..1b18c566c3 100644
--- a/Source/Core/Core/HW/EXI.cpp
+++ b/Source/Core/Core/HW/EXI.cpp
@@ -12,6 +12,7 @@
#include "Core/CoreTiming.h"
#include "Core/HW/EXI.h"
#include "Core/HW/EXI_Channel.h"
+#include "Core/HW/EXI_DeviceMemoryCard.h"
#include "Core/HW/MMIO.h"
#include "Core/HW/ProcessorInterface.h"
#include "Core/HW/Sram.h"
@@ -23,8 +24,8 @@ bool g_SRAM_netplay_initialized = false;
namespace ExpansionInterface
{
-static int changeDevice;
-static int updateInterrupts;
+static CoreTiming::EventType* changeDevice;
+static CoreTiming::EventType* updateInterrupts;
static std::array<std::unique_ptr<CEXIChannel>, MAX_EXI_CHANNELS> g_Channels;
@@ -38,6 +39,7 @@ void Init()
InitSRAM();
}
+ CEXIMemoryCard::Init();
for (u32 i = 0; i < MAX_EXI_CHANNELS; i++)
g_Channels[i] = std::make_unique<CEXIChannel>(i);
@@ -65,6 +67,8 @@ void Shutdown()
{
for (auto& channel : g_Channels)
channel.reset();
+
+ CEXIMemoryCard::Shutdown();
}
void DoState(PointerWrap& p)
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
index 3ca0ddcfcf..fdef3181fb 100644
--- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
+#include <array>
#include <cstring>
#include <memory>
#include <string>
@@ -41,6 +42,9 @@
static const u32 MC_TRANSFER_RATE_READ = 512 * 1024;
static const u32 MC_TRANSFER_RATE_WRITE = (u32)(96.125f * 1024.0f);
+static std::array<CoreTiming::EventType*, 2> s_et_cmd_done;
+static std::array<CoreTiming::EventType*, 2> s_et_transfer_complete;
+
// Takes care of the nasty recovery of the 'this' pointer from card_index,
// stored in the userdata parameter of the CoreTiming event.
void CEXIMemoryCard::EventCompleteFindInstance(u64 userdata,
@@ -70,25 +74,37 @@ void CEXIMemoryCard::TransferCompleteCallback(u64 userdata, s64 cyclesLate)
[](CEXIMemoryCard* instance) { instance->TransferComplete(); });
}
-CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(index)
+void CEXIMemoryCard::Init()
{
- struct
- {
- const char* done;
- const char* transfer_complete;
- } const event_names[] = {
- {"memcardDoneA", "memcardTransferCompleteA"}, {"memcardDoneB", "memcardTransferCompleteB"},
- };
+ static constexpr char DONE_PREFIX[] = "memcardDone";
+ static constexpr char TRANSFER_COMPLETE_PREFIX[] = "memcardTransferComplete";
- if ((size_t)index >= ArraySize(event_names))
+ static_assert(s_et_cmd_done.size() == s_et_transfer_complete.size(), "Event array size differs");
+ for (unsigned int i = 0; i < s_et_cmd_done.size(); ++i)
{
- PanicAlertT("Trying to create invalid memory card index.");
+ std::string name = DONE_PREFIX;
+ name += static_cast<char>('A' + i);
+ s_et_cmd_done[i] = CoreTiming::RegisterEvent(name, CmdDoneCallback);
+
+ name = TRANSFER_COMPLETE_PREFIX;
+ name += static_cast<char>('A' + i);
+ s_et_transfer_complete[i] = CoreTiming::RegisterEvent(name, TransferCompleteCallback);
}
- // we're potentially leaking events here, since there's no RemoveEvent
- // until emu shutdown, but I guess it's inconsequential
- et_cmd_done = CoreTiming::RegisterEvent(event_names[index].done, CmdDoneCallback);
- et_transfer_complete =
- CoreTiming::RegisterEvent(event_names[index].transfer_complete, TransferCompleteCallback);
+}
+
+void CEXIMemoryCard::Shutdown()
+{
+ s_et_cmd_done.fill(nullptr);
+ s_et_transfer_complete.fill(nullptr);
+}
+
+CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(index)
+{
+ _assert_msg_(EXPANSIONINTERFACE, static_cast<std::size_t>(index) < s_et_cmd_done.size(),
+ "Trying to create invalid memory card index %d.", index);
+
+ // NOTE: When loading a save state, DMA completion callbacks (s_et_transfer_complete) and such
+ // may have been restored, we need to anticipate those arriving.
interruptSwitch = 0;
m_bInterruptSet = 0;
@@ -248,8 +264,8 @@ void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
CEXIMemoryCard::~CEXIMemoryCard()
{
- CoreTiming::RemoveEvent(et_cmd_done);
- CoreTiming::RemoveEvent(et_transfer_complete);
+ CoreTiming::RemoveEvent(s_et_cmd_done[card_index]);
+ CoreTiming::RemoveEvent(s_et_transfer_complete[card_index]);
}
bool CEXIMemoryCard::UseDelayedTransferCompletion() const
@@ -279,8 +295,8 @@ void CEXIMemoryCard::TransferComplete()
void CEXIMemoryCard::CmdDoneLater(u64 cycles)
{
- CoreTiming::RemoveEvent(et_cmd_done);
- CoreTiming::ScheduleEvent((int)cycles, et_cmd_done, (u64)card_index);
+ CoreTiming::RemoveEvent(s_et_cmd_done[card_index]);
+ CoreTiming::ScheduleEvent((int)cycles, s_et_cmd_done[card_index], (u64)card_index);
}
void CEXIMemoryCard::SetCS(int cs)
@@ -547,7 +563,7 @@ void CEXIMemoryCard::DMARead(u32 _uAddr, u32 _uSize)
// Schedule transfer complete later based on read speed
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_READ),
- et_transfer_complete, (u64)card_index);
+ s_et_transfer_complete[card_index], (u64)card_index);
}
// DMA write are preceded by all of the necessary setup via IMMWrite
@@ -563,5 +579,5 @@ void CEXIMemoryCard::DMAWrite(u32 _uAddr, u32 _uSize)
// Schedule transfer complete later based on write speed
CoreTiming::ScheduleEvent(_uSize * (SystemTimers::GetTicksPerSecond() / MC_TRANSFER_RATE_WRITE),
- et_transfer_complete, (u64)card_index);
+ s_et_transfer_complete[card_index], (u64)card_index);
}
diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
index 53763e7e5f..95c0dd1e1f 100644
--- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
+++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h
@@ -26,6 +26,12 @@ public:
void DMARead(u32 _uAddr, u32 _uSize) override;
void DMAWrite(u32 _uAddr, u32 _uSize) override;
+ // CoreTiming events need to be registered during boot since CoreTiming is DoState()-ed
+ // before ExpansionInterface so we'll lose the save stated events if the callbacks are
+ // not already registered first.
+ static void Init();
+ static void Shutdown();
+
private:
void SetupGciFolder(u16 sizeMb);
void SetupRawMemcard(u16 sizeMb);
@@ -67,7 +73,6 @@ private:
};
int card_index;
- int et_cmd_done, et_transfer_complete;
//! memory card state
// STATE_TO_SAVE
diff --git a/Source/Core/Core/HW/ProcessorInterface.cpp b/Source/Core/Core/HW/ProcessorInterface.cpp
index 59ce6a4569..04fec4cd07 100644
--- a/Source/Core/Core/HW/ProcessorInterface.cpp
+++ b/Source/Core/Core/HW/ProcessorInterface.cpp
@@ -31,10 +31,10 @@ static u32 m_FlipperRev;
static u32 m_Unknown;
// ID and callback for scheduling reset button presses/releases
-static int toggleResetButton;
+static CoreTiming::EventType* toggleResetButton;
static void ToggleResetButtonCallback(u64 userdata, s64 cyclesLate);
-static int iosNotifyResetButton;
+static CoreTiming::EventType* iosNotifyResetButton;
static void IOSNotifyResetButtonCallback(u64 userdata, s64 cyclesLate);
// Let the PPC know that an external exception is set/cleared
diff --git a/Source/Core/Core/HW/SI.cpp b/Source/Core/Core/HW/SI.cpp
index 55c9c06904..917f70d7e4 100644
--- a/Source/Core/Core/HW/SI.cpp
+++ b/Source/Core/Core/HW/SI.cpp
@@ -23,8 +23,8 @@
namespace SerialInterface
{
-static int changeDevice;
-static int et_transfer_pending;
+static CoreTiming::EventType* changeDevice;
+static CoreTiming::EventType* et_transfer_pending;
static void RunSIBuffer(u64 userdata, s64 cyclesLate);
static void UpdateInterrupts();
diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp
index 6736d191b7..3e8b96381f 100644
--- a/Source/Core/Core/HW/SystemTimers.cpp
+++ b/Source/Core/Core/HW/SystemTimers.cpp
@@ -64,13 +64,14 @@ IPC_HLE_PERIOD: For the Wiimote this is the call schedule:
namespace SystemTimers
{
-static int et_Dec;
-static int et_VI;
-static int et_AudioDMA;
-static int et_DSP;
-static int et_IPC_HLE;
-static int et_PatchEngine; // PatchEngine updates every 1/60th of a second by default
-static int et_Throttle;
+static CoreTiming::EventType* et_Dec;
+static CoreTiming::EventType* et_VI;
+static CoreTiming::EventType* et_AudioDMA;
+static CoreTiming::EventType* et_DSP;
+static CoreTiming::EventType* et_IPC_HLE;
+// PatchEngine updates every 1/60th of a second by default
+static CoreTiming::EventType* et_PatchEngine;
+static CoreTiming::EventType* et_Throttle;
static u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!)
diff --git a/Source/Core/Core/HW/WII_IPC.cpp b/Source/Core/Core/HW/WII_IPC.cpp
index 646abb778d..67da12be96 100644
--- a/Source/Core/Core/HW/WII_IPC.cpp
+++ b/Source/Core/Core/HW/WII_IPC.cpp
@@ -98,7 +98,7 @@ static u32 arm_irq_masks;
static u32 sensorbar_power; // do we need to care about this?
-static int updateInterrupts;
+static CoreTiming::EventType* updateInterrupts;
static void UpdateInterrupts(u64 = 0, s64 cyclesLate = 0);
void DoState(PointerWrap& p)
@@ -113,7 +113,7 @@ void DoState(PointerWrap& p)
p.Do(sensorbar_power);
}
-void Init()
+static void InitState()
{
ctrl = CtrlRegister();
ppc_msg = 0;
@@ -127,14 +127,18 @@ void Init()
sensorbar_power = 0;
ppc_irq_masks |= INT_CAUSE_IPC_BROADWAY;
+}
+void Init()
+{
+ InitState();
updateInterrupts = CoreTiming::RegisterEvent("IPCInterrupt", UpdateInterrupts);
}
void Reset()
{
INFO_LOG(WII_IPC, "Resetting ...");
- Init();
+ InitState();
WII_IPC_HLE_Interface::Reset();
}
diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
index 1720790b75..0934c79f59 100644
--- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
+++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp
@@ -76,8 +76,8 @@ static ipc_msg_queue request_queue; // ppc -> arm
static ipc_msg_queue reply_queue; // arm -> ppc
static ipc_msg_queue ack_queue; // arm -> ppc
-static int event_enqueue;
-static int event_sdio_notify;
+static CoreTiming::EventType* event_enqueue;
+static CoreTiming::EventType* event_sdio_notify;
static u64 last_reply_time;
diff --git a/Source/Core/Core/MemoryWatcher.cpp b/Source/Core/Core/MemoryWatcher.cpp
index a0fccb973b..3aea80a6b6 100644
--- a/Source/Core/Core/MemoryWatcher.cpp
+++ b/Source/Core/Core/MemoryWatcher.cpp
@@ -15,7 +15,7 @@
#include "Core/MemoryWatcher.h"
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
-static int s_event;
+static CoreTiming::EventType* s_event;
static const int MW_RATE = 600; // Steps per second
static void MWCallback(u64 userdata, s64 cyclesLate)
diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp
index 24337e37d7..336371914e 100644
--- a/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp
+++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter.cpp
@@ -198,7 +198,7 @@ void Interpreter::SingleStep()
{
SingleStepInner();
- CoreTiming::g_slicelength = 1;
+ CoreTiming::g_slice_length = 1;
PowerPC::ppcState.downcount = 0;
CoreTiming::Advance();
diff --git a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp
index f952fae751..27a8e4afec 100644
--- a/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp
+++ b/Source/Core/Core/PowerPC/Jit64/Jit_SystemRegisters.cpp
@@ -286,13 +286,13 @@ void Jit64::mfspr(UGeckoInstruction inst)
// cost of calling out to C for this is actually significant.
// Scale downcount by the CPU overclocking factor.
CVTSI2SS(XMM0, PPCSTATE(downcount));
- MULSS(XMM0, M(&CoreTiming::g_lastOCFactor_inverted));
+ MULSS(XMM0, M(&CoreTiming::g_last_OC_factor_inverted));
CVTSS2SI(RDX, R(XMM0)); // RDX is downcount scaled by the overclocking factor
- MOV(32, R(RAX), M(&CoreTiming::g_slicelength));
+ MOV(32, R(RAX), M(&CoreTiming::g_slice_length));
SUB(64, R(RAX), R(RDX)); // cycles since the last CoreTiming::Advance() event is (slicelength -
// Scaled_downcount)
- ADD(64, R(RAX), M(&CoreTiming::g_globalTimer));
- SUB(64, R(RAX), M(&CoreTiming::g_fakeTBStartTicks));
+ ADD(64, R(RAX), M(&CoreTiming::g_global_timer));
+ SUB(64, R(RAX), M(&CoreTiming::g_fake_TB_start_ticks));
// It might seem convenient to correct the timer for the block position here for even more
// accurate
// timing, but as of currently, this can break games. If we end up reading a time *after* the
@@ -308,7 +308,7 @@ void Jit64::mfspr(UGeckoInstruction inst)
// a / 12 = (a * 0xAAAAAAAAAAAAAAAB) >> 67
MOV(64, R(RDX), Imm64(0xAAAAAAAAAAAAAAABULL));
MUL(64, R(RDX));
- MOV(64, R(RAX), M(&CoreTiming::g_fakeTBStartValue));
+ MOV(64, R(RAX), M(&CoreTiming::g_fake_TB_start_value));
SHR(64, R(RDX), Imm8(3));
ADD(64, R(RAX), R(RDX));
MOV(64, PPCSTATE(spr[SPR_TL]), R(RAX));
diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
index 22ecb11405..f5c457a00b 100644
--- a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
+++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp
@@ -234,9 +234,9 @@ 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.
- MOVI2R(XA, (u64)&CoreTiming::g_globalTimer);
+ MOVI2R(XA, (u64)&CoreTiming::g_global_timer);
LDR(INDEX_UNSIGNED, XA, XA, 0);
- MOVI2R(XB, (u64)&CoreTiming::g_fakeTBStartTicks);
+ MOVI2R(XB, (u64)&CoreTiming::g_fake_TB_start_ticks);
LDR(INDEX_UNSIGNED, XB, XB, 0);
SUB(XA, XA, XB);
@@ -254,7 +254,7 @@ void JitArm64::mfspr(UGeckoInstruction inst)
ADD(XB, XB, 1);
UMULH(XA, XA, XB);
- MOVI2R(XB, (u64)&CoreTiming::g_fakeTBStartValue);
+ MOVI2R(XB, (u64)&CoreTiming::g_fake_TB_start_value);
LDR(INDEX_UNSIGNED, XB, XB, 0);
ADD(XA, XB, XA, ArithOption(XA, ST_LSR, 3));
STR(INDEX_UNSIGNED, XA, PPC_REG, PPCSTATE_OFF(spr[SPR_TL]));
diff --git a/Source/Core/Core/PowerPC/PowerPC.cpp b/Source/Core/Core/PowerPC/PowerPC.cpp
index 68b21ab0e0..e7d9e8617d 100644
--- a/Source/Core/Core/PowerPC/PowerPC.cpp
+++ b/Source/Core/Core/PowerPC/PowerPC.cpp
@@ -36,7 +36,7 @@ BreakPoints breakpoints;
MemChecks memchecks;
PPCDebugInterface debug_interface;
-static int s_invalidate_cache_thread_safe;
+static CoreTiming::EventType* s_invalidate_cache_thread_safe;
static void InvalidateCacheThreadSafe(u64 userdata, s64 cyclesLate)
{
ppcState.iCache.Invalidate(static_cast<u32>(userdata));
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index feee9730f5..117e16b935 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -70,7 +70,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
-static const u32 STATE_VERSION = 55;
+static const u32 STATE_VERSION = 56;
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp
index 7d7ca38361..ab1910fd1e 100644
--- a/Source/Core/VideoBackends/Software/SWmain.cpp
+++ b/Source/Core/VideoBackends/Software/SWmain.cpp
@@ -142,7 +142,6 @@ bool VideoSoftware::Initialize(void* window_handle)
SWOGLWindow::Init(window_handle);
- PixelEngine::Init();
Clipper::Init();
Rasterizer::Init();
SWRenderer::Init();
diff --git a/Source/Core/VideoCommon/CommandProcessor.cpp b/Source/Core/VideoCommon/CommandProcessor.cpp
index 287d93d28f..32095d57b2 100644
--- a/Source/Core/VideoCommon/CommandProcessor.cpp
+++ b/Source/Core/VideoCommon/CommandProcessor.cpp
@@ -21,7 +21,7 @@
namespace CommandProcessor
{
-static int et_UpdateInterrupts;
+static CoreTiming::EventType* et_UpdateInterrupts;
// TODO(ector): Warn on bbox read/write
diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp
index 263d21193c..97cb8753e0 100644
--- a/Source/Core/VideoCommon/Fifo.cpp
+++ b/Source/Core/VideoCommon/Fifo.cpp
@@ -48,7 +48,7 @@ static u8* s_fifo_aux_read_ptr;
static bool s_use_deterministic_gpu_thread;
static u64 s_last_sync_gpu_tick;
-static int s_event_sync_gpu;
+static CoreTiming::EventType* s_event_sync_gpu;
// STATE_TO_SAVE
static u8* s_video_buffer;
diff --git a/Source/Core/VideoCommon/PixelEngine.cpp b/Source/Core/VideoCommon/PixelEngine.cpp
index f38eb71125..5271f11d9a 100644
--- a/Source/Core/VideoCommon/PixelEngine.cpp
+++ b/Source/Core/VideoCommon/PixelEngine.cpp
@@ -100,7 +100,7 @@ static bool s_event_raised;
static bool s_signal_token_interrupt;
static bool s_signal_finish_interrupt;
-static int et_SetTokenFinishOnMainThread;
+static CoreTiming::EventType* et_SetTokenFinishOnMainThread;
enum
{