summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Lioncash2018-11-22 01:27:23 -0500
committerGravatar Lioncash2018-11-22 04:28:19 -0500
commit232d95b56e4d634e21bc6d496cff6b3dae32dc14 (patch)
tree388c419ff948b26298c057991a049c271f7b31c7
parentMerge pull request #1737 from FernandoS27/layer-copy (diff)
downloadyuzu-232d95b56e4d634e21bc6d496cff6b3dae32dc14.tar.gz
yuzu-232d95b56e4d634e21bc6d496cff6b3dae32dc14.tar.xz
yuzu-232d95b56e4d634e21bc6d496cff6b3dae32dc14.zip
core: Relocate CPU core management to its own class
Keeps the CPU-specific behavior from being spread throughout the main System class. This will also act as the home to contain member functions that perform operations on all cores. The reason for this being that the following pattern is sort of prevalent throughout sections of the codebase: If clearing the instruction cache for all 4 cores is necessary: Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); This is kind of... well, silly to copy around whenever it's needed. especially when it can be reduced down to a single line. This change also puts the basics in place to begin "ungrafting" all of the forwarding member functions from the System class that are used to access CPU state or invoke CPU-specific behavior. As such, this change itself makes no changes to the direct external interface of System. This will be covered by another changeset.
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/core.cpp119
-rw-r--r--src/core/cpu_core_manager.cpp142
-rw-r--r--src/core/cpu_core_manager.h59
4 files changed, 225 insertions, 97 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a355eaca6..3d2e0767a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -12,6 +12,8 @@ add_library(core STATIC
12 core_timing.h 12 core_timing.h
13 core_timing_util.cpp 13 core_timing_util.cpp
14 core_timing_util.h 14 core_timing_util.h
15 cpu_core_manager.cpp
16 cpu_core_manager.h
15 crypto/aes_util.cpp 17 crypto/aes_util.cpp
16 crypto/aes_util.h 18 crypto/aes_util.h
17 crypto/encryption_layer.cpp 19 crypto/encryption_layer.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6c72fdf4a..795fabc65 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,6 +14,7 @@
14#include "core/core.h" 14#include "core/core.h"
15#include "core/core_cpu.h" 15#include "core/core_cpu.h"
16#include "core/core_timing.h" 16#include "core/core_timing.h"
17#include "core/cpu_core_manager.h"
17#include "core/file_sys/mode.h" 18#include "core/file_sys/mode.h"
18#include "core/file_sys/vfs_concat.h" 19#include "core/file_sys/vfs_concat.h"
19#include "core/file_sys/vfs_real.h" 20#include "core/file_sys/vfs_real.h"
@@ -28,7 +29,6 @@
28#include "core/hle/service/sm/sm.h" 29#include "core/hle/service/sm/sm.h"
29#include "core/loader/loader.h" 30#include "core/loader/loader.h"
30#include "core/perf_stats.h" 31#include "core/perf_stats.h"
31#include "core/settings.h"
32#include "core/telemetry_session.h" 32#include "core/telemetry_session.h"
33#include "frontend/applets/software_keyboard.h" 33#include "frontend/applets/software_keyboard.h"
34#include "video_core/debug_utils/debug_utils.h" 34#include "video_core/debug_utils/debug_utils.h"
@@ -71,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
71 71
72 return vfs->OpenFile(path, FileSys::Mode::Read); 72 return vfs->OpenFile(path, FileSys::Mode::Read);
73} 73}
74
75/// Runs a CPU core while the system is powered on
76void RunCpuCore(Cpu& cpu_state) {
77 while (Core::System::GetInstance().IsPoweredOn()) {
78 cpu_state.RunLoop(true);
79 }
80}
81} // Anonymous namespace 74} // Anonymous namespace
82 75
83struct System::Impl { 76struct System::Impl {
84 Cpu& CurrentCpuCore() { 77 Cpu& CurrentCpuCore() {
85 if (Settings::values.use_multi_core) { 78 return cpu_core_manager.GetCurrentCore();
86 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
87 ASSERT(search != thread_to_cpu.end());
88 ASSERT(search->second);
89 return *search->second;
90 }
91
92 // Otherwise, use single-threaded mode active_core variable
93 return *cpu_cores[active_core];
94 } 79 }
95 80
96 ResultStatus RunLoop(bool tight_loop) { 81 ResultStatus RunLoop(bool tight_loop) {
97 status = ResultStatus::Success; 82 status = ResultStatus::Success;
98 83
99 // Update thread_to_cpu in case Core 0 is run from a different host thread 84 cpu_core_manager.RunLoop(tight_loop);
100 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
101
102 if (GDBStub::IsServerEnabled()) {
103 GDBStub::HandlePacket();
104
105 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
106 // execute. Otherwise, get out of the loop function.
107 if (GDBStub::GetCpuHaltFlag()) {
108 if (GDBStub::GetCpuStepFlag()) {
109 tight_loop = false;
110 } else {
111 return ResultStatus::Success;
112 }
113 }
114 }
115
116 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
117 cpu_cores[active_core]->RunLoop(tight_loop);
118 if (Settings::values.use_multi_core) {
119 // Cores 1-3 are run on other threads in this mode
120 break;
121 }
122 }
123
124 if (GDBStub::IsServerEnabled()) {
125 GDBStub::SetCpuStepFlag(false);
126 }
127 85
128 return status; 86 return status;
129 } 87 }
130 88
131 ResultStatus Init(Frontend::EmuWindow& emu_window) { 89 ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
132 LOG_DEBUG(HW_Memory, "initialized OK"); 90 LOG_DEBUG(HW_Memory, "initialized OK");
133 91
134 CoreTiming::Init(); 92 CoreTiming::Init();
@@ -145,12 +103,6 @@ struct System::Impl {
145 auto main_process = Kernel::Process::Create(kernel, "main"); 103 auto main_process = Kernel::Process::Create(kernel, "main");
146 kernel.MakeCurrentProcess(main_process.get()); 104 kernel.MakeCurrentProcess(main_process.get());
147 105
148 cpu_barrier = std::make_unique<CpuBarrier>();
149 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
150 for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
151 cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
152 }
153
154 telemetry_session = std::make_unique<Core::TelemetrySession>(); 106 telemetry_session = std::make_unique<Core::TelemetrySession>();
155 service_manager = std::make_shared<Service::SM::ServiceManager>(); 107 service_manager = std::make_shared<Service::SM::ServiceManager>();
156 108
@@ -164,17 +116,8 @@ struct System::Impl {
164 116
165 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 117 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
166 118
167 // Create threads for CPU cores 1-3, and build thread_to_cpu map 119 cpu_core_manager.Initialize(system);
168 // CPU core 0 is run on the main thread 120 is_powered_on = true;
169 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
170 if (Settings::values.use_multi_core) {
171 for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
172 cpu_core_threads[index] =
173 std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
174 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
175 }
176 }
177
178 LOG_DEBUG(Core, "Initialized OK"); 121 LOG_DEBUG(Core, "Initialized OK");
179 122
180 // Reset counters and set time origin to current frame 123 // Reset counters and set time origin to current frame
@@ -184,7 +127,8 @@ struct System::Impl {
184 return ResultStatus::Success; 127 return ResultStatus::Success;
185 } 128 }
186 129
187 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 130 ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
131 const std::string& filepath) {
188 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 132 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
189 133
190 if (!app_loader) { 134 if (!app_loader) {
@@ -201,7 +145,7 @@ struct System::Impl {
201 return ResultStatus::ErrorSystemMode; 145 return ResultStatus::ErrorSystemMode;
202 } 146 }
203 147
204 ResultStatus init_result{Init(emu_window)}; 148 ResultStatus init_result{Init(system, emu_window)};
205 if (init_result != ResultStatus::Success) { 149 if (init_result != ResultStatus::Success) {
206 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", 150 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
207 static_cast<int>(init_result)); 151 static_cast<int>(init_result));
@@ -231,6 +175,8 @@ struct System::Impl {
231 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 175 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
232 perf_results.frametime * 1000.0); 176 perf_results.frametime * 1000.0);
233 177
178 is_powered_on = false;
179
234 // Shutdown emulation session 180 // Shutdown emulation session
235 renderer.reset(); 181 renderer.reset();
236 GDBStub::Shutdown(); 182 GDBStub::Shutdown();
@@ -240,19 +186,7 @@ struct System::Impl {
240 gpu_core.reset(); 186 gpu_core.reset();
241 187
242 // Close all CPU/threading state 188 // Close all CPU/threading state
243 cpu_barrier->NotifyEnd(); 189 cpu_core_manager.Shutdown();
244 if (Settings::values.use_multi_core) {
245 for (auto& thread : cpu_core_threads) {
246 thread->join();
247 thread.reset();
248 }
249 }
250 thread_to_cpu.clear();
251 for (auto& cpu_core : cpu_cores) {
252 cpu_core.reset();
253 }
254 cpu_exclusive_monitor.reset();
255 cpu_barrier.reset();
256 190
257 // Shutdown kernel and core timing 191 // Shutdown kernel and core timing
258 kernel.Shutdown(); 192 kernel.Shutdown();
@@ -289,11 +223,8 @@ struct System::Impl {
289 std::unique_ptr<VideoCore::RendererBase> renderer; 223 std::unique_ptr<VideoCore::RendererBase> renderer;
290 std::unique_ptr<Tegra::GPU> gpu_core; 224 std::unique_ptr<Tegra::GPU> gpu_core;
291 std::shared_ptr<Tegra::DebugContext> debug_context; 225 std::shared_ptr<Tegra::DebugContext> debug_context;
292 std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; 226 CpuCoreManager cpu_core_manager;
293 std::unique_ptr<CpuBarrier> cpu_barrier; 227 bool is_powered_on = false;
294 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
295 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
296 std::size_t active_core{}; ///< Active core, only used in single thread mode
297 228
298 /// Frontend applets 229 /// Frontend applets
299 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; 230 std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@@ -307,9 +238,6 @@ struct System::Impl {
307 ResultStatus status = ResultStatus::Success; 238 ResultStatus status = ResultStatus::Success;
308 std::string status_details = ""; 239 std::string status_details = "";
309 240
310 /// Map of guest threads to CPU cores
311 std::map<std::thread::id, Cpu*> thread_to_cpu;
312
313 Core::PerfStats perf_stats; 241 Core::PerfStats perf_stats;
314 Core::FrameLimiter frame_limiter; 242 Core::FrameLimiter frame_limiter;
315}; 243};
@@ -334,17 +262,15 @@ System::ResultStatus System::SingleStep() {
334} 262}
335 263
336void System::InvalidateCpuInstructionCaches() { 264void System::InvalidateCpuInstructionCaches() {
337 for (auto& cpu : impl->cpu_cores) { 265 impl->cpu_core_manager.InvalidateAllInstructionCaches();
338 cpu->ArmInterface().ClearInstructionCache();
339 }
340} 266}
341 267
342System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 268System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
343 return impl->Load(emu_window, filepath); 269 return impl->Load(*this, emu_window, filepath);
344} 270}
345 271
346bool System::IsPoweredOn() const { 272bool System::IsPoweredOn() const {
347 return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); 273 return impl->is_powered_on;
348} 274}
349 275
350void System::PrepareReschedule() { 276void System::PrepareReschedule() {
@@ -408,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
408} 334}
409 335
410Cpu& System::CpuCore(std::size_t core_index) { 336Cpu& System::CpuCore(std::size_t core_index) {
411 ASSERT(core_index < NUM_CPU_CORES); 337 return impl->cpu_core_manager.GetCore(core_index);
412 return *impl->cpu_cores[core_index];
413} 338}
414 339
415const Cpu& System::CpuCore(std::size_t core_index) const { 340const Cpu& System::CpuCore(std::size_t core_index) const {
416 ASSERT(core_index < NUM_CPU_CORES); 341 ASSERT(core_index < NUM_CPU_CORES);
417 return *impl->cpu_cores[core_index]; 342 return impl->cpu_core_manager.GetCore(core_index);
418} 343}
419 344
420ExclusiveMonitor& System::Monitor() { 345ExclusiveMonitor& System::Monitor() {
421 return *impl->cpu_exclusive_monitor; 346 return impl->cpu_core_manager.GetExclusiveMonitor();
422} 347}
423 348
424const ExclusiveMonitor& System::Monitor() const { 349const ExclusiveMonitor& System::Monitor() const {
425 return *impl->cpu_exclusive_monitor; 350 return impl->cpu_core_manager.GetExclusiveMonitor();
426} 351}
427 352
428Tegra::GPU& System::GPU() { 353Tegra::GPU& System::GPU() {
@@ -506,7 +431,7 @@ const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() cons
506} 431}
507 432
508System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 433System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
509 return impl->Init(emu_window); 434 return impl->Init(*this, emu_window);
510} 435}
511 436
512void System::Shutdown() { 437void System::Shutdown() {
diff --git a/src/core/cpu_core_manager.cpp b/src/core/cpu_core_manager.cpp
new file mode 100644
index 000000000..769a6fefa
--- /dev/null
+++ b/src/core/cpu_core_manager.cpp
@@ -0,0 +1,142 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/arm/exclusive_monitor.h"
7#include "core/core.h"
8#include "core/core_cpu.h"
9#include "core/cpu_core_manager.h"
10#include "core/gdbstub/gdbstub.h"
11#include "core/settings.h"
12
13namespace Core {
14namespace {
15void RunCpuCore(const System& system, Cpu& cpu_state) {
16 while (system.IsPoweredOn()) {
17 cpu_state.RunLoop(true);
18 }
19}
20} // Anonymous namespace
21
22CpuCoreManager::CpuCoreManager() = default;
23CpuCoreManager::~CpuCoreManager() = default;
24
25void CpuCoreManager::Initialize(System& system) {
26 barrier = std::make_unique<CpuBarrier>();
27 exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
28
29 for (std::size_t index = 0; index < cores.size(); ++index) {
30 cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
31 }
32
33 // Create threads for CPU cores 1-3, and build thread_to_cpu map
34 // CPU core 0 is run on the main thread
35 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
36 if (!Settings::values.use_multi_core) {
37 return;
38 }
39
40 for (std::size_t index = 0; index < core_threads.size(); ++index) {
41 core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
42 std::ref(*cores[index + 1]));
43 thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
44 }
45}
46
47void CpuCoreManager::Shutdown() {
48 barrier->NotifyEnd();
49 if (Settings::values.use_multi_core) {
50 for (auto& thread : core_threads) {
51 thread->join();
52 thread.reset();
53 }
54 }
55
56 thread_to_cpu.clear();
57 for (auto& cpu_core : cores) {
58 cpu_core.reset();
59 }
60
61 exclusive_monitor.reset();
62 barrier.reset();
63}
64
65Cpu& CpuCoreManager::GetCore(std::size_t index) {
66 return *cores.at(index);
67}
68
69const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
70 return *cores.at(index);
71}
72
73ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
74 return *exclusive_monitor;
75}
76
77const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
78 return *exclusive_monitor;
79}
80
81Cpu& CpuCoreManager::GetCurrentCore() {
82 if (Settings::values.use_multi_core) {
83 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
84 ASSERT(search != thread_to_cpu.end());
85 ASSERT(search->second);
86 return *search->second;
87 }
88
89 // Otherwise, use single-threaded mode active_core variable
90 return *cores[active_core];
91}
92
93const Cpu& CpuCoreManager::GetCurrentCore() const {
94 if (Settings::values.use_multi_core) {
95 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
96 ASSERT(search != thread_to_cpu.end());
97 ASSERT(search->second);
98 return *search->second;
99 }
100
101 // Otherwise, use single-threaded mode active_core variable
102 return *cores[active_core];
103}
104
105void CpuCoreManager::RunLoop(bool tight_loop) {
106 // Update thread_to_cpu in case Core 0 is run from a different host thread
107 thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
108
109 if (GDBStub::IsServerEnabled()) {
110 GDBStub::HandlePacket();
111
112 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
113 // execute. Otherwise, get out of the loop function.
114 if (GDBStub::GetCpuHaltFlag()) {
115 if (GDBStub::GetCpuStepFlag()) {
116 tight_loop = false;
117 } else {
118 return;
119 }
120 }
121 }
122
123 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
124 cores[active_core]->RunLoop(tight_loop);
125 if (Settings::values.use_multi_core) {
126 // Cores 1-3 are run on other threads in this mode
127 break;
128 }
129 }
130
131 if (GDBStub::IsServerEnabled()) {
132 GDBStub::SetCpuStepFlag(false);
133 }
134}
135
136void CpuCoreManager::InvalidateAllInstructionCaches() {
137 for (auto& cpu : cores) {
138 cpu->ArmInterface().ClearInstructionCache();
139 }
140}
141
142} // namespace Core
diff --git a/src/core/cpu_core_manager.h b/src/core/cpu_core_manager.h
new file mode 100644
index 000000000..a4d70ec56
--- /dev/null
+++ b/src/core/cpu_core_manager.h
@@ -0,0 +1,59 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8#include <map>
9#include <memory>
10#include <thread>
11
12namespace Core {
13
14class Cpu;
15class CpuBarrier;
16class ExclusiveMonitor;
17class System;
18
19class CpuCoreManager {
20public:
21 CpuCoreManager();
22 CpuCoreManager(const CpuCoreManager&) = delete;
23 CpuCoreManager(CpuCoreManager&&) = delete;
24
25 ~CpuCoreManager();
26
27 CpuCoreManager& operator=(const CpuCoreManager&) = delete;
28 CpuCoreManager& operator=(CpuCoreManager&&) = delete;
29
30 void Initialize(System& system);
31 void Shutdown();
32
33 Cpu& GetCore(std::size_t index);
34 const Cpu& GetCore(std::size_t index) const;
35
36 Cpu& GetCurrentCore();
37 const Cpu& GetCurrentCore() const;
38
39 ExclusiveMonitor& GetExclusiveMonitor();
40 const ExclusiveMonitor& GetExclusiveMonitor() const;
41
42 void RunLoop(bool tight_loop);
43
44 void InvalidateAllInstructionCaches();
45
46private:
47 static constexpr std::size_t NUM_CPU_CORES = 4;
48
49 std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
50 std::unique_ptr<CpuBarrier> barrier;
51 std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
52 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
53 std::size_t active_core{}; ///< Active core, only used in single thread mode
54
55 /// Map of guest threads to CPU cores
56 std::map<std::thread::id, Cpu*> thread_to_cpu;
57};
58
59} // namespace Core