summaryrefslogtreecommitdiff
path: root/src/core/core_timing.cpp
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2020-02-24 22:04:12 -0400
committerGravatar Fernando Sahmkow2020-06-27 11:35:06 -0400
commite31425df3877636c098ec7426ebd2067920715cb (patch)
tree5c0fc518a4ebb8413c491b43a9fdd99450c7bd80 /src/core/core_timing.cpp
parentMerge pull request #3396 from FernandoS27/prometheus-1 (diff)
downloadyuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.gz
yuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.xz
yuzu-e31425df3877636c098ec7426ebd2067920715cb.zip
General: Recover Prometheus project from harddrive failure
This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system.
Diffstat (limited to 'src/core/core_timing.cpp')
-rw-r--r--src/core/core_timing.cpp208
1 files changed, 96 insertions, 112 deletions
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 46d4178c4..a3ce69790 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,5 +1,5 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "core/core_timing.h" 5#include "core/core_timing.h"
@@ -10,20 +10,16 @@
10#include <tuple> 10#include <tuple>
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/thread.h"
14#include "core/core_timing_util.h" 13#include "core/core_timing_util.h"
15#include "core/hardware_properties.h"
16 14
17namespace Core::Timing { 15namespace Core::Timing {
18 16
19constexpr int MAX_SLICE_LENGTH = 10000;
20
21std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 17std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
22 return std::make_shared<EventType>(std::move(callback), std::move(name)); 18 return std::make_shared<EventType>(std::move(callback), std::move(name));
23} 19}
24 20
25struct CoreTiming::Event { 21struct CoreTiming::Event {
26 s64 time; 22 u64 time;
27 u64 fifo_order; 23 u64 fifo_order;
28 u64 userdata; 24 u64 userdata;
29 std::weak_ptr<EventType> type; 25 std::weak_ptr<EventType> type;
@@ -39,51 +35,74 @@ struct CoreTiming::Event {
39 } 35 }
40}; 36};
41 37
42CoreTiming::CoreTiming() = default; 38CoreTiming::CoreTiming() {
43CoreTiming::~CoreTiming() = default; 39 clock =
40 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
41}
44 42
45void CoreTiming::Initialize() { 43CoreTiming::~CoreTiming() = default;
46 downcounts.fill(MAX_SLICE_LENGTH);
47 time_slice.fill(MAX_SLICE_LENGTH);
48 slice_length = MAX_SLICE_LENGTH;
49 global_timer = 0;
50 idled_cycles = 0;
51 current_context = 0;
52 44
53 // The time between CoreTiming being initialized and the first call to Advance() is considered 45void CoreTiming::ThreadEntry(CoreTiming& instance) {
54 // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before 46 std::string name = "yuzu:HostTiming";
55 // executing the first cycle of each slice to prepare the slice length and downcount for 47 Common::SetCurrentThreadName(name.c_str());
56 // that slice. 48 instance.on_thread_init();
57 is_global_timer_sane = true; 49 instance.ThreadLoop();
50}
58 51
52void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) {
53 on_thread_init = std::move(on_thread_init_);
59 event_fifo_id = 0; 54 event_fifo_id = 0;
60
61 const auto empty_timed_callback = [](u64, s64) {}; 55 const auto empty_timed_callback = [](u64, s64) {};
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 56 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
57 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
63} 58}
64 59
65void CoreTiming::Shutdown() { 60void CoreTiming::Shutdown() {
61 paused = true;
62 shutting_down = true;
63 event.Set();
64 timer_thread->join();
66 ClearPendingEvents(); 65 ClearPendingEvents();
66 timer_thread.reset();
67 has_started = false;
67} 68}
68 69
69void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, 70void CoreTiming::Pause(bool is_paused) {
70 u64 userdata) { 71 paused = is_paused;
71 std::lock_guard guard{inner_mutex}; 72}
72 const s64 timeout = GetTicks() + cycles_into_future;
73 73
74 // If this event needs to be scheduled before the next advance(), force one early 74void CoreTiming::SyncPause(bool is_paused) {
75 if (!is_global_timer_sane) { 75 if (is_paused == paused && paused_set == paused) {
76 ForceExceptionCheck(cycles_into_future); 76 return;
77 } 77 }
78 Pause(is_paused);
79 event.Set();
80 while (paused_set != is_paused)
81 ;
82}
83
84bool CoreTiming::IsRunning() const {
85 return !paused_set;
86}
87
88bool CoreTiming::HasPendingEvents() const {
89 return !(wait_set && event_queue.empty());
90}
91
92void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
93 u64 userdata) {
94 basic_lock.lock();
95 const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future);
78 96
79 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 97 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
80 98
81 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 99 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
100 basic_lock.unlock();
101 event.Set();
82} 102}
83 103
84void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { 104void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
85 std::lock_guard guard{inner_mutex}; 105 basic_lock.lock();
86
87 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 106 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
88 return e.type.lock().get() == event_type.get() && e.userdata == userdata; 107 return e.type.lock().get() == event_type.get() && e.userdata == userdata;
89 }); 108 });
@@ -93,23 +112,23 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u
93 event_queue.erase(itr, event_queue.end()); 112 event_queue.erase(itr, event_queue.end());
94 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 113 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
95 } 114 }
115 basic_lock.unlock();
96} 116}
97 117
98u64 CoreTiming::GetTicks() const { 118void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) {
99 u64 ticks = static_cast<u64>(global_timer); 119 ticks_count[core_index] += ticks;
100 if (!is_global_timer_sane) { 120}
101 ticks += accumulated_ticks; 121
102 } 122void CoreTiming::ResetTicks(std::size_t core_index) {
103 return ticks; 123 ticks_count[core_index] = 0;
104} 124}
105 125
106u64 CoreTiming::GetIdleTicks() const { 126u64 CoreTiming::GetCPUTicks() const {
107 return static_cast<u64>(idled_cycles); 127 return clock->GetCPUCycles();
108} 128}
109 129
110void CoreTiming::AddTicks(u64 ticks) { 130u64 CoreTiming::GetClockTicks() const {
111 accumulated_ticks += ticks; 131 return clock->GetClockCycles();
112 downcounts[current_context] -= static_cast<s64>(ticks);
113} 132}
114 133
115void CoreTiming::ClearPendingEvents() { 134void CoreTiming::ClearPendingEvents() {
@@ -117,7 +136,7 @@ void CoreTiming::ClearPendingEvents() {
117} 136}
118 137
119void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { 138void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
120 std::lock_guard guard{inner_mutex}; 139 basic_lock.lock();
121 140
122 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 141 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
123 return e.type.lock().get() == event_type.get(); 142 return e.type.lock().get() == event_type.get();
@@ -128,99 +147,64 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
128 event_queue.erase(itr, event_queue.end()); 147 event_queue.erase(itr, event_queue.end());
129 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 148 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
130 } 149 }
150 basic_lock.unlock();
131} 151}
132 152
133void CoreTiming::ForceExceptionCheck(s64 cycles) { 153std::optional<u64> CoreTiming::Advance() {
134 cycles = std::max<s64>(0, cycles); 154 advance_lock.lock();
135 if (downcounts[current_context] <= cycles) { 155 basic_lock.lock();
136 return; 156 global_timer = GetGlobalTimeNs().count();
137 }
138
139 // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
140 // here. Account for cycles already executed by adjusting the g.slice_length
141 downcounts[current_context] = static_cast<int>(cycles);
142}
143
144std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const {
145 const u64 original_context = current_context;
146 u64 next_context = (original_context + 1) % num_cpu_cores;
147 while (next_context != original_context) {
148 if (time_slice[next_context] >= needed_ticks) {
149 return {next_context};
150 } else if (time_slice[next_context] >= 0) {
151 return std::nullopt;
152 }
153 next_context = (next_context + 1) % num_cpu_cores;
154 }
155 return std::nullopt;
156}
157
158void CoreTiming::Advance() {
159 std::unique_lock<std::mutex> guard(inner_mutex);
160
161 const u64 cycles_executed = accumulated_ticks;
162 time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks);
163 global_timer += cycles_executed;
164
165 is_global_timer_sane = true;
166 157
167 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 158 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
168 Event evt = std::move(event_queue.front()); 159 Event evt = std::move(event_queue.front());
169 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 160 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
170 event_queue.pop_back(); 161 event_queue.pop_back();
171 inner_mutex.unlock(); 162 basic_lock.unlock();
172 163
173 if (auto event_type{evt.type.lock()}) { 164 if (auto event_type{evt.type.lock()}) {
174 event_type->callback(evt.userdata, global_timer - evt.time); 165 event_type->callback(evt.userdata, global_timer - evt.time);
175 } 166 }
176 167
177 inner_mutex.lock(); 168 basic_lock.lock();
178 } 169 }
179 170
180 is_global_timer_sane = false;
181
182 // Still events left (scheduled in the future)
183 if (!event_queue.empty()) { 171 if (!event_queue.empty()) {
184 const s64 needed_ticks = 172 const u64 next_time = event_queue.front().time - global_timer;
185 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 173 basic_lock.unlock();
186 const auto next_core = NextAvailableCore(needed_ticks); 174 advance_lock.unlock();
187 if (next_core) { 175 return next_time;
188 downcounts[*next_core] = needed_ticks; 176 } else {
189 } 177 basic_lock.unlock();
178 advance_lock.unlock();
179 return std::nullopt;
190 } 180 }
191
192 accumulated_ticks = 0;
193
194 downcounts[current_context] = time_slice[current_context];
195} 181}
196 182
197void CoreTiming::ResetRun() { 183void CoreTiming::ThreadLoop() {
198 downcounts.fill(MAX_SLICE_LENGTH); 184 has_started = true;
199 time_slice.fill(MAX_SLICE_LENGTH); 185 while (!shutting_down) {
200 current_context = 0; 186 while (!paused) {
201 // Still events left (scheduled in the future) 187 paused_set = false;
202 if (!event_queue.empty()) { 188 const auto next_time = Advance();
203 const s64 needed_ticks = 189 if (next_time) {
204 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 190 std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
205 downcounts[current_context] = needed_ticks; 191 event.WaitFor(next_time_ns);
192 } else {
193 wait_set = true;
194 event.Wait();
195 }
196 wait_set = false;
197 }
198 paused_set = true;
206 } 199 }
207
208 is_global_timer_sane = false;
209 accumulated_ticks = 0;
210} 200}
211 201
212void CoreTiming::Idle() { 202std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
213 accumulated_ticks += downcounts[current_context]; 203 return clock->GetTimeNS();
214 idled_cycles += downcounts[current_context];
215 downcounts[current_context] = 0;
216} 204}
217 205
218std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { 206std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
219 return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; 207 return clock->GetTimeUS();
220}
221
222s64 CoreTiming::GetDowncount() const {
223 return downcounts[current_context];
224} 208}
225 209
226} // namespace Core::Timing 210} // namespace Core::Timing