diff options
| author | 2015-01-05 20:17:49 -0500 | |
|---|---|---|
| committer | 2015-01-07 15:08:35 -0500 | |
| commit | 9bf82beb4cbb0b75448a071179c3497187963248 (patch) | |
| tree | c7a99156a6aefb78436c625f1d3170ee4becccd0 /src/core/core_timing.cpp | |
| parent | Merge pull request #422 from lioncash/bxj (diff) | |
| download | yuzu-9bf82beb4cbb0b75448a071179c3497187963248.tar.gz yuzu-9bf82beb4cbb0b75448a071179c3497187963248.tar.xz yuzu-9bf82beb4cbb0b75448a071179c3497187963248.zip | |
CoreTiming: Ported the CoreTiming namespace from PPSSPP
Implemented the required calls to make it work.
CoreTiming: Added a new logging class Core_Timing.
Diffstat (limited to 'src/core/core_timing.cpp')
| -rw-r--r-- | src/core/core_timing.cpp | 709 |
1 files changed, 315 insertions, 394 deletions
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 321648b37..833199680 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp | |||
| @@ -1,16 +1,14 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | 1 | // Copyright (c) 2012- PPSSPP Project / Dolphin Project. |
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <vector> | ||
| 6 | #include <cstdio> | ||
| 7 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include <cstdio> | ||
| 8 | #include <mutex> | 7 | #include <mutex> |
| 8 | #include <vector> | ||
| 9 | 9 | ||
| 10 | #include "common/chunk_file.h" | 10 | #include "common/chunk_file.h" |
| 11 | #include "common/msg_handler.h" | 11 | #include "common/log.h" |
| 12 | #include "common/string_util.h" | ||
| 13 | |||
| 14 | #include "core/core.h" | 12 | #include "core/core.h" |
| 15 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 16 | 14 | ||
| @@ -22,16 +20,15 @@ int g_clock_rate_arm11 = 268123480; | |||
| 22 | 20 | ||
| 23 | namespace CoreTiming | 21 | namespace CoreTiming |
| 24 | { | 22 | { |
| 25 | |||
| 26 | struct EventType | 23 | struct EventType |
| 27 | { | 24 | { |
| 28 | EventType() {} | 25 | EventType() {} |
| 29 | 26 | ||
| 30 | EventType(TimedCallback cb, const char *n) | 27 | EventType(TimedCallback cb, const char* n) |
| 31 | : callback(cb), name(n) {} | 28 | : callback(cb), name(n) {} |
| 32 | 29 | ||
| 33 | TimedCallback callback; | 30 | TimedCallback callback; |
| 34 | const char *name; | 31 | const char* name; |
| 35 | }; | 32 | }; |
| 36 | 33 | ||
| 37 | std::vector<EventType> event_types; | 34 | std::vector<EventType> event_types; |
| @@ -41,262 +38,247 @@ struct BaseEvent | |||
| 41 | s64 time; | 38 | s64 time; |
| 42 | u64 userdata; | 39 | u64 userdata; |
| 43 | int type; | 40 | int type; |
| 44 | // Event *next; | ||
| 45 | }; | 41 | }; |
| 46 | 42 | ||
| 47 | typedef LinkedListItem<BaseEvent> Event; | 43 | typedef LinkedListItem<BaseEvent> Event; |
| 48 | 44 | ||
| 49 | Event *first; | 45 | Event* first; |
| 50 | Event *tsFirst; | 46 | Event* ts_first; |
| 51 | Event *tsLast; | 47 | Event* ts_last; |
| 52 | 48 | ||
| 53 | // event pools | 49 | // event pools |
| 54 | Event *eventPool = 0; | 50 | Event* event_pool = 0; |
| 55 | Event *eventTsPool = 0; | 51 | Event* event_ts_pool = 0; |
| 56 | int allocatedTsEvents = 0; | 52 | int allocated_ts_events = 0; |
| 57 | // Optimization to skip MoveEvents when possible. | 53 | // Optimization to skip MoveEvents when possible. |
| 58 | std::atomic<u32> hasTsEvents; | 54 | std::atomic<bool> has_ts_events(false); |
| 59 | 55 | ||
| 60 | // Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block | 56 | int g_slice_length; |
| 61 | // as we can already reach that structure through a register. | ||
| 62 | int slicelength; | ||
| 63 | 57 | ||
| 64 | MEMORY_ALIGNED16(s64) globalTimer; | 58 | s64 global_timer; |
| 65 | s64 idledCycles; | 59 | s64 idled_cycles; |
| 60 | s64 last_global_time_ticks; | ||
| 61 | s64 last_global_time_us; | ||
| 66 | 62 | ||
| 67 | static std::recursive_mutex externalEventSection; | 63 | static std::recursive_mutex external_event_section; |
| 68 | 64 | ||
| 69 | // Warning: not included in save state. | 65 | // Warning: not included in save state. |
| 70 | void(*advanceCallback)(int cyclesExecuted) = nullptr; | 66 | using AdvanceCallback = void(int cycles_executed); |
| 67 | AdvanceCallback* advance_callback = nullptr; | ||
| 68 | std::vector<MHzChangeCallback> mhz_change_callbacks; | ||
| 71 | 69 | ||
| 72 | void SetClockFrequencyMHz(int cpuMhz) | 70 | void FireMhzChange() { |
| 73 | { | 71 | for (auto callback : mhz_change_callbacks) |
| 74 | g_clock_rate_arm11 = cpuMhz * 1000000; | 72 | callback(); |
| 73 | } | ||
| 74 | |||
| 75 | void SetClockFrequencyMHz(int cpu_mhz) { | ||
| 76 | // When the mhz changes, we keep track of what "time" it was before hand. | ||
| 77 | // This way, time always moves forward, even if mhz is changed. | ||
| 78 | last_global_time_us = GetGlobalTimeUs(); | ||
| 79 | last_global_time_ticks = GetTicks(); | ||
| 80 | |||
| 81 | g_clock_rate_arm11 = cpu_mhz * 1000000; | ||
| 75 | // TODO: Rescale times of scheduled events? | 82 | // TODO: Rescale times of scheduled events? |
| 83 | |||
| 84 | FireMhzChange(); | ||
| 76 | } | 85 | } |
| 77 | 86 | ||
| 78 | int GetClockFrequencyMHz() | 87 | int GetClockFrequencyMHz() { |
| 79 | { | ||
| 80 | return g_clock_rate_arm11 / 1000000; | 88 | return g_clock_rate_arm11 / 1000000; |
| 81 | } | 89 | } |
| 82 | 90 | ||
| 91 | u64 GetGlobalTimeUs() { | ||
| 92 | s64 ticks_since_last = GetTicks() - last_global_time_ticks; | ||
| 93 | int freq = GetClockFrequencyMHz(); | ||
| 94 | s64 us_since_last = ticks_since_last / freq; | ||
| 95 | return last_global_time_us + us_since_last; | ||
| 96 | } | ||
| 83 | 97 | ||
| 84 | Event* GetNewEvent() | 98 | Event* GetNewEvent() { |
| 85 | { | 99 | if (!event_pool) |
| 86 | if (!eventPool) | ||
| 87 | return new Event; | 100 | return new Event; |
| 88 | 101 | ||
| 89 | Event* ev = eventPool; | 102 | Event* event = event_pool; |
| 90 | eventPool = ev->next; | 103 | event_pool = event->next; |
| 91 | return ev; | 104 | return event; |
| 92 | } | 105 | } |
| 93 | 106 | ||
| 94 | Event* GetNewTsEvent() | 107 | Event* GetNewTsEvent() { |
| 95 | { | 108 | allocated_ts_events++; |
| 96 | allocatedTsEvents++; | ||
| 97 | 109 | ||
| 98 | if (!eventTsPool) | 110 | if (!event_ts_pool) |
| 99 | return new Event; | 111 | return new Event; |
| 100 | 112 | ||
| 101 | Event* ev = eventTsPool; | 113 | Event* event = event_ts_pool; |
| 102 | eventTsPool = ev->next; | 114 | event_ts_pool = event->next; |
| 103 | return ev; | 115 | return event; |
| 104 | } | 116 | } |
| 105 | 117 | ||
| 106 | void FreeEvent(Event* ev) | 118 | void FreeEvent(Event* event) { |
| 107 | { | 119 | event->next = event_pool; |
| 108 | ev->next = eventPool; | 120 | event_pool = event; |
| 109 | eventPool = ev; | ||
| 110 | } | 121 | } |
| 111 | 122 | ||
| 112 | void FreeTsEvent(Event* ev) | 123 | void FreeTsEvent(Event* event) { |
| 113 | { | 124 | event->next = event_ts_pool; |
| 114 | ev->next = eventTsPool; | 125 | event_ts_pool = event; |
| 115 | eventTsPool = ev; | 126 | allocated_ts_events--; |
| 116 | allocatedTsEvents--; | ||
| 117 | } | 127 | } |
| 118 | 128 | ||
| 119 | int RegisterEvent(const char *name, TimedCallback callback) | 129 | int RegisterEvent(const char* name, TimedCallback callback) { |
| 120 | { | ||
| 121 | event_types.push_back(EventType(callback, name)); | 130 | event_types.push_back(EventType(callback, name)); |
| 122 | return (int)event_types.size() - 1; | 131 | return (int)event_types.size() - 1; |
| 123 | } | 132 | } |
| 124 | 133 | ||
| 125 | void AntiCrashCallback(u64 userdata, int cyclesLate) | 134 | void AntiCrashCallback(u64 userdata, int cycles_late) { |
| 126 | { | 135 | LOG_CRITICAL(Core_Timing, "Savestate broken: an unregistered event was called."); |
| 127 | LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called."); | ||
| 128 | Core::Halt("invalid timing events"); | 136 | Core::Halt("invalid timing events"); |
| 129 | } | 137 | } |
| 130 | 138 | ||
| 131 | void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) | 139 | void RestoreRegisterEvent(int event_type, const char* name, TimedCallback callback) { |
| 132 | { | ||
| 133 | if (event_type >= (int)event_types.size()) | 140 | if (event_type >= (int)event_types.size()) |
| 134 | event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); | 141 | event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); |
| 135 | 142 | ||
| 136 | event_types[event_type] = EventType(callback, name); | 143 | event_types[event_type] = EventType(callback, name); |
| 137 | } | 144 | } |
| 138 | 145 | ||
| 139 | void UnregisterAllEvents() | 146 | void UnregisterAllEvents() { |
| 140 | { | ||
| 141 | if (first) | 147 | if (first) |
| 142 | PanicAlert("Cannot unregister events with events pending"); | 148 | PanicAlert("Cannot unregister events with events pending"); |
| 143 | event_types.clear(); | 149 | event_types.clear(); |
| 144 | } | 150 | } |
| 145 | 151 | ||
| 146 | void Init() | 152 | void Init() { |
| 147 | { | 153 | Core::g_app_core->down_count = INITIAL_SLICE_LENGTH; |
| 148 | //currentMIPS->downcount = INITIAL_SLICE_LENGTH; | 154 | g_slice_length = INITIAL_SLICE_LENGTH; |
| 149 | //slicelength = INITIAL_SLICE_LENGTH; | 155 | global_timer = 0; |
| 150 | globalTimer = 0; | 156 | idled_cycles = 0; |
| 151 | idledCycles = 0; | 157 | last_global_time_ticks = 0; |
| 152 | hasTsEvents = 0; | 158 | last_global_time_us = 0; |
| 159 | has_ts_events = 0; | ||
| 160 | mhz_change_callbacks.clear(); | ||
| 153 | } | 161 | } |
| 154 | 162 | ||
| 155 | void Shutdown() | 163 | void Shutdown() { |
| 156 | { | ||
| 157 | MoveEvents(); | 164 | MoveEvents(); |
| 158 | ClearPendingEvents(); | 165 | ClearPendingEvents(); |
| 159 | UnregisterAllEvents(); | 166 | UnregisterAllEvents(); |
| 160 | 167 | ||
| 161 | while (eventPool) | 168 | while (event_pool) { |
| 162 | { | 169 | Event* event = event_pool; |
| 163 | Event *ev = eventPool; | 170 | event_pool = event->next; |
| 164 | eventPool = ev->next; | 171 | delete event; |
| 165 | delete ev; | ||
| 166 | } | 172 | } |
| 167 | 173 | ||
| 168 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 174 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 169 | while (eventTsPool) | 175 | while (event_ts_pool) { |
| 170 | { | 176 | Event* event = event_ts_pool; |
| 171 | Event *ev = eventTsPool; | 177 | event_ts_pool = event->next; |
| 172 | eventTsPool = ev->next; | 178 | delete event; |
| 173 | delete ev; | ||
| 174 | } | 179 | } |
| 175 | } | 180 | } |
| 176 | 181 | ||
| 177 | u64 GetTicks() | 182 | u64 GetTicks() { |
| 178 | { | 183 | return (u64)global_timer + g_slice_length - Core::g_app_core->down_count; |
| 179 | LOG_ERROR(Core, "Unimplemented function!"); | ||
| 180 | return 0; | ||
| 181 | //return (u64)globalTimer + slicelength - currentMIPS->downcount; | ||
| 182 | } | 184 | } |
| 183 | 185 | ||
| 184 | u64 GetIdleTicks() | 186 | u64 GetIdleTicks() { |
| 185 | { | 187 | return (u64)idled_cycles; |
| 186 | return (u64)idledCycles; | ||
| 187 | } | 188 | } |
| 188 | 189 | ||
| 189 | 190 | ||
| 190 | // This is to be called when outside threads, such as the graphics thread, wants to | 191 | // This is to be called when outside threads, such as the graphics thread, wants to |
| 191 | // schedule things to be executed on the main thread. | 192 | // schedule things to be executed on the main thread. |
| 192 | void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata) | 193 | void ScheduleEvent_Threadsafe(s64 cycles_into_future, int event_type, u64 userdata) { |
| 193 | { | 194 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 194 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 195 | Event* new_event = GetNewTsEvent(); |
| 195 | Event *ne = GetNewTsEvent(); | 196 | new_event->time = GetTicks() + cycles_into_future; |
| 196 | ne->time = GetTicks() + cyclesIntoFuture; | 197 | new_event->type = event_type; |
| 197 | ne->type = event_type; | 198 | new_event->next = 0; |
| 198 | ne->next = 0; | 199 | new_event->userdata = userdata; |
| 199 | ne->userdata = userdata; | 200 | if (!ts_first) |
| 200 | if (!tsFirst) | 201 | ts_first = new_event; |
| 201 | tsFirst = ne; | 202 | if (ts_last) |
| 202 | if (tsLast) | 203 | ts_last->next = new_event; |
| 203 | tsLast->next = ne; | 204 | ts_last = new_event; |
| 204 | tsLast = ne; | 205 | |
| 205 | 206 | has_ts_events = true; | |
| 206 | hasTsEvents.store(1, std::memory_order_release); | ||
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread | 209 | // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread |
| 210 | // in which case the event will get handled immediately, before returning. | 210 | // in which case the event will get handled immediately, before returning. |
| 211 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) | 211 | void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) { |
| 212 | { | ||
| 213 | if (false) //Core::IsCPUThread()) | 212 | if (false) //Core::IsCPUThread()) |
| 214 | { | 213 | { |
| 215 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 214 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 216 | event_types[event_type].callback(userdata, 0); | 215 | event_types[event_type].callback(userdata, 0); |
| 217 | } | 216 | } |
| 218 | else | 217 | else |
| 219 | ScheduleEvent_Threadsafe(0, event_type, userdata); | 218 | ScheduleEvent_Threadsafe(0, event_type, userdata); |
| 220 | } | 219 | } |
| 221 | 220 | ||
| 222 | void ClearPendingEvents() | 221 | void ClearPendingEvents() { |
| 223 | { | 222 | while (first) { |
| 224 | while (first) | 223 | Event* event = first->next; |
| 225 | { | ||
| 226 | Event *e = first->next; | ||
| 227 | FreeEvent(first); | 224 | FreeEvent(first); |
| 228 | first = e; | 225 | first = event; |
| 229 | } | 226 | } |
| 230 | } | 227 | } |
| 231 | 228 | ||
| 232 | void AddEventToQueue(Event* ne) | 229 | void AddEventToQueue(Event* new_event) { |
| 233 | { | 230 | Event* prev_event = nullptr; |
| 234 | Event* prev = nullptr; | 231 | Event** next_event = &first; |
| 235 | Event** pNext = &first; | 232 | for (;;) { |
| 236 | for (;;) | 233 | Event*& next = *next_event; |
| 237 | { | 234 | if (!next || new_event->time < next->time) { |
| 238 | Event*& next = *pNext; | 235 | new_event->next = next; |
| 239 | if (!next || ne->time < next->time) | 236 | next = new_event; |
| 240 | { | ||
| 241 | ne->next = next; | ||
| 242 | next = ne; | ||
| 243 | break; | 237 | break; |
| 244 | } | 238 | } |
| 245 | prev = next; | 239 | prev_event = next; |
| 246 | pNext = &prev->next; | 240 | next_event = &prev_event->next; |
| 247 | } | 241 | } |
| 248 | } | 242 | } |
| 249 | 243 | ||
| 250 | // This must be run ONLY from within the cpu thread | 244 | void ScheduleEvent(s64 cycles_into_future, int event_type, u64 userdata) { |
| 251 | // cyclesIntoFuture may be VERY inaccurate if called from anything else | 245 | Event* new_event = GetNewEvent(); |
| 252 | // than Advance | 246 | new_event->userdata = userdata; |
| 253 | void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) | 247 | new_event->type = event_type; |
| 254 | { | 248 | new_event->time = GetTicks() + cycles_into_future; |
| 255 | Event *ne = GetNewEvent(); | 249 | AddEventToQueue(new_event); |
| 256 | ne->userdata = userdata; | ||
| 257 | ne->type = event_type; | ||
| 258 | ne->time = GetTicks() + cyclesIntoFuture; | ||
| 259 | AddEventToQueue(ne); | ||
| 260 | } | 250 | } |
| 261 | 251 | ||
| 262 | // Returns cycles left in timer. | 252 | s64 UnscheduleEvent(int event_type, u64 userdata) { |
| 263 | s64 UnscheduleEvent(int event_type, u64 userdata) | ||
| 264 | { | ||
| 265 | s64 result = 0; | 253 | s64 result = 0; |
| 266 | if (!first) | 254 | if (!first) |
| 267 | return result; | 255 | return result; |
| 268 | while (first) | 256 | while (first) { |
| 269 | { | 257 | if (first->type == event_type && first->userdata == userdata) { |
| 270 | if (first->type == event_type && first->userdata == userdata) | 258 | result = first->time - GetTicks(); |
| 271 | { | ||
| 272 | result = first->time - globalTimer; | ||
| 273 | 259 | ||
| 274 | Event *next = first->next; | 260 | Event* next = first->next; |
| 275 | FreeEvent(first); | 261 | FreeEvent(first); |
| 276 | first = next; | 262 | first = next; |
| 277 | } | 263 | } else { |
| 278 | else | ||
| 279 | { | ||
| 280 | break; | 264 | break; |
| 281 | } | 265 | } |
| 282 | } | 266 | } |
| 283 | if (!first) | 267 | if (!first) |
| 284 | return result; | 268 | return result; |
| 285 | Event *prev = first; | ||
| 286 | Event *ptr = prev->next; | ||
| 287 | while (ptr) | ||
| 288 | { | ||
| 289 | if (ptr->type == event_type && ptr->userdata == userdata) | ||
| 290 | { | ||
| 291 | result = ptr->time - globalTimer; | ||
| 292 | 269 | ||
| 293 | prev->next = ptr->next; | 270 | Event* prev_event = first; |
| 271 | Event* ptr = prev_event->next; | ||
| 272 | |||
| 273 | while (ptr) { | ||
| 274 | if (ptr->type == event_type && ptr->userdata == userdata) { | ||
| 275 | result = ptr->time - GetTicks(); | ||
| 276 | |||
| 277 | prev_event->next = ptr->next; | ||
| 294 | FreeEvent(ptr); | 278 | FreeEvent(ptr); |
| 295 | ptr = prev->next; | 279 | ptr = prev_event->next; |
| 296 | } | 280 | } else { |
| 297 | else | 281 | prev_event = ptr; |
| 298 | { | ||
| 299 | prev = ptr; | ||
| 300 | ptr = ptr->next; | 282 | ptr = ptr->next; |
| 301 | } | 283 | } |
| 302 | } | 284 | } |
| @@ -304,51 +286,44 @@ s64 UnscheduleEvent(int event_type, u64 userdata) | |||
| 304 | return result; | 286 | return result; |
| 305 | } | 287 | } |
| 306 | 288 | ||
| 307 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) | 289 | s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) { |
| 308 | { | ||
| 309 | s64 result = 0; | 290 | s64 result = 0; |
| 310 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 291 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 311 | if (!tsFirst) | 292 | if (!ts_first) |
| 312 | return result; | 293 | return result; |
| 313 | while (tsFirst) | ||
| 314 | { | ||
| 315 | if (tsFirst->type == event_type && tsFirst->userdata == userdata) | ||
| 316 | { | ||
| 317 | result = tsFirst->time - globalTimer; | ||
| 318 | 294 | ||
| 319 | Event *next = tsFirst->next; | 295 | while (ts_first) { |
| 320 | FreeTsEvent(tsFirst); | 296 | if (ts_first->type == event_type && ts_first->userdata == userdata) { |
| 321 | tsFirst = next; | 297 | result = ts_first->time - GetTicks(); |
| 322 | } | 298 | |
| 323 | else | 299 | Event* next = ts_first->next; |
| 324 | { | 300 | FreeTsEvent(ts_first); |
| 301 | ts_first = next; | ||
| 302 | } else { | ||
| 325 | break; | 303 | break; |
| 326 | } | 304 | } |
| 327 | } | 305 | } |
| 328 | if (!tsFirst) | 306 | |
| 307 | if (!ts_first) | ||
| 329 | { | 308 | { |
| 330 | tsLast = nullptr; | 309 | ts_last = nullptr; |
| 331 | return result; | 310 | return result; |
| 332 | } | 311 | } |
| 333 | 312 | ||
| 334 | Event *prev = tsFirst; | 313 | Event* prev_event = ts_first; |
| 335 | Event *ptr = prev->next; | 314 | Event* next = prev_event->next; |
| 336 | while (ptr) | 315 | while (next) { |
| 337 | { | 316 | if (next->type == event_type && next->userdata == userdata) { |
| 338 | if (ptr->type == event_type && ptr->userdata == userdata) | 317 | result = next->time - GetTicks(); |
| 339 | { | 318 | |
| 340 | result = ptr->time - globalTimer; | 319 | prev_event->next = next->next; |
| 341 | 320 | if (next == ts_last) | |
| 342 | prev->next = ptr->next; | 321 | ts_last = prev_event; |
| 343 | if (ptr == tsLast) | 322 | FreeTsEvent(next); |
| 344 | tsLast = prev; | 323 | next = prev_event->next; |
| 345 | FreeTsEvent(ptr); | 324 | } else { |
| 346 | ptr = prev->next; | 325 | prev_event = next; |
| 347 | } | 326 | next = next->next; |
| 348 | else | ||
| 349 | { | ||
| 350 | prev = ptr; | ||
| 351 | ptr = ptr->next; | ||
| 352 | } | 327 | } |
| 353 | } | 328 | } |
| 354 | 329 | ||
| @@ -356,271 +331,217 @@ s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) | |||
| 356 | } | 331 | } |
| 357 | 332 | ||
| 358 | // Warning: not included in save state. | 333 | // Warning: not included in save state. |
| 359 | void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)) | 334 | void RegisterAdvanceCallback(AdvanceCallback* callback) { |
| 360 | { | 335 | advance_callback = callback; |
| 361 | advanceCallback = callback; | ||
| 362 | } | 336 | } |
| 363 | 337 | ||
| 364 | bool IsScheduled(int event_type) | 338 | void RegisterMHzChangeCallback(MHzChangeCallback callback) { |
| 365 | { | 339 | mhz_change_callbacks.push_back(callback); |
| 340 | } | ||
| 341 | |||
| 342 | bool IsScheduled(int event_type) { | ||
| 366 | if (!first) | 343 | if (!first) |
| 367 | return false; | 344 | return false; |
| 368 | Event *e = first; | 345 | Event* event = first; |
| 369 | while (e) { | 346 | while (event) { |
| 370 | if (e->type == event_type) | 347 | if (event->type == event_type) |
| 371 | return true; | 348 | return true; |
| 372 | e = e->next; | 349 | event = event->next; |
| 373 | } | 350 | } |
| 374 | return false; | 351 | return false; |
| 375 | } | 352 | } |
| 376 | 353 | ||
| 377 | void RemoveEvent(int event_type) | 354 | void RemoveEvent(int event_type) { |
| 378 | { | ||
| 379 | if (!first) | 355 | if (!first) |
| 380 | return; | 356 | return; |
| 381 | while (first) | 357 | while (first) { |
| 382 | { | 358 | if (first->type == event_type) { |
| 383 | if (first->type == event_type) | ||
| 384 | { | ||
| 385 | Event *next = first->next; | 359 | Event *next = first->next; |
| 386 | FreeEvent(first); | 360 | FreeEvent(first); |
| 387 | first = next; | 361 | first = next; |
| 388 | } | 362 | } else { |
| 389 | else | ||
| 390 | { | ||
| 391 | break; | 363 | break; |
| 392 | } | 364 | } |
| 393 | } | 365 | } |
| 394 | if (!first) | 366 | if (!first) |
| 395 | return; | 367 | return; |
| 396 | Event *prev = first; | 368 | Event* prev = first; |
| 397 | Event *ptr = prev->next; | 369 | Event* next = prev->next; |
| 398 | while (ptr) | 370 | while (next) { |
| 399 | { | 371 | if (next->type == event_type) { |
| 400 | if (ptr->type == event_type) | 372 | prev->next = next->next; |
| 401 | { | 373 | FreeEvent(next); |
| 402 | prev->next = ptr->next; | 374 | next = prev->next; |
| 403 | FreeEvent(ptr); | 375 | } else { |
| 404 | ptr = prev->next; | 376 | prev = next; |
| 405 | } | 377 | next = next->next; |
| 406 | else | ||
| 407 | { | ||
| 408 | prev = ptr; | ||
| 409 | ptr = ptr->next; | ||
| 410 | } | 378 | } |
| 411 | } | 379 | } |
| 412 | } | 380 | } |
| 413 | 381 | ||
| 414 | void RemoveThreadsafeEvent(int event_type) | 382 | void RemoveThreadsafeEvent(int event_type) { |
| 415 | { | 383 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 416 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 384 | if (!ts_first) |
| 417 | if (!tsFirst) | ||
| 418 | { | ||
| 419 | return; | 385 | return; |
| 420 | } | 386 | |
| 421 | while (tsFirst) | 387 | while (ts_first) { |
| 422 | { | 388 | if (ts_first->type == event_type) { |
| 423 | if (tsFirst->type == event_type) | 389 | Event* next = ts_first->next; |
| 424 | { | 390 | FreeTsEvent(ts_first); |
| 425 | Event *next = tsFirst->next; | 391 | ts_first = next; |
| 426 | FreeTsEvent(tsFirst); | 392 | } else { |
| 427 | tsFirst = next; | ||
| 428 | } | ||
| 429 | else | ||
| 430 | { | ||
| 431 | break; | 393 | break; |
| 432 | } | 394 | } |
| 433 | } | 395 | } |
| 434 | if (!tsFirst) | 396 | |
| 435 | { | 397 | if (!ts_first) { |
| 436 | tsLast = nullptr; | 398 | ts_last = nullptr; |
| 437 | return; | 399 | return; |
| 438 | } | 400 | } |
| 439 | Event *prev = tsFirst; | 401 | |
| 440 | Event *ptr = prev->next; | 402 | Event* prev = ts_first; |
| 441 | while (ptr) | 403 | Event* next = prev->next; |
| 442 | { | 404 | while (next) { |
| 443 | if (ptr->type == event_type) | 405 | if (next->type == event_type) { |
| 444 | { | 406 | prev->next = next->next; |
| 445 | prev->next = ptr->next; | 407 | if (next == ts_last) |
| 446 | if (ptr == tsLast) | 408 | ts_last = prev; |
| 447 | tsLast = prev; | 409 | FreeTsEvent(next); |
| 448 | FreeTsEvent(ptr); | 410 | next = prev->next; |
| 449 | ptr = prev->next; | 411 | } else { |
| 450 | } | 412 | prev = next; |
| 451 | else | 413 | next = next->next; |
| 452 | { | ||
| 453 | prev = ptr; | ||
| 454 | ptr = ptr->next; | ||
| 455 | } | 414 | } |
| 456 | } | 415 | } |
| 457 | } | 416 | } |
| 458 | 417 | ||
| 459 | void RemoveAllEvents(int event_type) | 418 | void RemoveAllEvents(int event_type) { |
| 460 | { | ||
| 461 | RemoveThreadsafeEvent(event_type); | 419 | RemoveThreadsafeEvent(event_type); |
| 462 | RemoveEvent(event_type); | 420 | RemoveEvent(event_type); |
| 463 | } | 421 | } |
| 464 | 422 | ||
| 465 | //This raise only the events required while the fifo is processing data | 423 | // This raise only the events required while the fifo is processing data |
| 466 | void ProcessFifoWaitEvents() | 424 | void ProcessFifoWaitEvents() { |
| 467 | { | 425 | while (first) { |
| 468 | while (first) | 426 | if (first->time <= (s64)GetTicks()) { |
| 469 | { | ||
| 470 | if (first->time <= globalTimer) | ||
| 471 | { | ||
| 472 | //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", | ||
| 473 | // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); | ||
| 474 | Event* evt = first; | 427 | Event* evt = first; |
| 475 | first = first->next; | 428 | first = first->next; |
| 476 | event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); | 429 | event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time)); |
| 477 | FreeEvent(evt); | 430 | FreeEvent(evt); |
| 478 | } | 431 | } else { |
| 479 | else | ||
| 480 | { | ||
| 481 | break; | 432 | break; |
| 482 | } | 433 | } |
| 483 | } | 434 | } |
| 484 | } | 435 | } |
| 485 | 436 | ||
| 486 | void MoveEvents() | 437 | void MoveEvents() { |
| 487 | { | 438 | has_ts_events = false; |
| 488 | hasTsEvents.store(0, std::memory_order_release); | ||
| 489 | 439 | ||
| 490 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 440 | std::lock_guard<std::recursive_mutex> lock(external_event_section); |
| 491 | // Move events from async queue into main queue | 441 | // Move events from async queue into main queue |
| 492 | while (tsFirst) | 442 | while (ts_first) { |
| 493 | { | 443 | Event* next = ts_first->next; |
| 494 | Event *next = tsFirst->next; | 444 | AddEventToQueue(ts_first); |
| 495 | AddEventToQueue(tsFirst); | 445 | ts_first = next; |
| 496 | tsFirst = next; | ||
| 497 | } | 446 | } |
| 498 | tsLast = nullptr; | 447 | ts_last = nullptr; |
| 499 | 448 | ||
| 500 | // Move free events to threadsafe pool | 449 | // Move free events to threadsafe pool |
| 501 | while (allocatedTsEvents > 0 && eventPool) | 450 | while (allocated_ts_events > 0 && event_pool) { |
| 502 | { | 451 | Event* event = event_pool; |
| 503 | Event *ev = eventPool; | 452 | event_pool = event->next; |
| 504 | eventPool = ev->next; | 453 | event->next = event_ts_pool; |
| 505 | ev->next = eventTsPool; | 454 | event_ts_pool = event; |
| 506 | eventTsPool = ev; | 455 | allocated_ts_events--; |
| 507 | allocatedTsEvents--; | ||
| 508 | } | 456 | } |
| 509 | } | 457 | } |
| 510 | 458 | ||
| 511 | void Advance() | 459 | void ForceCheck() { |
| 512 | { | 460 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 513 | LOG_ERROR(Core, "Unimplemented function!"); | 461 | global_timer += cycles_executed; |
| 514 | //int cyclesExecuted = slicelength - currentMIPS->downcount; | 462 | // This will cause us to check for new events immediately. |
| 515 | //globalTimer += cyclesExecuted; | 463 | Core::g_app_core->down_count = 0; |
| 516 | //currentMIPS->downcount = slicelength; | 464 | // But let's not eat a bunch more time in Advance() because of this. |
| 517 | 465 | g_slice_length = 0; | |
| 518 | //if (Common::AtomicLoadAcquire(hasTsEvents)) | ||
| 519 | // MoveEvents(); | ||
| 520 | //ProcessFifoWaitEvents(); | ||
| 521 | |||
| 522 | //if (!first) | ||
| 523 | //{ | ||
| 524 | // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); | ||
| 525 | // currentMIPS->downcount += 10000; | ||
| 526 | //} | ||
| 527 | //else | ||
| 528 | //{ | ||
| 529 | // slicelength = (int)(first->time - globalTimer); | ||
| 530 | // if (slicelength > MAX_SLICE_LENGTH) | ||
| 531 | // slicelength = MAX_SLICE_LENGTH; | ||
| 532 | // currentMIPS->downcount = slicelength; | ||
| 533 | //} | ||
| 534 | //if (advanceCallback) | ||
| 535 | // advanceCallback(cyclesExecuted); | ||
| 536 | } | ||
| 537 | |||
| 538 | void LogPendingEvents() | ||
| 539 | { | ||
| 540 | Event *ptr = first; | ||
| 541 | while (ptr) | ||
| 542 | { | ||
| 543 | //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); | ||
| 544 | ptr = ptr->next; | ||
| 545 | } | ||
| 546 | } | 466 | } |
| 547 | 467 | ||
| 548 | void Idle(int maxIdle) | 468 | void Advance() { |
| 549 | { | 469 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 550 | LOG_ERROR(Core, "Unimplemented function!"); | 470 | global_timer += cycles_executed; |
| 551 | //int cyclesDown = currentMIPS->downcount; | 471 | Core::g_app_core->down_count = g_slice_length; |
| 552 | //if (maxIdle != 0 && cyclesDown > maxIdle) | ||
| 553 | // cyclesDown = maxIdle; | ||
| 554 | |||
| 555 | //if (first && cyclesDown > 0) | ||
| 556 | //{ | ||
| 557 | // int cyclesExecuted = slicelength - currentMIPS->downcount; | ||
| 558 | // int cyclesNextEvent = (int) (first->time - globalTimer); | ||
| 559 | |||
| 560 | // if (cyclesNextEvent < cyclesExecuted + cyclesDown) | ||
| 561 | // { | ||
| 562 | // cyclesDown = cyclesNextEvent - cyclesExecuted; | ||
| 563 | // // Now, now... no time machines, please. | ||
| 564 | // if (cyclesDown < 0) | ||
| 565 | // cyclesDown = 0; | ||
| 566 | // } | ||
| 567 | //} | ||
| 568 | |||
| 569 | //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); | ||
| 570 | |||
| 571 | //idledCycles += cyclesDown; | ||
| 572 | //currentMIPS->downcount -= cyclesDown; | ||
| 573 | //if (currentMIPS->downcount == 0) | ||
| 574 | // currentMIPS->downcount = -1; | ||
| 575 | } | ||
| 576 | |||
| 577 | std::string GetScheduledEventsSummary() | ||
| 578 | { | ||
| 579 | Event *ptr = first; | ||
| 580 | std::string text = "Scheduled events\n"; | ||
| 581 | text.reserve(1000); | ||
| 582 | while (ptr) | ||
| 583 | { | ||
| 584 | unsigned int t = ptr->type; | ||
| 585 | if (t >= event_types.size()) | ||
| 586 | PanicAlert("Invalid event type"); // %i", t); | ||
| 587 | const char *name = event_types[ptr->type].name; | ||
| 588 | if (!name) | ||
| 589 | name = "[unknown]"; | ||
| 590 | 472 | ||
| 591 | text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time, | 473 | if (has_ts_events) |
| 592 | (u32)(ptr->userdata >> 32), (u32)(ptr->userdata)); | 474 | MoveEvents(); |
| 475 | ProcessFifoWaitEvents(); | ||
| 593 | 476 | ||
| 594 | ptr = ptr->next; | 477 | if (!first) { |
| 478 | if (g_slice_length < 10000) { | ||
| 479 | g_slice_length += 10000; | ||
| 480 | Core::g_app_core->down_count += g_slice_length; | ||
| 481 | } | ||
| 482 | } else { | ||
| 483 | // Note that events can eat cycles as well. | ||
| 484 | int target = (int)(first->time - global_timer); | ||
| 485 | if (target > MAX_SLICE_LENGTH) | ||
| 486 | target = MAX_SLICE_LENGTH; | ||
| 487 | |||
| 488 | const int diff = target - g_slice_length; | ||
| 489 | g_slice_length += diff; | ||
| 490 | Core::g_app_core->down_count += diff; | ||
| 595 | } | 491 | } |
| 596 | return text; | 492 | if (advance_callback) |
| 493 | advance_callback(cycles_executed); | ||
| 597 | } | 494 | } |
| 598 | 495 | ||
| 599 | void Event_DoState(PointerWrap &p, BaseEvent *ev) | 496 | void LogPendingEvents() { |
| 600 | { | 497 | Event* event = first; |
| 601 | p.Do(*ev); | 498 | while (event) { |
| 499 | //LOG_TRACE(Core_Timing, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, next->time, next->type); | ||
| 500 | event = event->next; | ||
| 501 | } | ||
| 602 | } | 502 | } |
| 603 | 503 | ||
| 604 | void DoState(PointerWrap &p) | 504 | void Idle(int max_idle) { |
| 605 | { | 505 | int cycles_down = Core::g_app_core->down_count; |
| 606 | std::lock_guard<std::recursive_mutex> lk(externalEventSection); | 506 | if (max_idle != 0 && cycles_down > max_idle) |
| 507 | cycles_down = max_idle; | ||
| 607 | 508 | ||
| 608 | auto s = p.Section("CoreTiming", 1); | 509 | if (first && cycles_down > 0) { |
| 609 | if (!s) | 510 | int cycles_executed = g_slice_length - Core::g_app_core->down_count; |
| 610 | return; | 511 | int cycles_next_event = (int)(first->time - global_timer); |
| 611 | 512 | ||
| 612 | int n = (int)event_types.size(); | 513 | if (cycles_next_event < cycles_executed + cycles_down) { |
| 613 | p.Do(n); | 514 | cycles_down = cycles_next_event - cycles_executed; |
| 614 | // These (should) be filled in later by the modules. | 515 | // Now, now... no time machines, please. |
| 615 | event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); | 516 | if (cycles_down < 0) |
| 517 | cycles_down = 0; | ||
| 518 | } | ||
| 519 | } | ||
| 616 | 520 | ||
| 617 | p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)nullptr); | 521 | LOG_TRACE(Core_Timing, "Idle for %i cycles! (%f ms)", cycles_down, cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); |
| 618 | p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast); | ||
| 619 | 522 | ||
| 620 | p.Do(g_clock_rate_arm11); | 523 | idled_cycles += cycles_down; |
| 621 | p.Do(slicelength); | 524 | Core::g_app_core->down_count -= cycles_down; |
| 622 | p.Do(globalTimer); | 525 | if (Core::g_app_core->down_count == 0) |
| 623 | p.Do(idledCycles); | 526 | Core::g_app_core->down_count = -1; |
| 527 | } | ||
| 528 | |||
| 529 | std::string GetScheduledEventsSummary() { | ||
| 530 | Event* event = first; | ||
| 531 | std::string text = "Scheduled events\n"; | ||
| 532 | text.reserve(1000); | ||
| 533 | while (event) { | ||
| 534 | unsigned int t = event->type; | ||
| 535 | if (t >= event_types.size()) | ||
| 536 | PanicAlert("Invalid event type"); // %i", t); | ||
| 537 | const char* name = event_types[event->type].name; | ||
| 538 | if (!name) | ||
| 539 | name = "[unknown]"; | ||
| 540 | text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)event->time, | ||
| 541 | (u32)(event->userdata >> 32), (u32)(event->userdata)); | ||
| 542 | event = event->next; | ||
| 543 | } | ||
| 544 | return text; | ||
| 624 | } | 545 | } |
| 625 | 546 | ||
| 626 | } // namespace | 547 | } // namespace |