summaryrefslogtreecommitdiff
path: root/src/core/core_timing.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/core_timing.h')
-rw-r--r--src/core/core_timing.h211
1 files changed, 136 insertions, 75 deletions
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index 093989d4c..59163bae1 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -4,92 +4,153 @@
4 4
5#pragma once 5#pragma once
6 6
7/**
8 * This is a system to schedule events into the emulated machine's future. Time is measured
9 * in main CPU clock cycles.
10 *
11 * To schedule an event, you first have to register its type. This is where you pass in the
12 * callback. You then schedule events using the type id you get back.
13 *
14 * The int cyclesLate that the callbacks get is how many cycles late it was.
15 * So to schedule a new event on a regular basis:
16 * inside callback:
17 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
18 */
19
20#include <chrono> 7#include <chrono>
21#include <functional> 8#include <functional>
22#include <string> 9#include <string>
10#include <unordered_map>
11#include <vector>
23#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/threadsafe_queue.h"
24 14
25namespace Core::Timing { 15namespace Core::Timing {
26 16
27struct EventType; 17/// A callback that may be scheduled for a particular core timing event.
28
29using TimedCallback = std::function<void(u64 userdata, int cycles_late)>; 18using TimedCallback = std::function<void(u64 userdata, int cycles_late)>;
30 19
31/** 20/// Contains the characteristics of a particular event.
32 * CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 21struct EventType {
33 * required to end slice -1 and start slice 0 before the first cycle of code is executed. 22 /// The event's callback function.
34 */ 23 TimedCallback callback;
35void Init(); 24 /// A pointer to the name of the event.
36void Shutdown(); 25 const std::string* name;
37 26};
38/**
39 * This should only be called from the emu thread, if you are calling it any other thread, you are
40 * doing something evil
41 */
42u64 GetTicks();
43u64 GetIdleTicks();
44void AddTicks(u64 ticks);
45
46/**
47 * Returns the event_type identifier. if name is not unique, it will assert.
48 */
49EventType* RegisterEvent(const std::string& name, TimedCallback callback);
50void UnregisterAllEvents();
51
52/**
53 * After the first Advance, the slice lengths and the downcount will be reduced whenever an event
54 * is scheduled earlier than the current values.
55 * Scheduling from a callback will not update the downcount until the Advance() completes.
56 */
57void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
58 27
59/** 28/**
60 * This is to be called when outside of hle threads, such as the graphics thread, wants to 29 * This is a system to schedule events into the emulated machine's future. Time is measured
61 * schedule things to be executed on the main thread. 30 * in main CPU clock cycles.
62 * Not that this doesn't change slice_length and thus events scheduled by this might be called 31 *
63 * with a delay of up to MAX_SLICE_LENGTH 32 * To schedule an event, you first have to register its type. This is where you pass in the
64 */ 33 * callback. You then schedule events using the type id you get back.
65void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type, u64 userdata); 34 *
66 35 * The int cyclesLate that the callbacks get is how many cycles late it was.
67void UnscheduleEvent(const EventType* event_type, u64 userdata); 36 * So to schedule a new event on a regular basis:
68void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata); 37 * inside callback:
69 38 * ScheduleEvent(periodInCycles - cyclesLate, callback, "whatever")
70/// We only permit one event of each type in the queue at a time.
71void RemoveEvent(const EventType* event_type);
72void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
73
74/** Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
75 * the previous timing slice and begins the next one, you must Advance from the previous
76 * slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
77 * Advance() is required to initialize the slice length before the first cycle of emulated
78 * instructions is executed.
79 */ 39 */
80void Advance(); 40class CoreTiming {
81void MoveEvents(); 41public:
82 42 CoreTiming();
83/// Pretend that the main CPU has executed enough cycles to reach the next event. 43 ~CoreTiming();
84void Idle(); 44
85 45 CoreTiming(const CoreTiming&) = delete;
86/// Clear all pending events. This should ONLY be done on exit. 46 CoreTiming(CoreTiming&&) = delete;
87void ClearPendingEvents(); 47
88 48 CoreTiming& operator=(const CoreTiming&) = delete;
89void ForceExceptionCheck(s64 cycles); 49 CoreTiming& operator=(CoreTiming&&) = delete;
90 50
91std::chrono::microseconds GetGlobalTimeUs(); 51 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
92 52 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
93int GetDowncount(); 53 void Initialize();
54
55 /// Tears down all timing related functionality.
56 void Shutdown();
57
58 /// Registers a core timing event with the given name and callback.
59 ///
60 /// @param name The name of the core timing event to register.
61 /// @param callback The callback to execute for the event.
62 ///
63 /// @returns An EventType instance representing the registered event.
64 ///
65 /// @pre The name of the event being registered must be unique among all
66 /// registered events.
67 ///
68 EventType* RegisterEvent(const std::string& name, TimedCallback callback);
69
70 /// Unregisters all registered events thus far.
71 void UnregisterAllEvents();
72
73 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an
74 /// event is scheduled earlier than the current values.
75 ///
76 /// Scheduling from a callback will not update the downcount until the Advance() completes.
77 void ScheduleEvent(s64 cycles_into_future, const EventType* event_type, u64 userdata = 0);
78
79 /// This is to be called when outside of hle threads, such as the graphics thread, wants to
80 /// schedule things to be executed on the main thread.
81 ///
82 /// @note This doesn't change slice_length and thus events scheduled by this might be
83 /// called with a delay of up to MAX_SLICE_LENGTH
84 void ScheduleEventThreadsafe(s64 cycles_into_future, const EventType* event_type,
85 u64 userdata = 0);
86
87 void UnscheduleEvent(const EventType* event_type, u64 userdata);
88 void UnscheduleEventThreadsafe(const EventType* event_type, u64 userdata);
89
90 /// We only permit one event of each type in the queue at a time.
91 void RemoveEvent(const EventType* event_type);
92 void RemoveNormalAndThreadsafeEvent(const EventType* event_type);
93
94 void ForceExceptionCheck(s64 cycles);
95
96 /// This should only be called from the emu thread, if you are calling it any other thread,
97 /// you are doing something evil
98 u64 GetTicks() const;
99
100 u64 GetIdleTicks() const;
101
102 void AddTicks(u64 ticks);
103
104 /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends
105 /// the previous timing slice and begins the next one, you must Advance from the previous
106 /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
107 /// Advance() is required to initialize the slice length before the first cycle of emulated
108 /// instructions is executed.
109 void Advance();
110
111 /// Pretend that the main CPU has executed enough cycles to reach the next event.
112 void Idle();
113
114 std::chrono::microseconds GetGlobalTimeUs() const;
115
116 int GetDowncount() const;
117
118private:
119 struct Event;
120
121 /// Clear all pending events. This should ONLY be done on exit.
122 void ClearPendingEvents();
123 void MoveEvents();
124
125 s64 global_timer = 0;
126 s64 idled_cycles = 0;
127 int slice_length = 0;
128 int downcount = 0;
129
130 // Are we in a function that has been called from Advance()
131 // If events are scheduled from a function that gets called from Advance(),
132 // don't change slice_length and downcount.
133 bool is_global_timer_sane = false;
134
135 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
136 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
137 // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
138 // accomodated by the standard adaptor class.
139 std::vector<Event> event_queue;
140 u64 event_fifo_id = 0;
141
142 // Stores each element separately as a linked list node so pointers to elements
143 // remain stable regardless of rehashes/resizing.
144 std::unordered_map<std::string, EventType> event_types;
145
146 // The queue for storing the events from other threads threadsafe until they will be added
147 // to the event_queue by the emu thread
148 Common::MPSCQueue<Event> ts_queue;
149
150 // The queue for unscheduling the events from other threads threadsafe
151 Common::MPSCQueue<std::pair<const EventType*, u64>> unschedule_queue;
152
153 EventType* ev_lost = nullptr;
154};
94 155
95} // namespace Core::Timing 156} // namespace Core::Timing