CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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