| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- // Copyright 2013 Dolphin Emulator Project
- // Licensed under GPLv2
- // Refer to the license.txt file included.
- #include <vector>
- #include <cstdio>
- #include <atomic>
- #include <mutex>
- #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<EventType> event_types;
- struct BaseEvent
- {
- s64 time;
- u64 userdata;
- int type;
- // Event *next;
- };
- typedef LinkedListItem<BaseEvent> 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<u32> 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) = NULL;
- 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)
- {
- ERROR_LOG(TIME, "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<std::recursive_mutex> lk(externalEventSection);
- while (eventTsPool)
- {
- Event *ev = eventTsPool;
- eventTsPool = ev->next;
- delete ev;
- }
- }
- u64 GetTicks()
- {
- ERROR_LOG(TIME, "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<std::recursive_mutex> 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<std::recursive_mutex> 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 = NULL;
- 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<std::recursive_mutex> 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 = NULL;
- 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<std::recursive_mutex> 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 = NULL;
- 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<std::recursive_mutex> lk(externalEventSection);
- // Move events from async queue into main queue
- while (tsFirst)
- {
- Event *next = tsFirst->next;
- AddEventToQueue(tsFirst);
- tsFirst = next;
- }
- tsLast = NULL;
- // Move free events to threadsafe pool
- while (allocatedTsEvents > 0 && eventPool)
- {
- Event *ev = eventPool;
- eventPool = ev->next;
- ev->next = eventTsPool;
- eventTsPool = ev;
- allocatedTsEvents--;
- }
- }
- void Advance()
- {
- ERROR_LOG(TIME, "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)
- {
- ERROR_LOG(TIME, "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<std::recursive_mutex> 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<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL);
- p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
- p.Do(g_clock_rate_arm11);
- p.Do(slicelength);
- p.Do(globalTimer);
- p.Do(idledCycles);
- }
- } // namespace
|