summaryrefslogtreecommitdiff
path: root/src/core/core.cpp
diff options
context:
space:
mode:
authorGravatar Lioncash2018-08-30 10:50:54 -0400
committerGravatar Lioncash2018-08-31 07:16:57 -0400
commite2457418dae19b889b2ad85255bb95d4cd0e4bff (patch)
tree76ad194f1cc1933f7907223dbb2542b3614576e5 /src/core/core.cpp
parentMerge pull request #1198 from lioncash/kernel (diff)
downloadyuzu-e2457418dae19b889b2ad85255bb95d4cd0e4bff.tar.gz
yuzu-e2457418dae19b889b2ad85255bb95d4cd0e4bff.tar.xz
yuzu-e2457418dae19b889b2ad85255bb95d4cd0e4bff.zip
core: Make the main System class use the PImpl idiom
core.h is kind of a massive header in terms what it includes within itself. It includes VFS utilities, kernel headers, file_sys header, ARM-related headers, etc. This means that changing anything in the headers included by core.h essentially requires you to rebuild almost all of core. Instead, we can modify the System class to use the PImpl idiom, which allows us to move all of those headers to the cpp file and forward declare the bulk of the types that would otherwise be included, reducing compile times. This change specifically only performs the PImpl portion.
Diffstat (limited to 'src/core/core.cpp')
-rw-r--r--src/core/core.cpp515
1 files changed, 341 insertions, 174 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 2293669e5..75c259068 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -27,135 +27,299 @@ namespace Core {
27 27
28/*static*/ System System::s_instance; 28/*static*/ System System::s_instance;
29 29
30System::System() = default; 30namespace {
31FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
32 const std::string& path) {
33 // To account for split 00+01+etc files.
34 std::string dir_name;
35 std::string filename;
36 Common::SplitPath(path, &dir_name, &filename, nullptr);
37 if (filename == "00") {
38 const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
39 std::vector<FileSys::VirtualFile> concat;
40 for (u8 i = 0; i < 0x10; ++i) {
41 auto next = dir->GetFile(fmt::format("{:02X}", i));
42 if (next != nullptr)
43 concat.push_back(std::move(next));
44 else {
45 next = dir->GetFile(fmt::format("{:02x}", i));
46 if (next != nullptr)
47 concat.push_back(std::move(next));
48 else
49 break;
50 }
51 }
31 52
32System::~System() = default; 53 if (concat.empty())
54 return nullptr;
55
56 return FileSys::ConcatenateFiles(concat, dir->GetName());
57 }
58
59 return vfs->OpenFile(path, FileSys::Mode::Read);
60}
33 61
34/// Runs a CPU core while the system is powered on 62/// Runs a CPU core while the system is powered on
35static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { 63void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
36 while (Core::System::GetInstance().IsPoweredOn()) { 64 while (Core::System::GetInstance().IsPoweredOn()) {
37 cpu_state->RunLoop(true); 65 cpu_state->RunLoop(true);
38 } 66 }
39} 67}
68} // Anonymous namespace
40 69
41Cpu& System::CurrentCpuCore() { 70struct System::Impl {
42 // If multicore is enabled, use host thread to figure out the current CPU core 71 Cpu& CurrentCpuCore() {
43 if (Settings::values.use_multi_core) { 72 if (Settings::values.use_multi_core) {
44 const auto& search = thread_to_cpu.find(std::this_thread::get_id()); 73 const auto& search = thread_to_cpu.find(std::this_thread::get_id());
45 ASSERT(search != thread_to_cpu.end()); 74 ASSERT(search != thread_to_cpu.end());
46 ASSERT(search->second); 75 ASSERT(search->second);
47 return *search->second; 76 return *search->second;
77 }
78
79 // Otherwise, use single-threaded mode active_core variable
80 return *cpu_cores[active_core];
48 } 81 }
49 82
50 // Otherwise, use single-threaded mode active_core variable 83 ResultStatus RunLoop(bool tight_loop) {
51 return *cpu_cores[active_core]; 84 status = ResultStatus::Success;
52}
53 85
54System::ResultStatus System::RunLoop(bool tight_loop) { 86 // Update thread_to_cpu in case Core 0 is run from a different host thread
55 status = ResultStatus::Success; 87 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
56 88
57 // Update thread_to_cpu in case Core 0 is run from a different host thread 89 if (GDBStub::IsServerEnabled()) {
58 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 90 GDBStub::HandlePacket();
59 91
60 if (GDBStub::IsServerEnabled()) { 92 // If the loop is halted and we want to step, use a tiny (1) number of instructions to
61 GDBStub::HandlePacket(); 93 // execute. Otherwise, get out of the loop function.
94 if (GDBStub::GetCpuHaltFlag()) {
95 if (GDBStub::GetCpuStepFlag()) {
96 tight_loop = false;
97 } else {
98 return ResultStatus::Success;
99 }
100 }
101 }
62 102
63 // If the loop is halted and we want to step, use a tiny (1) number of instructions to 103 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
64 // execute. Otherwise, get out of the loop function. 104 cpu_cores[active_core]->RunLoop(tight_loop);
65 if (GDBStub::GetCpuHaltFlag()) { 105 if (Settings::values.use_multi_core) {
66 if (GDBStub::GetCpuStepFlag()) { 106 // Cores 1-3 are run on other threads in this mode
67 tight_loop = false; 107 break;
68 } else {
69 return ResultStatus::Success;
70 } 108 }
71 } 109 }
110
111 if (GDBStub::IsServerEnabled()) {
112 GDBStub::SetCpuStepFlag(false);
113 }
114
115 return status;
72 } 116 }
73 117
74 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { 118 ResultStatus Init(Frontend::EmuWindow& emu_window) {
75 cpu_cores[active_core]->RunLoop(tight_loop); 119 LOG_DEBUG(HW_Memory, "initialized OK");
120
121 CoreTiming::Init();
122 kernel.Initialize();
123
124 // Create a default fs if one doesn't already exist.
125 if (virtual_filesystem == nullptr)
126 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
127
128 current_process = Kernel::Process::Create(kernel, "main");
129
130 cpu_barrier = std::make_shared<CpuBarrier>();
131 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
132 for (size_t index = 0; index < cpu_cores.size(); ++index) {
133 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
134 }
135
136 telemetry_session = std::make_unique<Core::TelemetrySession>();
137 service_manager = std::make_shared<Service::SM::ServiceManager>();
138
139 Service::Init(service_manager, virtual_filesystem);
140 GDBStub::Init();
141
142 renderer = VideoCore::CreateRenderer(emu_window);
143 if (!renderer->Init()) {
144 return ResultStatus::ErrorVideoCore;
145 }
146
147 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
148
149 // Create threads for CPU cores 1-3, and build thread_to_cpu map
150 // CPU core 0 is run on the main thread
151 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
76 if (Settings::values.use_multi_core) { 152 if (Settings::values.use_multi_core) {
77 // Cores 1-3 are run on other threads in this mode 153 for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
78 break; 154 cpu_core_threads[index] =
155 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
156 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
157 }
79 } 158 }
80 }
81 159
82 if (GDBStub::IsServerEnabled()) { 160 LOG_DEBUG(Core, "Initialized OK");
83 GDBStub::SetCpuStepFlag(false); 161
162 // Reset counters and set time origin to current frame
163 GetAndResetPerfStats();
164 perf_stats.BeginSystemFrame();
165
166 return ResultStatus::Success;
84 } 167 }
85 168
86 return status; 169 ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
87} 170 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
88 171
89System::ResultStatus System::SingleStep() { 172 if (!app_loader) {
90 return RunLoop(false); 173 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
91} 174 return ResultStatus::ErrorGetLoader;
175 }
176 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
177 app_loader->LoadKernelSystemMode();
92 178
93static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 179 if (system_mode.second != Loader::ResultStatus::Success) {
94 const std::string& path) { 180 LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
95 // To account for split 00+01+etc files. 181 static_cast<int>(system_mode.second));
96 std::string dir_name; 182
97 std::string filename; 183 return ResultStatus::ErrorSystemMode;
98 Common::SplitPath(path, &dir_name, &filename, nullptr);
99 if (filename == "00") {
100 const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
101 std::vector<FileSys::VirtualFile> concat;
102 for (u8 i = 0; i < 0x10; ++i) {
103 auto next = dir->GetFile(fmt::format("{:02X}", i));
104 if (next != nullptr)
105 concat.push_back(std::move(next));
106 else {
107 next = dir->GetFile(fmt::format("{:02x}", i));
108 if (next != nullptr)
109 concat.push_back(std::move(next));
110 else
111 break;
112 }
113 } 184 }
114 185
115 if (concat.empty()) 186 ResultStatus init_result{Init(emu_window)};
116 return nullptr; 187 if (init_result != ResultStatus::Success) {
188 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
189 static_cast<int>(init_result));
190 Shutdown();
191 return init_result;
192 }
117 193
118 return FileSys::ConcatenateFiles(concat, dir->GetName()); 194 const Loader::ResultStatus load_result{app_loader->Load(current_process)};
195 if (load_result != Loader::ResultStatus::Success) {
196 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
197 Shutdown();
198
199 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
200 static_cast<u32>(load_result));
201 }
202 status = ResultStatus::Success;
203 return status;
119 } 204 }
120 205
121 return vfs->OpenFile(path, FileSys::Mode::Read); 206 void Shutdown() {
122} 207 // Log last frame performance stats
208 auto perf_results = GetAndResetPerfStats();
209 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
210 perf_results.emulation_speed * 100.0);
211 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
212 perf_results.game_fps);
213 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
214 perf_results.frametime * 1000.0);
215
216 // Shutdown emulation session
217 renderer.reset();
218 GDBStub::Shutdown();
219 Service::Shutdown();
220 service_manager.reset();
221 telemetry_session.reset();
222 gpu_core.reset();
223
224 // Close all CPU/threading state
225 cpu_barrier->NotifyEnd();
226 if (Settings::values.use_multi_core) {
227 for (auto& thread : cpu_core_threads) {
228 thread->join();
229 thread.reset();
230 }
231 }
232 thread_to_cpu.clear();
233 for (auto& cpu_core : cpu_cores) {
234 cpu_core.reset();
235 }
236 cpu_barrier.reset();
123 237
124System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { 238 // Shutdown kernel and core timing
125 app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); 239 kernel.Shutdown();
240 CoreTiming::Shutdown();
241
242 // Close app loader
243 app_loader.reset();
126 244
127 if (!app_loader) { 245 LOG_DEBUG(Core, "Shutdown OK");
128 LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
129 return ResultStatus::ErrorGetLoader;
130 } 246 }
131 std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
132 app_loader->LoadKernelSystemMode();
133 247
134 if (system_mode.second != Loader::ResultStatus::Success) { 248 Loader::ResultStatus GetGameName(std::string& out) const {
135 LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!", 249 if (app_loader == nullptr)
136 static_cast<int>(system_mode.second)); 250 return Loader::ResultStatus::ErrorNotInitialized;
251 return app_loader->ReadTitle(out);
252 }
137 253
138 return ResultStatus::ErrorSystemMode; 254 void SetStatus(ResultStatus new_status, const char* details = nullptr) {
255 status = new_status;
256 if (details) {
257 status_details = details;
258 }
139 } 259 }
140 260
141 ResultStatus init_result{Init(emu_window)}; 261 PerfStats::Results GetAndResetPerfStats() {
142 if (init_result != ResultStatus::Success) { 262 return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
143 LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
144 static_cast<int>(init_result));
145 System::Shutdown();
146 return init_result;
147 } 263 }
148 264
149 const Loader::ResultStatus load_result{app_loader->Load(current_process)}; 265 Kernel::KernelCore kernel;
150 if (load_result != Loader::ResultStatus::Success) { 266 /// RealVfsFilesystem instance
151 LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); 267 FileSys::VirtualFilesystem virtual_filesystem;
152 System::Shutdown(); 268 /// AppLoader used to load the current executing application
269 std::unique_ptr<Loader::AppLoader> app_loader;
270 std::unique_ptr<VideoCore::RendererBase> renderer;
271 std::unique_ptr<Tegra::GPU> gpu_core;
272 std::shared_ptr<Tegra::DebugContext> debug_context;
273 Kernel::SharedPtr<Kernel::Process> current_process;
274 std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
275 std::shared_ptr<CpuBarrier> cpu_barrier;
276 std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
277 std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
278 size_t active_core{}; ///< Active core, only used in single thread mode
279
280 /// Service manager
281 std::shared_ptr<Service::SM::ServiceManager> service_manager;
282
283 /// Telemetry session for this emulation session
284 std::unique_ptr<Core::TelemetrySession> telemetry_session;
285
286 ResultStatus status = ResultStatus::Success;
287 std::string status_details = "";
288
289 /// Map of guest threads to CPU cores
290 std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
291
292 Core::PerfStats perf_stats;
293 Core::FrameLimiter frame_limiter;
294};
295
296System::System() : impl{std::make_unique<Impl>()} {}
297System::~System() = default;
298
299Cpu& System::CurrentCpuCore() {
300 return impl->CurrentCpuCore();
301}
302
303System::ResultStatus System::RunLoop(bool tight_loop) {
304 return impl->RunLoop(tight_loop);
305}
306
307System::ResultStatus System::SingleStep() {
308 return RunLoop(false);
309}
153 310
154 return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + 311void System::InvalidateCpuInstructionCaches() {
155 static_cast<u32>(load_result)); 312 for (auto& cpu : impl->cpu_cores) {
313 cpu->ArmInterface().ClearInstructionCache();
156 } 314 }
157 status = ResultStatus::Success; 315}
158 return status; 316
317System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
318 return impl->Load(emu_window, filepath);
319}
320
321bool System::IsPoweredOn() const {
322 return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
159} 323}
160 324
161void System::PrepareReschedule() { 325void System::PrepareReschedule() {
@@ -163,131 +327,134 @@ void System::PrepareReschedule() {
163} 327}
164 328
165PerfStats::Results System::GetAndResetPerfStats() { 329PerfStats::Results System::GetAndResetPerfStats() {
166 return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); 330 return impl->GetAndResetPerfStats();
167} 331}
168 332
169const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { 333Core::TelemetrySession& System::TelemetrySession() const {
170 ASSERT(core_index < NUM_CPU_CORES); 334 return *impl->telemetry_session;
171 return cpu_cores[core_index]->Scheduler();
172} 335}
173 336
174Kernel::KernelCore& System::Kernel() { 337ARM_Interface& System::CurrentArmInterface() {
175 return kernel; 338 return CurrentCpuCore().ArmInterface();
176} 339}
177 340
178const Kernel::KernelCore& System::Kernel() const { 341size_t System::CurrentCoreIndex() {
179 return kernel; 342 return CurrentCpuCore().CoreIndex();
343}
344
345Kernel::Scheduler& System::CurrentScheduler() {
346 return *CurrentCpuCore().Scheduler();
347}
348
349const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
350 ASSERT(core_index < NUM_CPU_CORES);
351 return impl->cpu_cores[core_index]->Scheduler();
352}
353
354Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
355 return impl->current_process;
180} 356}
181 357
182ARM_Interface& System::ArmInterface(size_t core_index) { 358ARM_Interface& System::ArmInterface(size_t core_index) {
183 ASSERT(core_index < NUM_CPU_CORES); 359 ASSERT(core_index < NUM_CPU_CORES);
184 return cpu_cores[core_index]->ArmInterface(); 360 return impl->cpu_cores[core_index]->ArmInterface();
185} 361}
186 362
187Cpu& System::CpuCore(size_t core_index) { 363Cpu& System::CpuCore(size_t core_index) {
188 ASSERT(core_index < NUM_CPU_CORES); 364 ASSERT(core_index < NUM_CPU_CORES);
189 return *cpu_cores[core_index]; 365 return *impl->cpu_cores[core_index];
190} 366}
191 367
192System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 368ExclusiveMonitor& System::Monitor() {
193 LOG_DEBUG(HW_Memory, "initialized OK"); 369 return *impl->cpu_exclusive_monitor;
370}
194 371
195 CoreTiming::Init(); 372Tegra::GPU& System::GPU() {
196 kernel.Initialize(); 373 return *impl->gpu_core;
374}
197 375
198 // Create a default fs if one doesn't already exist. 376const Tegra::GPU& System::GPU() const {
199 if (virtual_filesystem == nullptr) 377 return *impl->gpu_core;
200 virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); 378}
201 379
202 current_process = Kernel::Process::Create(kernel, "main"); 380VideoCore::RendererBase& System::Renderer() {
381 return *impl->renderer;
382}
203 383
204 cpu_barrier = std::make_shared<CpuBarrier>(); 384const VideoCore::RendererBase& System::Renderer() const {
205 cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); 385 return *impl->renderer;
206 for (size_t index = 0; index < cpu_cores.size(); ++index) { 386}
207 cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
208 }
209 387
210 telemetry_session = std::make_unique<Core::TelemetrySession>(); 388Kernel::KernelCore& System::Kernel() {
211 service_manager = std::make_shared<Service::SM::ServiceManager>(); 389 return impl->kernel;
390}
212 391
213 Service::Init(service_manager, virtual_filesystem); 392const Kernel::KernelCore& System::Kernel() const {
214 GDBStub::Init(); 393 return impl->kernel;
394}
215 395
216 renderer = VideoCore::CreateRenderer(emu_window); 396Core::PerfStats& System::GetPerfStats() {
217 if (!renderer->Init()) { 397 return impl->perf_stats;
218 return ResultStatus::ErrorVideoCore; 398}
219 }
220 399
221 gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); 400const Core::PerfStats& System::GetPerfStats() const {
401 return impl->perf_stats;
402}
222 403
223 // Create threads for CPU cores 1-3, and build thread_to_cpu map 404Core::FrameLimiter& System::FrameLimiter() {
224 // CPU core 0 is run on the main thread 405 return impl->frame_limiter;
225 thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; 406}
226 if (Settings::values.use_multi_core) {
227 for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
228 cpu_core_threads[index] =
229 std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
230 thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
231 }
232 }
233 407
234 LOG_DEBUG(Core, "Initialized OK"); 408const Core::FrameLimiter& System::FrameLimiter() const {
409 return impl->frame_limiter;
410}
235 411
236 // Reset counters and set time origin to current frame 412Loader::ResultStatus System::GetGameName(std::string& out) const {
237 GetAndResetPerfStats(); 413 return impl->GetGameName(out);
238 perf_stats.BeginSystemFrame(); 414}
239 415
240 return ResultStatus::Success; 416void System::SetStatus(ResultStatus new_status, const char* details) {
417 impl->SetStatus(new_status, details);
241} 418}
242 419
243void System::Shutdown() { 420const std::string& System::GetStatusDetails() const {
244 // Log last frame performance stats 421 return impl->status_details;
245 auto perf_results = GetAndResetPerfStats(); 422}
246 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
247 perf_results.emulation_speed * 100.0);
248 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
249 perf_results.game_fps);
250 Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
251 perf_results.frametime * 1000.0);
252
253 // Shutdown emulation session
254 renderer.reset();
255 GDBStub::Shutdown();
256 Service::Shutdown();
257 service_manager.reset();
258 telemetry_session.reset();
259 gpu_core.reset();
260
261 // Close all CPU/threading state
262 cpu_barrier->NotifyEnd();
263 if (Settings::values.use_multi_core) {
264 for (auto& thread : cpu_core_threads) {
265 thread->join();
266 thread.reset();
267 }
268 }
269 thread_to_cpu.clear();
270 for (auto& cpu_core : cpu_cores) {
271 cpu_core.reset();
272 }
273 cpu_barrier.reset();
274 423
275 // Shutdown kernel and core timing 424Loader::AppLoader& System::GetAppLoader() const {
276 kernel.Shutdown(); 425 return *impl->app_loader;
277 CoreTiming::Shutdown(); 426}
278 427
279 // Close app loader 428void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
280 app_loader.reset(); 429 impl->debug_context = std::move(context);
430}
281 431
282 LOG_DEBUG(Core, "Shutdown OK"); 432std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const {
433 return impl->debug_context;
434}
435
436void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
437 impl->virtual_filesystem = std::move(vfs);
438}
439
440FileSys::VirtualFilesystem System::GetFilesystem() const {
441 return impl->virtual_filesystem;
442}
443
444System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
445 return impl->Init(emu_window);
446}
447
448void System::Shutdown() {
449 impl->Shutdown();
283} 450}
284 451
285Service::SM::ServiceManager& System::ServiceManager() { 452Service::SM::ServiceManager& System::ServiceManager() {
286 return *service_manager; 453 return *impl->service_manager;
287} 454}
288 455
289const Service::SM::ServiceManager& System::ServiceManager() const { 456const Service::SM::ServiceManager& System::ServiceManager() const {
290 return *service_manager; 457 return *impl->service_manager;
291} 458}
292 459
293} // namespace Core 460} // namespace Core