summaryrefslogtreecommitdiff
path: root/src/core/core_timing.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/core_timing.cpp')
-rw-r--r--src/core/core_timing.cpp623
1 files changed, 623 insertions, 0 deletions
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
new file mode 100644
index 000000000..78bbaafe2
--- /dev/null
+++ b/src/core/core_timing.cpp
@@ -0,0 +1,623 @@
1// Copyright 2013 Dolphin Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <vector>
6#include <cstdio>
7
8#include "msg_handler.h"
9#include "std_mutex.h"
10#include "atomic.h"
11#include "core_timing.h"
12#include "core.h"
13#include "chunk_file.h"
14
15int g_clock_rate_arm11 = 268123480;
16
17// is this really necessary?
18#define INITIAL_SLICE_LENGTH 20000
19#define MAX_SLICE_LENGTH 100000000
20
21namespace CoreTiming
22{
23
24struct EventType
25{
26 EventType() {}
27
28 EventType(TimedCallback cb, const char *n)
29 : callback(cb), name(n) {}
30
31 TimedCallback callback;
32 const char *name;
33};
34
35std::vector<EventType> event_types;
36
37struct BaseEvent
38{
39 s64 time;
40 u64 userdata;
41 int type;
42 // Event *next;
43};
44
45typedef LinkedListItem<BaseEvent> Event;
46
47Event *first;
48Event *tsFirst;
49Event *tsLast;
50
51// event pools
52Event *eventPool = 0;
53Event *eventTsPool = 0;
54int allocatedTsEvents = 0;
55// Optimization to skip MoveEvents when possible.
56volatile u32 hasTsEvents = false;
57
58// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block
59// as we can already reach that structure through a register.
60int slicelength;
61
62MEMORY_ALIGNED16(s64) globalTimer;
63s64 idledCycles;
64
65static std::recursive_mutex externalEventSection;
66
67// Warning: not included in save state.
68void(*advanceCallback)(int cyclesExecuted) = NULL;
69
70void SetClockFrequencyMHz(int cpuMhz)
71{
72 g_clock_rate_arm11 = cpuMhz * 1000000;
73 // TODO: Rescale times of scheduled events?
74}
75
76int GetClockFrequencyMHz()
77{
78 return g_clock_rate_arm11 / 1000000;
79}
80
81
82Event* GetNewEvent()
83{
84 if (!eventPool)
85 return new Event;
86
87 Event* ev = eventPool;
88 eventPool = ev->next;
89 return ev;
90}
91
92Event* GetNewTsEvent()
93{
94 allocatedTsEvents++;
95
96 if (!eventTsPool)
97 return new Event;
98
99 Event* ev = eventTsPool;
100 eventTsPool = ev->next;
101 return ev;
102}
103
104void FreeEvent(Event* ev)
105{
106 ev->next = eventPool;
107 eventPool = ev;
108}
109
110void FreeTsEvent(Event* ev)
111{
112 ev->next = eventTsPool;
113 eventTsPool = ev;
114 allocatedTsEvents--;
115}
116
117int RegisterEvent(const char *name, TimedCallback callback)
118{
119 event_types.push_back(EventType(callback, name));
120 return (int)event_types.size() - 1;
121}
122
123void AntiCrashCallback(u64 userdata, int cyclesLate)
124{
125 ERROR_LOG(TIME, "Savestate broken: an unregistered event was called.");
126 Core::Halt("invalid timing events");
127}
128
129void RestoreRegisterEvent(int event_type, const char *name, TimedCallback callback)
130{
131 if (event_type >= (int)event_types.size())
132 event_types.resize(event_type + 1, EventType(AntiCrashCallback, "INVALID EVENT"));
133
134 event_types[event_type] = EventType(callback, name);
135}
136
137void UnregisterAllEvents()
138{
139 if (first)
140 PanicAlert("Cannot unregister events with events pending");
141 event_types.clear();
142}
143
144void Init()
145{
146 //currentMIPS->downcount = INITIAL_SLICE_LENGTH;
147 //slicelength = INITIAL_SLICE_LENGTH;
148 globalTimer = 0;
149 idledCycles = 0;
150 hasTsEvents = 0;
151}
152
153void Shutdown()
154{
155 MoveEvents();
156 ClearPendingEvents();
157 UnregisterAllEvents();
158
159 while (eventPool)
160 {
161 Event *ev = eventPool;
162 eventPool = ev->next;
163 delete ev;
164 }
165
166 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
167 while (eventTsPool)
168 {
169 Event *ev = eventTsPool;
170 eventTsPool = ev->next;
171 delete ev;
172 }
173}
174
175u64 GetTicks()
176{
177 ERROR_LOG(TIME, "Unimplemented function!");
178 return 0;
179 //return (u64)globalTimer + slicelength - currentMIPS->downcount;
180}
181
182u64 GetIdleTicks()
183{
184 return (u64)idledCycles;
185}
186
187
188// This is to be called when outside threads, such as the graphics thread, wants to
189// schedule things to be executed on the main thread.
190void ScheduleEvent_Threadsafe(s64 cyclesIntoFuture, int event_type, u64 userdata)
191{
192 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
193 Event *ne = GetNewTsEvent();
194 ne->time = GetTicks() + cyclesIntoFuture;
195 ne->type = event_type;
196 ne->next = 0;
197 ne->userdata = userdata;
198 if (!tsFirst)
199 tsFirst = ne;
200 if (tsLast)
201 tsLast->next = ne;
202 tsLast = ne;
203
204 Common::AtomicStoreRelease(hasTsEvents, 1);
205}
206
207// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
208// in which case the event will get handled immediately, before returning.
209void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata)
210{
211 if (false) //Core::IsCPUThread())
212 {
213 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
214 event_types[event_type].callback(userdata, 0);
215 }
216 else
217 ScheduleEvent_Threadsafe(0, event_type, userdata);
218}
219
220void ClearPendingEvents()
221{
222 while (first)
223 {
224 Event *e = first->next;
225 FreeEvent(first);
226 first = e;
227 }
228}
229
230void AddEventToQueue(Event* ne)
231{
232 Event* prev = NULL;
233 Event** pNext = &first;
234 for (;;)
235 {
236 Event*& next = *pNext;
237 if (!next || ne->time < next->time)
238 {
239 ne->next = next;
240 next = ne;
241 break;
242 }
243 prev = next;
244 pNext = &prev->next;
245 }
246}
247
248// This must be run ONLY from within the cpu thread
249// cyclesIntoFuture may be VERY inaccurate if called from anything else
250// than Advance
251void ScheduleEvent(s64 cyclesIntoFuture, int event_type, u64 userdata)
252{
253 Event *ne = GetNewEvent();
254 ne->userdata = userdata;
255 ne->type = event_type;
256 ne->time = GetTicks() + cyclesIntoFuture;
257 AddEventToQueue(ne);
258}
259
260// Returns cycles left in timer.
261s64 UnscheduleEvent(int event_type, u64 userdata)
262{
263 s64 result = 0;
264 if (!first)
265 return result;
266 while (first)
267 {
268 if (first->type == event_type && first->userdata == userdata)
269 {
270 result = first->time - globalTimer;
271
272 Event *next = first->next;
273 FreeEvent(first);
274 first = next;
275 }
276 else
277 {
278 break;
279 }
280 }
281 if (!first)
282 return result;
283 Event *prev = first;
284 Event *ptr = prev->next;
285 while (ptr)
286 {
287 if (ptr->type == event_type && ptr->userdata == userdata)
288 {
289 result = ptr->time - globalTimer;
290
291 prev->next = ptr->next;
292 FreeEvent(ptr);
293 ptr = prev->next;
294 }
295 else
296 {
297 prev = ptr;
298 ptr = ptr->next;
299 }
300 }
301
302 return result;
303}
304
305s64 UnscheduleThreadsafeEvent(int event_type, u64 userdata)
306{
307 s64 result = 0;
308 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
309 if (!tsFirst)
310 return result;
311 while (tsFirst)
312 {
313 if (tsFirst->type == event_type && tsFirst->userdata == userdata)
314 {
315 result = tsFirst->time - globalTimer;
316
317 Event *next = tsFirst->next;
318 FreeTsEvent(tsFirst);
319 tsFirst = next;
320 }
321 else
322 {
323 break;
324 }
325 }
326 if (!tsFirst)
327 {
328 tsLast = NULL;
329 return result;
330 }
331
332 Event *prev = tsFirst;
333 Event *ptr = prev->next;
334 while (ptr)
335 {
336 if (ptr->type == event_type && ptr->userdata == userdata)
337 {
338 result = ptr->time - globalTimer;
339
340 prev->next = ptr->next;
341 if (ptr == tsLast)
342 tsLast = prev;
343 FreeTsEvent(ptr);
344 ptr = prev->next;
345 }
346 else
347 {
348 prev = ptr;
349 ptr = ptr->next;
350 }
351 }
352
353 return result;
354}
355
356// Warning: not included in save state.
357void RegisterAdvanceCallback(void(*callback)(int cyclesExecuted))
358{
359 advanceCallback = callback;
360}
361
362bool IsScheduled(int event_type)
363{
364 if (!first)
365 return false;
366 Event *e = first;
367 while (e) {
368 if (e->type == event_type)
369 return true;
370 e = e->next;
371 }
372 return false;
373}
374
375void RemoveEvent(int event_type)
376{
377 if (!first)
378 return;
379 while (first)
380 {
381 if (first->type == event_type)
382 {
383 Event *next = first->next;
384 FreeEvent(first);
385 first = next;
386 }
387 else
388 {
389 break;
390 }
391 }
392 if (!first)
393 return;
394 Event *prev = first;
395 Event *ptr = prev->next;
396 while (ptr)
397 {
398 if (ptr->type == event_type)
399 {
400 prev->next = ptr->next;
401 FreeEvent(ptr);
402 ptr = prev->next;
403 }
404 else
405 {
406 prev = ptr;
407 ptr = ptr->next;
408 }
409 }
410}
411
412void RemoveThreadsafeEvent(int event_type)
413{
414 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
415 if (!tsFirst)
416 {
417 return;
418 }
419 while (tsFirst)
420 {
421 if (tsFirst->type == event_type)
422 {
423 Event *next = tsFirst->next;
424 FreeTsEvent(tsFirst);
425 tsFirst = next;
426 }
427 else
428 {
429 break;
430 }
431 }
432 if (!tsFirst)
433 {
434 tsLast = NULL;
435 return;
436 }
437 Event *prev = tsFirst;
438 Event *ptr = prev->next;
439 while (ptr)
440 {
441 if (ptr->type == event_type)
442 {
443 prev->next = ptr->next;
444 if (ptr == tsLast)
445 tsLast = prev;
446 FreeTsEvent(ptr);
447 ptr = prev->next;
448 }
449 else
450 {
451 prev = ptr;
452 ptr = ptr->next;
453 }
454 }
455}
456
457void RemoveAllEvents(int event_type)
458{
459 RemoveThreadsafeEvent(event_type);
460 RemoveEvent(event_type);
461}
462
463//This raise only the events required while the fifo is processing data
464void ProcessFifoWaitEvents()
465{
466 while (first)
467 {
468 if (first->time <= globalTimer)
469 {
470 // LOG(TIMER, "[Scheduler] %s (%lld, %lld) ",
471 // first->name ? first->name : "?", (u64)globalTimer, (u64)first->time);
472 Event* evt = first;
473 first = first->next;
474 event_types[evt->type].callback(evt->userdata, (int)(globalTimer - evt->time));
475 FreeEvent(evt);
476 }
477 else
478 {
479 break;
480 }
481 }
482}
483
484void MoveEvents()
485{
486 Common::AtomicStoreRelease(hasTsEvents, 0);
487
488 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
489 // Move events from async queue into main queue
490 while (tsFirst)
491 {
492 Event *next = tsFirst->next;
493 AddEventToQueue(tsFirst);
494 tsFirst = next;
495 }
496 tsLast = NULL;
497
498 // Move free events to threadsafe pool
499 while (allocatedTsEvents > 0 && eventPool)
500 {
501 Event *ev = eventPool;
502 eventPool = ev->next;
503 ev->next = eventTsPool;
504 eventTsPool = ev;
505 allocatedTsEvents--;
506 }
507}
508
509void Advance()
510{
511 ERROR_LOG(TIME, "Unimplemented function!");
512 //int cyclesExecuted = slicelength - currentMIPS->downcount;
513 //globalTimer += cyclesExecuted;
514 //currentMIPS->downcount = slicelength;
515
516 //if (Common::AtomicLoadAcquire(hasTsEvents))
517 // MoveEvents();
518 //ProcessFifoWaitEvents();
519
520 //if (!first)
521 //{
522 // // WARN_LOG(TIMER, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000");
523 // currentMIPS->downcount += 10000;
524 //}
525 //else
526 //{
527 // slicelength = (int)(first->time - globalTimer);
528 // if (slicelength > MAX_SLICE_LENGTH)
529 // slicelength = MAX_SLICE_LENGTH;
530 // currentMIPS->downcount = slicelength;
531 //}
532 //if (advanceCallback)
533 // advanceCallback(cyclesExecuted);
534}
535
536void LogPendingEvents()
537{
538 Event *ptr = first;
539 while (ptr)
540 {
541 //INFO_LOG(TIMER, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type);
542 ptr = ptr->next;
543 }
544}
545
546void Idle(int maxIdle)
547{
548 ERROR_LOG(TIME, "Unimplemented function!");
549 //int cyclesDown = currentMIPS->downcount;
550 //if (maxIdle != 0 && cyclesDown > maxIdle)
551 // cyclesDown = maxIdle;
552
553 //if (first && cyclesDown > 0)
554 //{
555 // int cyclesExecuted = slicelength - currentMIPS->downcount;
556 // int cyclesNextEvent = (int) (first->time - globalTimer);
557
558 // if (cyclesNextEvent < cyclesExecuted + cyclesDown)
559 // {
560 // cyclesDown = cyclesNextEvent - cyclesExecuted;
561 // // Now, now... no time machines, please.
562 // if (cyclesDown < 0)
563 // cyclesDown = 0;
564 // }
565 //}
566
567 //INFO_LOG(TIME, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(g_clock_rate_arm11 * 0.001f));
568
569 //idledCycles += cyclesDown;
570 //currentMIPS->downcount -= cyclesDown;
571 //if (currentMIPS->downcount == 0)
572 // currentMIPS->downcount = -1;
573}
574
575std::string GetScheduledEventsSummary()
576{
577 Event *ptr = first;
578 std::string text = "Scheduled events\n";
579 text.reserve(1000);
580 while (ptr)
581 {
582 unsigned int t = ptr->type;
583 if (t >= event_types.size())
584 PanicAlert("Invalid event type"); // %i", t);
585 const char *name = event_types[ptr->type].name;
586 if (!name)
587 name = "[unknown]";
588 char temp[512];
589 sprintf(temp, "%s : %i %08x%08x\n", name, (int)ptr->time, (u32)(ptr->userdata >> 32), (u32)(ptr->userdata));
590 text += temp;
591 ptr = ptr->next;
592 }
593 return text;
594}
595
596void Event_DoState(PointerWrap &p, BaseEvent *ev)
597{
598 p.Do(*ev);
599}
600
601void DoState(PointerWrap &p)
602{
603 std::lock_guard<std::recursive_mutex> lk(externalEventSection);
604
605 auto s = p.Section("CoreTiming", 1);
606 if (!s)
607 return;
608
609 int n = (int)event_types.size();
610 p.Do(n);
611 // These (should) be filled in later by the modules.
612 event_types.resize(n, EventType(AntiCrashCallback, "INVALID EVENT"));
613
614 p.DoLinkedList<BaseEvent, GetNewEvent, FreeEvent, Event_DoState>(first, (Event **)NULL);
615 p.DoLinkedList<BaseEvent, GetNewTsEvent, FreeTsEvent, Event_DoState>(tsFirst, &tsLast);
616
617 p.Do(g_clock_rate_arm11);
618 p.Do(slicelength);
619 p.Do(globalTimer);
620 p.Do(idledCycles);
621}
622
623} // namespace