CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/CoreTiming.cpp
Views: 1401
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <atomic>18#include <climits>19#include <cstdio>20#include <cstring>21#include <mutex>22#include <set>23#include <vector>2425#include "Common/Profiler/Profiler.h"2627#include "Common/Serialize/Serializer.h"28#include "Common/Serialize/SerializeList.h"29#include "Core/CoreTiming.h"30#include "Core/Core.h"31#include "Core/Config.h"32#include "Core/HLE/sceKernelThread.h"33#include "Core/MIPS/MIPS.h"3435static const int initialHz = 222000000;36int CPU_HZ = 222000000;3738// is this really necessary?39#define INITIAL_SLICE_LENGTH 2000040#define MAX_SLICE_LENGTH 1000000004142namespace CoreTiming43{4445struct EventType {46TimedCallback callback;47const char *name;48};4950static std::vector<EventType> event_types;51// Only used during restore.52static std::set<int> usedEventTypes;53static std::set<int> restoredEventTypes;54static int nextEventTypeRestoreId = -1;5556struct BaseEvent {57s64 time;58u64 userdata;59int type;60};6162typedef LinkedListItem<BaseEvent> Event;6364Event *first;65Event *eventPool = 0;6667// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block68// as we can already reach that structure through a register.69int slicelength;7071alignas(16) s64 globalTimer;72s64 idledCycles;73s64 lastGlobalTimeTicks;74s64 lastGlobalTimeUs;7576std::vector<MHzChangeCallback> mhzChangeCallbacks;7778void FireMhzChange() {79for (MHzChangeCallback cb : mhzChangeCallbacks) {80cb();81}82}8384void SetClockFrequencyHz(int cpuHz) {85if (cpuHz <= 0) {86// Paranoid check, protecting against division by zero and similar nonsense.87return;88}8990// When the mhz changes, we keep track of what "time" it was before hand.91// This way, time always moves forward, even if mhz is changed.92lastGlobalTimeUs = GetGlobalTimeUs();93lastGlobalTimeTicks = GetTicks();9495CPU_HZ = cpuHz;96// TODO: Rescale times of scheduled events?9798FireMhzChange();99}100101int GetClockFrequencyHz() {102return CPU_HZ;103}104105u64 GetGlobalTimeUsScaled() {106return GetGlobalTimeUs();107}108109u64 GetGlobalTimeUs() {110s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks;111int freq = GetClockFrequencyHz();112s64 usSinceLast = ticksSinceLast * 1000000 / freq;113if (ticksSinceLast > UINT_MAX) {114// Adjust the calculated value to avoid overflow errors.115lastGlobalTimeUs += usSinceLast;116lastGlobalTimeTicks = GetTicks();117usSinceLast = 0;118}119return lastGlobalTimeUs + usSinceLast;120}121122Event* GetNewEvent()123{124if(!eventPool)125return new Event;126127Event* ev = eventPool;128eventPool = ev->next;129return ev;130}131132void FreeEvent(Event* ev)133{134ev->next = eventPool;135eventPool = ev;136}137138int RegisterEvent(const char *name, TimedCallback callback) {139for (const auto &ty : event_types) {140if (!strcmp(ty.name, name)) {141_assert_msg_(false, "Event type %s already registered", name);142// Try to make sure it doesn't work so we notice for sure.143return -1;144}145}146147int id = (int)event_types.size();148event_types.push_back(EventType{ callback, name });149usedEventTypes.insert(id);150return id;151}152153void AntiCrashCallback(u64 userdata, int cyclesLate) {154ERROR_LOG(Log::SaveState, "Savestate broken: an unregistered event was called.");155Core_EnableStepping(true, "savestate.crash", 0);156}157158void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback) {159// Some old states have a duplicate restore, do our best to fix...160if (restoredEventTypes.count(event_type) != 0)161event_type = -1;162if (event_type == -1)163event_type = nextEventTypeRestoreId++;164if (event_type >= (int)event_types.size()) {165// Give it any unused event id starting from the end.166// Older save states with messed up ids have gaps near the end.167for (int i = (int)event_types.size() - 1; i >= 0; --i) {168if (usedEventTypes.count(i) == 0) {169event_type = i;170break;171}172}173}174_assert_msg_(event_type >= 0 && event_type < (int)event_types.size(), "Invalid event type %d", event_type);175event_types[event_type] = EventType{ callback, name };176usedEventTypes.insert(event_type);177restoredEventTypes.insert(event_type);178}179180void UnregisterAllEvents() {181_dbg_assert_msg_(first == nullptr, "Unregistering events with events pending - this isn't good.");182event_types.clear();183usedEventTypes.clear();184restoredEventTypes.clear();185}186187void Init()188{189currentMIPS->downcount = INITIAL_SLICE_LENGTH;190slicelength = INITIAL_SLICE_LENGTH;191globalTimer = 0;192idledCycles = 0;193lastGlobalTimeTicks = 0;194lastGlobalTimeUs = 0;195mhzChangeCallbacks.clear();196CPU_HZ = initialHz;197}198199void Shutdown()200{201ClearPendingEvents();202UnregisterAllEvents();203204while (eventPool) {205Event *ev = eventPool;206eventPool = ev->next;207delete ev;208}209}210211u64 GetTicks()212{213if (currentMIPS) {214return (u64)globalTimer + slicelength - currentMIPS->downcount;215} else {216// Reporting can actually end up here during weird task switching sequences on Android217return false;218}219}220221u64 GetIdleTicks()222{223return (u64)idledCycles;224}225226void ClearPendingEvents()227{228while (first)229{230Event *e = first->next;231FreeEvent(first);232first = e;233}234}235236void AddEventToQueue(Event* ne)237{238Event* prev = NULL;239Event** pNext = &first;240for(;;)241{242Event*& next = *pNext;243if(!next || ne->time < next->time)244{245ne->next = next;246next = ne;247break;248}249prev = next;250pNext = &prev->next;251}252}253254// This must be run ONLY from within the cpu thread255// cyclesIntoFuture may be VERY inaccurate if called from anything else256// than Advance257void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)258{259Event *ne = GetNewEvent();260ne->userdata = userdata;261ne->type = event_type;262ne->time = GetTicks() + cyclesIntoFuture;263AddEventToQueue(ne);264}265266// Returns cycles left in timer.267s64 UnscheduleEvent(int event_type, u64 userdata)268{269s64 result = 0;270if (!first)271return result;272while(first)273{274if (first->type == event_type && first->userdata == userdata)275{276result = first->time - GetTicks();277278Event *next = first->next;279FreeEvent(first);280first = next;281}282else283{284break;285}286}287if (!first)288return result;289Event *prev = first;290Event *ptr = prev->next;291while (ptr)292{293if (ptr->type == event_type && ptr->userdata == userdata)294{295result = ptr->time - GetTicks();296297prev->next = ptr->next;298FreeEvent(ptr);299ptr = prev->next;300}301else302{303prev = ptr;304ptr = ptr->next;305}306}307308return result;309}310311void RegisterMHzChangeCallback(MHzChangeCallback callback) {312mhzChangeCallbacks.push_back(callback);313}314315bool IsScheduled(int event_type)316{317if (!first)318return false;319Event *e = first;320while (e) {321if (e->type == event_type)322return true;323e = e->next;324}325return false;326}327328void RemoveEvent(int event_type)329{330if (!first)331return;332while(first)333{334if (first->type == event_type)335{336Event *next = first->next;337FreeEvent(first);338first = next;339}340else341{342break;343}344}345if (!first)346return;347Event *prev = first;348Event *ptr = prev->next;349while (ptr)350{351if (ptr->type == event_type)352{353prev->next = ptr->next;354FreeEvent(ptr);355ptr = prev->next;356}357else358{359prev = ptr;360ptr = ptr->next;361}362}363}364365void ProcessEvents() {366while (first) {367if (first->time <= (s64)GetTicks()) {368// INFO_LOG(Log::CPU, "%s (%lld, %lld) ", first->name ? first->name : "?", (u64)GetTicks(), (u64)first->time);369Event *evt = first;370first = first->next;371if (evt->type >= 0 && evt->type < event_types.size()) {372event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time));373} else {374_dbg_assert_msg_(false, "Bad event type %d", evt->type);375}376FreeEvent(evt);377} else {378// Caught up to the current time.379break;380}381}382}383384void ForceCheck()385{386int cyclesExecuted = slicelength - currentMIPS->downcount;387globalTimer += cyclesExecuted;388// This will cause us to check for new events immediately.389currentMIPS->downcount = -1;390// But let's not eat a bunch more time in Advance() because of this.391slicelength = -1;392393#ifdef _DEBUG394_dbg_assert_msg_( cyclesExecuted >= 0, "Shouldn't have a negative cyclesExecuted");395#endif396}397398void Advance() {399PROFILE_THIS_SCOPE("advance");400int cyclesExecuted = slicelength - currentMIPS->downcount;401globalTimer += cyclesExecuted;402currentMIPS->downcount = slicelength;403404ProcessEvents();405406if (!first) {407// This should never happen in PPSSPP.408if (slicelength < 10000) {409slicelength += 10000;410currentMIPS->downcount += 10000;411}412} else {413// Note that events can eat cycles as well.414int target = (int)(first->time - globalTimer);415if (target > MAX_SLICE_LENGTH)416target = MAX_SLICE_LENGTH;417418const int diff = target - slicelength;419slicelength += diff;420currentMIPS->downcount += diff;421}422}423424void LogPendingEvents() {425Event *ptr = first;426while (ptr) {427//INFO_LOG(Log::CPU, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type);428ptr = ptr->next;429}430}431432void Idle(int maxIdle) {433int cyclesDown = currentMIPS->downcount;434if (maxIdle != 0 && cyclesDown > maxIdle)435cyclesDown = maxIdle;436437if (first && cyclesDown > 0) {438int cyclesExecuted = slicelength - currentMIPS->downcount;439int cyclesNextEvent = (int) (first->time - globalTimer);440441if (cyclesNextEvent < cyclesExecuted + cyclesDown)442cyclesDown = cyclesNextEvent - cyclesExecuted;443}444445// Now, now... no time machines, please.446if (cyclesDown < 0)447cyclesDown = 0;448449// VERBOSE_LOG(Log::CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f));450451idledCycles += cyclesDown;452currentMIPS->downcount -= cyclesDown;453if (currentMIPS->downcount == 0)454currentMIPS->downcount = -1;455}456457std::string GetScheduledEventsSummary() {458Event *ptr = first;459std::string text = "Scheduled events\n";460text.reserve(1000);461while (ptr) {462unsigned int t = ptr->type;463if (t >= event_types.size()) {464_dbg_assert_msg_(false, "Invalid event type %d", t);465ptr = ptr->next;466continue;467}468const char *name = event_types[t].name;469if (!name)470name = "[unknown]";471char temp[512];472snprintf(temp, sizeof(temp), "%s : %i %08x%08x\n", name, (int)ptr->time, (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));473text += temp;474ptr = ptr->next;475}476return text;477}478479void Event_DoState(PointerWrap &p, BaseEvent *ev) {480// There may be padding, so do each one individually.481Do(p, ev->time);482Do(p, ev->userdata);483Do(p, ev->type);484usedEventTypes.insert(ev->type);485}486487void Event_DoStateOld(PointerWrap &p, BaseEvent *ev) {488Do(p, *ev);489usedEventTypes.insert(ev->type);490}491492void DoState(PointerWrap &p) {493auto s = p.Section("CoreTiming", 1, 3);494if (!s)495return;496497int n = (int)event_types.size();498int current = n;499Do(p, n);500if (n > current) {501WARN_LOG(Log::SaveState, "Savestate failure: more events than current (can't ever remove an event)");502p.SetError(p.ERROR_FAILURE);503return;504}505506// These (should) be filled in later by the modules.507for (int i = 0; i < current; ++i) {508event_types[i].callback = AntiCrashCallback;509event_types[i].name = "INVALID EVENT";510}511nextEventTypeRestoreId = n - 1;512usedEventTypes.clear();513restoredEventTypes.clear();514515if (s >= 3) {516DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(p, first, (Event **)nullptr);517// This is here because we previously stored a second queue of "threadsafe" events. Gone now. Remove in the next section version upgrade.518DoIgnoreUnusedLinkedList(p);519} else {520DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoStateOld>(p, first, (Event **)nullptr);521DoIgnoreUnusedLinkedList(p);522}523524Do(p, CPU_HZ);525Do(p, slicelength);526Do(p, globalTimer);527Do(p, idledCycles);528529if (s >= 2) {530Do(p, lastGlobalTimeTicks);531Do(p, lastGlobalTimeUs);532} else {533lastGlobalTimeTicks = 0;534lastGlobalTimeUs = 0;535}536537FireMhzChange();538}539540} // namespace541542543