Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/CoreTiming.cpp
3185 views
1
// Copyright (c) 2012- PPSSPP Project / Dolphin Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <atomic>
19
#include <climits>
20
#include <cstdio>
21
#include <cstring>
22
#include <mutex>
23
#include <set>
24
#include <vector>
25
26
#include "Common/Profiler/Profiler.h"
27
28
#include "Common/Serialize/Serializer.h"
29
#include "Common/Serialize/SerializeList.h"
30
#include "Core/CoreTiming.h"
31
#include "Core/Core.h"
32
#include "Core/Config.h"
33
#include "Core/HLE/sceKernelThread.h"
34
#include "Core/MIPS/MIPS.h"
35
36
static const int initialHz = 222000000;
37
int CPU_HZ = 222000000;
38
39
// is this really necessary?
40
#define INITIAL_SLICE_LENGTH 20000
41
#define MAX_SLICE_LENGTH 100000000
42
43
namespace CoreTiming {
44
45
static std::vector<EventType> event_types;
46
// Only used during restore.
47
static std::set<int> usedEventTypes;
48
static std::set<int> restoredEventTypes;
49
static int nextEventTypeRestoreId = -1;
50
51
Event *first;
52
Event *eventPool = 0;
53
54
// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
55
// as we can already reach that structure through a register.
56
int slicelength;
57
58
alignas(16) s64 globalTimer;
59
s64 idledCycles;
60
s64 lastGlobalTimeTicks;
61
s64 lastGlobalTimeUs;
62
63
std::vector<MHzChangeCallback> mhzChangeCallbacks;
64
65
void FireMhzChange() {
66
for (MHzChangeCallback cb : mhzChangeCallbacks) {
67
cb();
68
}
69
}
70
71
void SetClockFrequencyHz(int cpuHz) {
72
if (cpuHz <= 0) {
73
// Paranoid check, protecting against division by zero and similar nonsense.
74
return;
75
}
76
77
// When the mhz changes, we keep track of what "time" it was before hand.
78
// This way, time always moves forward, even if mhz is changed.
79
lastGlobalTimeUs = GetGlobalTimeUs();
80
lastGlobalTimeTicks = GetTicks();
81
82
CPU_HZ = cpuHz;
83
// TODO: Rescale times of scheduled events?
84
85
FireMhzChange();
86
}
87
88
int GetClockFrequencyHz() {
89
return CPU_HZ;
90
}
91
92
u64 GetGlobalTimeUsScaled() {
93
return GetGlobalTimeUs();
94
}
95
96
u64 GetGlobalTimeUs() {
97
s64 ticksSinceLast = GetTicks() - lastGlobalTimeTicks;
98
int freq = GetClockFrequencyHz();
99
s64 usSinceLast = ticksSinceLast * 1000000 / freq;
100
if (ticksSinceLast > UINT_MAX) {
101
// Adjust the calculated value to avoid overflow errors.
102
lastGlobalTimeUs += usSinceLast;
103
lastGlobalTimeTicks = GetTicks();
104
usSinceLast = 0;
105
}
106
return lastGlobalTimeUs + usSinceLast;
107
}
108
109
const Event *GetFirstEvent() {
110
return first;
111
}
112
113
const std::vector<EventType> &GetEventTypes() {
114
return event_types;
115
}
116
117
Event* GetNewEvent()
118
{
119
if(!eventPool)
120
return new Event;
121
122
Event* ev = eventPool;
123
eventPool = ev->next;
124
return ev;
125
}
126
127
void FreeEvent(Event* ev)
128
{
129
ev->next = eventPool;
130
eventPool = ev;
131
}
132
133
int RegisterEvent(const char *name, TimedCallback callback) {
134
for (const auto &ty : event_types) {
135
if (!strcmp(ty.name, name)) {
136
_assert_msg_(false, "Event type %s already registered", name);
137
// Try to make sure it doesn't work so we notice for sure.
138
return -1;
139
}
140
}
141
142
int id = (int)event_types.size();
143
event_types.push_back(EventType{ callback, name });
144
usedEventTypes.insert(id);
145
return id;
146
}
147
148
void AntiCrashCallback(u64 userdata, int cyclesLate) {
149
ERROR_LOG(Log::SaveState, "Savestate broken: an unregistered event was called.");
150
Core_Break(BreakReason::SavestateCrash, 0);
151
}
152
153
void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callback) {
154
// Some old states have a duplicate restore, do our best to fix...
155
if (restoredEventTypes.count(event_type) != 0)
156
event_type = -1;
157
if (event_type == -1)
158
event_type = nextEventTypeRestoreId++;
159
if (event_type >= (int)event_types.size()) {
160
// Give it any unused event id starting from the end.
161
// Older save states with messed up ids have gaps near the end.
162
for (int i = (int)event_types.size() - 1; i >= 0; --i) {
163
if (usedEventTypes.count(i) == 0) {
164
event_type = i;
165
break;
166
}
167
}
168
}
169
_assert_msg_(event_type >= 0 && event_type < (int)event_types.size(), "Invalid event type %d", event_type);
170
event_types[event_type] = EventType{ callback, name };
171
usedEventTypes.insert(event_type);
172
restoredEventTypes.insert(event_type);
173
}
174
175
void UnregisterAllEvents() {
176
_dbg_assert_msg_(first == nullptr, "Unregistering events with events pending - this isn't good.");
177
event_types.clear();
178
usedEventTypes.clear();
179
restoredEventTypes.clear();
180
}
181
182
void Init()
183
{
184
currentMIPS->downcount = INITIAL_SLICE_LENGTH;
185
slicelength = INITIAL_SLICE_LENGTH;
186
globalTimer = 0;
187
idledCycles = 0;
188
lastGlobalTimeTicks = 0;
189
lastGlobalTimeUs = 0;
190
mhzChangeCallbacks.clear();
191
CPU_HZ = initialHz;
192
}
193
194
void Shutdown()
195
{
196
ClearPendingEvents();
197
UnregisterAllEvents();
198
199
while (eventPool) {
200
Event *ev = eventPool;
201
eventPool = ev->next;
202
delete ev;
203
}
204
}
205
206
u64 GetTicks()
207
{
208
if (currentMIPS) {
209
return (u64)globalTimer + slicelength - currentMIPS->downcount;
210
} else {
211
// Reporting can actually end up here during weird task switching sequences on Android
212
return false;
213
}
214
}
215
216
u64 GetIdleTicks()
217
{
218
return (u64)idledCycles;
219
}
220
221
void ClearPendingEvents()
222
{
223
while (first)
224
{
225
Event *e = first->next;
226
FreeEvent(first);
227
first = e;
228
}
229
}
230
231
void AddEventToQueue(Event* ne)
232
{
233
Event* prev = NULL;
234
Event** pNext = &first;
235
for(;;)
236
{
237
Event*& next = *pNext;
238
if(!next || ne->time < next->time)
239
{
240
ne->next = next;
241
next = ne;
242
break;
243
}
244
prev = next;
245
pNext = &prev->next;
246
}
247
}
248
249
// This must be run ONLY from within the cpu thread
250
// cyclesIntoFuture may be VERY inaccurate if called from anything else
251
// than Advance
252
void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
253
{
254
Event *ne = GetNewEvent();
255
ne->userdata = userdata;
256
ne->type = event_type;
257
ne->time = GetTicks() + cyclesIntoFuture;
258
AddEventToQueue(ne);
259
}
260
261
// Returns cycles left in timer.
262
s64 UnscheduleEvent(int event_type, u64 userdata)
263
{
264
s64 result = 0;
265
if (!first)
266
return result;
267
while(first)
268
{
269
if (first->type == event_type && first->userdata == userdata)
270
{
271
result = first->time - GetTicks();
272
273
Event *next = first->next;
274
FreeEvent(first);
275
first = next;
276
}
277
else
278
{
279
break;
280
}
281
}
282
if (!first)
283
return result;
284
Event *prev = first;
285
Event *ptr = prev->next;
286
while (ptr)
287
{
288
if (ptr->type == event_type && ptr->userdata == userdata)
289
{
290
result = ptr->time - GetTicks();
291
292
prev->next = ptr->next;
293
FreeEvent(ptr);
294
ptr = prev->next;
295
}
296
else
297
{
298
prev = ptr;
299
ptr = ptr->next;
300
}
301
}
302
303
return result;
304
}
305
306
void RegisterMHzChangeCallback(MHzChangeCallback callback) {
307
mhzChangeCallbacks.push_back(callback);
308
}
309
310
bool IsScheduled(int event_type)
311
{
312
if (!first)
313
return false;
314
Event *e = first;
315
while (e) {
316
if (e->type == event_type)
317
return true;
318
e = e->next;
319
}
320
return false;
321
}
322
323
void RemoveEvent(int event_type)
324
{
325
if (!first)
326
return;
327
while(first)
328
{
329
if (first->type == event_type)
330
{
331
Event *next = first->next;
332
FreeEvent(first);
333
first = next;
334
}
335
else
336
{
337
break;
338
}
339
}
340
if (!first)
341
return;
342
Event *prev = first;
343
Event *ptr = prev->next;
344
while (ptr)
345
{
346
if (ptr->type == event_type)
347
{
348
prev->next = ptr->next;
349
FreeEvent(ptr);
350
ptr = prev->next;
351
}
352
else
353
{
354
prev = ptr;
355
ptr = ptr->next;
356
}
357
}
358
}
359
360
void ProcessEvents() {
361
while (first) {
362
if (first->time <= (s64)GetTicks()) {
363
// INFO_LOG(Log::CPU, "%s (%lld, %lld) ", first->name ? first->name : "?", (u64)GetTicks(), (u64)first->time);
364
Event *evt = first;
365
first = first->next;
366
if (evt->type >= 0 && evt->type < event_types.size()) {
367
event_types[evt->type].callback(evt->userdata, (int)(GetTicks() - evt->time));
368
} else {
369
_dbg_assert_msg_(false, "Bad event type %d", evt->type);
370
}
371
FreeEvent(evt);
372
} else {
373
// Caught up to the current time.
374
break;
375
}
376
}
377
}
378
379
void ForceCheck()
380
{
381
int cyclesExecuted = slicelength - currentMIPS->downcount;
382
globalTimer += cyclesExecuted;
383
// This will cause us to check for new events immediately.
384
currentMIPS->downcount = -1;
385
// But let's not eat a bunch more time in Advance() because of this.
386
slicelength = -1;
387
388
#ifdef _DEBUG
389
_dbg_assert_msg_( cyclesExecuted >= 0, "Shouldn't have a negative cyclesExecuted");
390
#endif
391
}
392
393
void Advance() {
394
PROFILE_THIS_SCOPE("advance");
395
int cyclesExecuted = slicelength - currentMIPS->downcount;
396
globalTimer += cyclesExecuted;
397
currentMIPS->downcount = slicelength;
398
399
ProcessEvents();
400
401
if (!first) {
402
// This should never happen in PPSSPP.
403
if (slicelength < 10000) {
404
slicelength += 10000;
405
currentMIPS->downcount += 10000;
406
}
407
} else {
408
// Note that events can eat cycles as well.
409
int target = (int)(first->time - globalTimer);
410
if (target > MAX_SLICE_LENGTH)
411
target = MAX_SLICE_LENGTH;
412
413
const int diff = target - slicelength;
414
slicelength += diff;
415
currentMIPS->downcount += diff;
416
}
417
}
418
419
void LogPendingEvents() {
420
Event *ptr = first;
421
while (ptr) {
422
//INFO_LOG(Log::CPU, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type);
423
ptr = ptr->next;
424
}
425
}
426
427
void Idle(int maxIdle) {
428
int cyclesDown = currentMIPS->downcount;
429
if (maxIdle != 0 && cyclesDown > maxIdle)
430
cyclesDown = maxIdle;
431
432
if (first && cyclesDown > 0) {
433
int cyclesExecuted = slicelength - currentMIPS->downcount;
434
int cyclesNextEvent = (int) (first->time - globalTimer);
435
436
if (cyclesNextEvent < cyclesExecuted + cyclesDown)
437
cyclesDown = cyclesNextEvent - cyclesExecuted;
438
}
439
440
// Now, now... no time machines, please.
441
if (cyclesDown < 0)
442
cyclesDown = 0;
443
444
// VERBOSE_LOG(Log::CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f));
445
446
idledCycles += cyclesDown;
447
currentMIPS->downcount -= cyclesDown;
448
if (currentMIPS->downcount == 0)
449
currentMIPS->downcount = -1;
450
}
451
452
std::string GetScheduledEventsSummary() {
453
Event *ptr = first;
454
std::string text = "Scheduled events\n";
455
text.reserve(1000);
456
while (ptr) {
457
unsigned int t = ptr->type;
458
if (t >= event_types.size()) {
459
_dbg_assert_msg_(false, "Invalid event type %d", t);
460
ptr = ptr->next;
461
continue;
462
}
463
const char *name = event_types[t].name;
464
if (!name)
465
name = "[unknown]";
466
char temp[512];
467
snprintf(temp, sizeof(temp), "%s : %i %08x%08x\n", name, (int)ptr->time, (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));
468
text += temp;
469
ptr = ptr->next;
470
}
471
return text;
472
}
473
474
void Event_DoState(PointerWrap &p, BaseEvent *ev) {
475
// There may be padding, so do each one individually.
476
Do(p, ev->time);
477
Do(p, ev->userdata);
478
Do(p, ev->type);
479
usedEventTypes.insert(ev->type);
480
}
481
482
void Event_DoStateOld(PointerWrap &p, BaseEvent *ev) {
483
Do(p, *ev);
484
usedEventTypes.insert(ev->type);
485
}
486
487
void DoState(PointerWrap &p) {
488
auto s = p.Section("CoreTiming", 1, 3);
489
if (!s)
490
return;
491
492
int n = (int)event_types.size();
493
int current = n;
494
Do(p, n);
495
if (n > current) {
496
WARN_LOG(Log::SaveState, "Savestate failure: more events than current (can't ever remove an event)");
497
p.SetError(p.ERROR_FAILURE);
498
return;
499
}
500
501
// These (should) be filled in later by the modules.
502
for (int i = 0; i < current; ++i) {
503
event_types[i].callback = AntiCrashCallback;
504
event_types[i].name = "INVALID EVENT";
505
}
506
nextEventTypeRestoreId = n - 1;
507
usedEventTypes.clear();
508
restoredEventTypes.clear();
509
510
if (s >= 3) {
511
DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(p, first, (Event **)nullptr);
512
// This is here because we previously stored a second queue of "threadsafe" events. Gone now. Remove in the next section version upgrade.
513
DoIgnoreUnusedLinkedList(p);
514
} else {
515
DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoStateOld>(p, first, (Event **)nullptr);
516
DoIgnoreUnusedLinkedList(p);
517
}
518
519
Do(p, CPU_HZ);
520
Do(p, slicelength);
521
Do(p, globalTimer);
522
Do(p, idledCycles);
523
524
if (s >= 2) {
525
Do(p, lastGlobalTimeTicks);
526
Do(p, lastGlobalTimeUs);
527
} else {
528
lastGlobalTimeTicks = 0;
529
lastGlobalTimeUs = 0;
530
}
531
532
FireMhzChange();
533
}
534
535
} // namespace
536
537