diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index ad8cb71139..329f63a7ae 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "Common/Assert.h" @@ -28,16 +29,14 @@ namespace CoreTiming struct EventType { TimedCallback callback; - std::string name; + const std::string* name; }; -static std::vector s_event_types; - struct Event { s64 time; u64 userdata; - int type; + EventType* type; }; constexpr bool operator>(const Event& left, const Event& right) @@ -49,6 +48,10 @@ constexpr bool operator<(const Event& left, const Event& right) return left.time < right.time; } +// 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 @@ -74,7 +77,7 @@ s64 g_global_timer; u64 g_fake_TB_start_value; u64 g_fake_TB_start_ticks; -static int s_ev_lost; +static EventType* s_ev_lost = nullptr; static void EmptyTimedCallback(u64 userdata, s64 cyclesLate) { @@ -97,38 +100,25 @@ static int CyclesToDowncount(int cycles) return static_cast(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 : s_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()); - s_event_types.push_back(type); - return static_cast(s_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() { _assert_msg_(POWERPC, s_event_queue.empty(), "Cannot unregister events with events pending"); s_event_types.clear(); - s_event_types.shrink_to_fit(); } void Init() @@ -179,16 +169,15 @@ void DoState(PointerWrap& p) // 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 = s_event_types[ev.type].name; + name = *ev.type->name; pw.Do(name); if (pw.GetMode() == PointerWrap::MODE_READ) { - auto itr = std::find_if(s_event_types.begin(), s_event_types.end(), - [&](const EventType& evt) { return evt.name == name; }); + auto itr = s_event_types.find(name); if (itr != s_event_types.end()) { - ev.type = static_cast(itr - s_event_types.begin()); + ev.type = &itr->second; } else { @@ -231,8 +220,10 @@ void ClearPendingEvents() s_event_queue.clear(); } -void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata, FromThread from) +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."); + bool from_cpu_thread; if (from == FromThread::ANY) { @@ -262,7 +253,7 @@ 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.", - s_event_types[event_type].name.c_str()); + event_type->name->c_str()); } std::lock_guard lk(s_ts_write_lock); @@ -270,7 +261,7 @@ void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata, FromThr } } -void RemoveEvent(int event_type) +void RemoveEvent(EventType* event_type) { auto itr = std::remove_if(s_event_queue.begin(), s_event_queue.end(), [&](const Event& e) { return e.type == event_type; }); @@ -283,7 +274,7 @@ void RemoveEvent(int event_type) } } -void RemoveAllEvents(int event_type) +void RemoveAllEvents(EventType* event_type) { MoveEvents(); RemoveEvent(event_type); @@ -328,7 +319,7 @@ void Advance() s_event_queue.pop_back(); // NOTICE_LOG(POWERPC, "[Scheduler] %-20s (%lld, %lld)", evt.type->name->c_str(), // g_global_timer, evt.time); - s_event_types[evt.type].callback(evt.userdata, g_global_timer - evt.time); + evt.type->callback(evt.userdata, g_global_timer - evt.time); } s_is_global_timer_sane = false; @@ -355,10 +346,8 @@ void LogPendingEvents() std::sort(clone.begin(), clone.end()); for (const Event& ev : clone) { - INFO_LOG(POWERPC, "PENDING: Now: %" PRId64 " Pending: %" PRId64 " Type: %s (%d)", - g_global_timer, ev.time, - ev.type < s_event_types.size() ? s_event_types[ev.type].name.c_str() : "", - ev.type); + INFO_LOG(POWERPC, "PENDING: Now: %" PRId64 " Pending: %" PRId64 " Type: %s", g_global_timer, + ev.time, ev.type->name->c_str()); } } @@ -385,17 +374,8 @@ std::string GetScheduledEventsSummary() std::sort(clone.begin(), clone.end()); for (const Event& ev : clone) { - unsigned int t = ev.type; - if (t >= s_event_types.size()) - { - PanicAlertT("Invalid event type %i", t); - continue; - } - - const std::string& name = s_event_types[ev.type].name; - - text += - StringFromFormat("%s : %" PRIi64 " %016" PRIx64 "\n", name.c_str(), ev.time, ev.userdata); + text += StringFromFormat("%s : %" PRIi64 " %016" PRIx64 "\n", ev.type->name->c_str(), ev.time, + ev.userdata); } return text; } diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 4fd76e4df7..4f209c97bd 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -43,9 +43,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 +60,12 @@ 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, +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); 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..32c6ce80a7 100644 --- a/Source/Core/Core/HW/EXI.cpp +++ b/Source/Core/Core/HW/EXI.cpp @@ -23,8 +23,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, MAX_EXI_CHANNELS> g_Channels; diff --git a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h index 53763e7e5f..5cafcfce29 100644 --- a/Source/Core/Core/HW/EXI_DeviceMemoryCard.h +++ b/Source/Core/Core/HW/EXI_DeviceMemoryCard.h @@ -9,6 +9,10 @@ #include "Core/HW/EXI_Device.h" +namespace CoreTiming +{ +struct EventType; +} class MemoryCardBase; class PointerWrap; @@ -67,7 +71,8 @@ private: }; int card_index; - int et_cmd_done, et_transfer_complete; + CoreTiming::EventType* et_cmd_done = nullptr; + CoreTiming::EventType* et_transfer_complete = nullptr; //! 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 ff24dd3447..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) 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 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/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(userdata)); 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 {