// Copyright 2013 Dolphin Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #include #include #include #include #include "common/chunk_file.h" #include "common/msg_handler.h" #include "common/string_util.h" #include "core/core.h" #include "core/core_timing.h" int g_clock_rate_arm11 = 268123480; // is this really necessary? #define INITIAL_SLICE_LENGTH 20000 #define MAX_SLICE_LENGTH 100000000 namespace CoreTiming { struct EventType { EventType() {} EventType(TimedCallback cb, const char *n) : callback(cb), name(n) {} TimedCallback callback; const char *name; }; std::vector event_types; struct BaseEvent { s64 time; u64 userdata; int type; // Event *next; }; typedef LinkedListItem Event; Event *first; Event *tsFirst; Event *tsLast; // event pools Event *eventPool = 0; Event *eventTsPool = 0; int allocatedTsEvents = 0; // Optimization to skip MoveEvents when possible. std::atomic hasTsEvents; // Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block // as we can already reach that structure through a register. int slicelength; MEMORY_ALIGNED16(s64) globalTimer; s64 idledCycles; static std::recursive_mutex externalEventSection; // Warning: not included in save state. void(*advanceCallback)(int cyclesExecuted) = nullptr; void SetClockFrequencyMHz(int cpuMhz) { g_clock_rate_arm11 = cpuMhz * 1000000; // TODO: Rescale times of scheduled events? } int GetClockFrequencyMHz() { return g_clock_rate_arm11 / 1000000; } Event* GetNewEvent() { if (!eventPool) return new Event; Event* ev = eventPool; eventPool = ev->next; return ev; } Event* GetNewTsEvent() { allocatedTsEvents++; if (!eventTsPool) return new Event; Event* ev = eventTsPool; eventTsPool = ev->next; return ev; } void FreeEvent(Event* ev) { ev->next = eventPool; eventPool = ev; } void FreeTsEvent(Event* ev) { ev->next = eventTsPool; eventTsPool = ev; allocatedTsEvents--; } int RegisterEvent(const char *name, TimedCallback callback) { event_types.push_back(EventType(callback, name)); return (int)event_types.size() - 1; } void AntiCrashCallback(u64 userdata, int cyclesLate) { LOG_CRITICAL(Core, "Savestate broken: an unregistered event was called."); Core::Halt("invalid timing events"); } void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback) { if (event_type >= (int)event_types.size()) event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT")); event_types[event_type] = EventType(callback, name); } void UnregisterAllEvents() { if (first) PanicAlert("Cannot unregister events with events pending"); event_types.clear(); } void Init() { //currentMIPS->downcount = INITIAL_SLICE_LENGTH; //slicelength = INITIAL_SLICE_LENGTH; globalTimer = 0; idledCycles = 0; hasTsEvents = 0; } void Shutdown() { MoveEvents(); ClearPendingEvents(); UnregisterAllEvents(); while (eventPool) { Event *ev = eventPool; eventPool = ev->next; delete ev; } std::lock_guard lk(externalEventSection); while (eventTsPool) { Event *ev = eventTsPool; eventTsPool = ev->next; delete ev; } } u64 GetTicks() { LOG_ERROR(Core, "Unimplemented function!"); return 0; //return (u64)globalTimer + slicelength - currentMIPS->downcount; } u64 GetIdleTicks() { return (u64)idledCycles; } // This is to be called when outside threads, such as the graphics thread, wants to // schedule things to be executed on the main thread. void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata) { std::lock_guard lk(externalEventSection); Event *ne = GetNewTsEvent(); ne->time = GetTicks() + cyclesIntoFuture; ne->type = event_type; ne->next = 0; ne->userdata = userdata; if (!tsFirst) tsFirst = ne; if (tsLast) tsLast->next = ne; tsLast = ne; hasTsEvents.store(1, std::memory_order_release); } // Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread // in which case the event will get handled immediately, before returning. void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata) { if (false) //Core::IsCPUThread()) { std::lock_guard lk(externalEventSection); event_types[event_type].callback(userdata, 0); } else ScheduleEvent_Threadsafe(0, event_type, userdata); } void ClearPendingEvents() { while (first) { Event *e = first->next; FreeEvent(first); first = e; } } void AddEventToQueue(Event* ne) { 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; } } // This must be run ONLY from within the cpu thread // cyclesIntoFuture may be VERY inaccurate if called from anything else // than Advance void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata) { Event *ne = GetNewEvent(); ne->userdata = userdata; ne->type = event_type; ne->time = GetTicks() + cyclesIntoFuture; AddEventToQueue(ne); } // Returns cycles left in timer. s64 UnscheduleEvent(int event_type, u64 userdata) { s64 result = 0; if (!first) return result; while (first) { if (first->type == event_type && first->userdata == userdata) { result = first->time - globalTimer; Event *next = first->next; FreeEvent(first); first = next; } else { break; } } if (!first) return result; Event *prev = first; Event *ptr = prev->next; while (ptr) { if (ptr->type == event_type && ptr->userdata == userdata) { result = ptr->time - globalTimer; prev->next = ptr->next; FreeEvent(ptr); ptr = prev->next; } else { prev = ptr; ptr = ptr->next; } } return result; } s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata) { s64 result = 0; std::lock_guard lk(externalEventSection); if (!tsFirst) return result; while (tsFirst) { if (tsFirst->type == event_type && tsFirst->userdata == userdata) { result = tsFirst->time - globalTimer; Event *next = tsFirst->next; FreeTsEvent(tsFirst); tsFirst = next; } else { break; } } if (!tsFirst) { tsLast = nullptr; return result; } Event *prev = tsFirst; Event *ptr = prev->next; while (ptr) { if (ptr->type == event_type && ptr->userdata == userdata) { result = ptr->time - globalTimer; prev->next = ptr->next; if (ptr == tsLast) tsLast = prev; FreeTsEvent(ptr); ptr = prev->next; } else { prev = ptr; ptr = ptr->next; } } return result; } // Warning: not included in save state. void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted)) { advanceCallback = callback; } bool IsScheduled(int event_type) { if (!first) return false; Event *e = first; while (e) { if (e->type == event_type) return true; e = e->next; } return false; } void RemoveEvent(int event_type) { if (!first) return; while (first) { if (first->type == event_type) { Event *next = first->next; FreeEvent(first); first = next; } else { break; } } if (!first) return; Event *prev = first; Event *ptr = prev->next; while (ptr) { if (ptr->type == event_type) { prev->next = ptr->next; FreeEvent(ptr); ptr = prev->next; } else { prev = ptr; ptr = ptr->next; } } } void RemoveThreadsafeEvent(int event_type) { std::lock_guard lk(externalEventSection); if (!tsFirst) { return; } while (tsFirst) { if (tsFirst->type == event_type) { Event *next = tsFirst->next; FreeTsEvent(tsFirst); tsFirst = next; } else { break; } } if (!tsFirst) { tsLast = nullptr; return; } Event *prev = tsFirst; Event *ptr = prev->next; while (ptr) { if (ptr->type == event_type) { prev->next = ptr->next; if (ptr == tsLast) tsLast = prev; FreeTsEvent(ptr); ptr = prev->next; } else { prev = ptr; ptr = ptr->next; } } } void RemoveAllEvents(int event_type) { RemoveThreadsafeEvent(event_type); RemoveEvent(event_type); } //This raise only the events required while the fifo is processing data void ProcessFifoWaitEvents() { while (first) { if (first->time <= globalTimer) { //LOG(TIMER, "[Scheduler] %s (%lld, %lld) ", // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); Event* evt = first; first = first->next; event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time)); FreeEvent(evt); } else { break; } } } void MoveEvents() { hasTsEvents.store(0, std::memory_order_release); std::lock_guard lk(externalEventSection); // Move events from async queue into main queue while (tsFirst) { Event *next = tsFirst->next; AddEventToQueue(tsFirst); tsFirst = next; } tsLast = nullptr; // Move free events to threadsafe pool while (allocatedTsEvents > 0 && eventPool) { Event *ev = eventPool; eventPool = ev->next; ev->next = eventTsPool; eventTsPool = ev; allocatedTsEvents--; } } void Advance() { LOG_ERROR(Core, "Unimplemented function!"); //int cyclesExecuted = slicelength - currentMIPS->downcount; //globalTimer += cyclesExecuted; //currentMIPS->downcount = slicelength; //if (Common::AtomicLoadAcquire(hasTsEvents)) // MoveEvents(); //ProcessFifoWaitEvents(); //if (!first) //{ // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); // currentMIPS->downcount += 10000; //} //else //{ // slicelength = (int)(first->time - globalTimer); // if (slicelength > MAX_SLICE_LENGTH) // slicelength = MAX_SLICE_LENGTH; // currentMIPS->downcount = slicelength; //} //if (advanceCallback) // advanceCallback(cyclesExecuted); } void LogPendingEvents() { Event *ptr = first; while (ptr) { //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); ptr = ptr->next; } } void Idle(int maxIdle) { LOG_ERROR(Core, "Unimplemented function!"); //int cyclesDown = currentMIPS->downcount; //if (maxIdle != 0 && cyclesDown > maxIdle) // cyclesDown = maxIdle; //if (first && cyclesDown > 0) //{ // int cyclesExecuted = slicelength - currentMIPS->downcount; // int cyclesNextEvent = (int) (first->time - globalTimer); // if (cyclesNextEvent < cyclesExecuted + cyclesDown) // { // cyclesDown = cyclesNextEvent - cyclesExecuted; // // Now, now... no time machines, please. // if (cyclesDown < 0) // cyclesDown = 0; // } //} //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f)); //idledCycles += cyclesDown; //currentMIPS->downcount -= cyclesDown; //if (currentMIPS->downcount == 0) // currentMIPS->downcount = -1; } 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()) PanicAlert("Invalid event type"); // %i", t); const char *name = event_types[ptr->type].name; if (!name) name = "[unknown]"; text += Common::StringFromFormat("%s : %i %08x%08x\n", name, (int)ptr->time, (u32)(ptr->userdata >> 32), (u32)(ptr->userdata)); ptr = ptr->next; } return text; } void Event_DoState(PointerWrap &p, BaseEvent *ev) { p.Do(*ev); } void DoState(PointerWrap &p) { std::lock_guard lk(externalEventSection); auto s = p.Section("CoreTiming", 1); if (!s) return; int n = (int)event_types.size(); p.Do(n); // These (should) be filled in later by the modules. event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT")); p.DoLinkedList(first, (Event **)nullptr); p.DoLinkedList(tsFirst, &tsLast); p.Do(g_clock_rate_arm11); p.Do(slicelength); p.Do(globalTimer); p.Do(idledCycles); } } // namespace