diff options
Diffstat (limited to 'src/core/core.cpp')
| -rw-r--r-- | src/core/core.cpp | 515 |
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 | ||
| 30 | System::System() = default; | 30 | namespace { |
| 31 | FileSys::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 | ||
| 32 | System::~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 |
| 35 | static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { | 63 | void 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 | ||
| 41 | Cpu& System::CurrentCpuCore() { | 70 | struct 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 | ||
| 54 | System::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 | ||
| 89 | System::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 | ||
| 93 | static 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 | ||
| 124 | System::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 | |||
| 296 | System::System() : impl{std::make_unique<Impl>()} {} | ||
| 297 | System::~System() = default; | ||
| 298 | |||
| 299 | Cpu& System::CurrentCpuCore() { | ||
| 300 | return impl->CurrentCpuCore(); | ||
| 301 | } | ||
| 302 | |||
| 303 | System::ResultStatus System::RunLoop(bool tight_loop) { | ||
| 304 | return impl->RunLoop(tight_loop); | ||
| 305 | } | ||
| 306 | |||
| 307 | System::ResultStatus System::SingleStep() { | ||
| 308 | return RunLoop(false); | ||
| 309 | } | ||
| 153 | 310 | ||
| 154 | return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) + | 311 | void 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 | |
| 317 | System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { | ||
| 318 | return impl->Load(emu_window, filepath); | ||
| 319 | } | ||
| 320 | |||
| 321 | bool System::IsPoweredOn() const { | ||
| 322 | return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); | ||
| 159 | } | 323 | } |
| 160 | 324 | ||
| 161 | void System::PrepareReschedule() { | 325 | void System::PrepareReschedule() { |
| @@ -163,131 +327,134 @@ void System::PrepareReschedule() { | |||
| 163 | } | 327 | } |
| 164 | 328 | ||
| 165 | PerfStats::Results System::GetAndResetPerfStats() { | 329 | PerfStats::Results System::GetAndResetPerfStats() { |
| 166 | return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); | 330 | return impl->GetAndResetPerfStats(); |
| 167 | } | 331 | } |
| 168 | 332 | ||
| 169 | const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { | 333 | Core::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 | ||
| 174 | Kernel::KernelCore& System::Kernel() { | 337 | ARM_Interface& System::CurrentArmInterface() { |
| 175 | return kernel; | 338 | return CurrentCpuCore().ArmInterface(); |
| 176 | } | 339 | } |
| 177 | 340 | ||
| 178 | const Kernel::KernelCore& System::Kernel() const { | 341 | size_t System::CurrentCoreIndex() { |
| 179 | return kernel; | 342 | return CurrentCpuCore().CoreIndex(); |
| 343 | } | ||
| 344 | |||
| 345 | Kernel::Scheduler& System::CurrentScheduler() { | ||
| 346 | return *CurrentCpuCore().Scheduler(); | ||
| 347 | } | ||
| 348 | |||
| 349 | const 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 | |||
| 354 | Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() { | ||
| 355 | return impl->current_process; | ||
| 180 | } | 356 | } |
| 181 | 357 | ||
| 182 | ARM_Interface& System::ArmInterface(size_t core_index) { | 358 | ARM_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 | ||
| 187 | Cpu& System::CpuCore(size_t core_index) { | 363 | Cpu& 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 | ||
| 192 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | 368 | ExclusiveMonitor& System::Monitor() { |
| 193 | LOG_DEBUG(HW_Memory, "initialized OK"); | 369 | return *impl->cpu_exclusive_monitor; |
| 370 | } | ||
| 194 | 371 | ||
| 195 | CoreTiming::Init(); | 372 | Tegra::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. | 376 | const 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"); | 380 | VideoCore::RendererBase& System::Renderer() { |
| 381 | return *impl->renderer; | ||
| 382 | } | ||
| 203 | 383 | ||
| 204 | cpu_barrier = std::make_shared<CpuBarrier>(); | 384 | const 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>(); | 388 | Kernel::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); | 392 | const Kernel::KernelCore& System::Kernel() const { |
| 214 | GDBStub::Init(); | 393 | return impl->kernel; |
| 394 | } | ||
| 215 | 395 | ||
| 216 | renderer = VideoCore::CreateRenderer(emu_window); | 396 | Core::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()); | 400 | const 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 | 404 | Core::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"); | 408 | const Core::FrameLimiter& System::FrameLimiter() const { |
| 409 | return impl->frame_limiter; | ||
| 410 | } | ||
| 235 | 411 | ||
| 236 | // Reset counters and set time origin to current frame | 412 | Loader::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; | 416 | void System::SetStatus(ResultStatus new_status, const char* details) { |
| 417 | impl->SetStatus(new_status, details); | ||
| 241 | } | 418 | } |
| 242 | 419 | ||
| 243 | void System::Shutdown() { | 420 | const 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 | 424 | Loader::AppLoader& System::GetAppLoader() const { |
| 276 | kernel.Shutdown(); | 425 | return *impl->app_loader; |
| 277 | CoreTiming::Shutdown(); | 426 | } |
| 278 | 427 | ||
| 279 | // Close app loader | 428 | void 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"); | 432 | std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const { |
| 433 | return impl->debug_context; | ||
| 434 | } | ||
| 435 | |||
| 436 | void System::SetFilesystem(FileSys::VirtualFilesystem vfs) { | ||
| 437 | impl->virtual_filesystem = std::move(vfs); | ||
| 438 | } | ||
| 439 | |||
| 440 | FileSys::VirtualFilesystem System::GetFilesystem() const { | ||
| 441 | return impl->virtual_filesystem; | ||
| 442 | } | ||
| 443 | |||
| 444 | System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { | ||
| 445 | return impl->Init(emu_window); | ||
| 446 | } | ||
| 447 | |||
| 448 | void System::Shutdown() { | ||
| 449 | impl->Shutdown(); | ||
| 283 | } | 450 | } |
| 284 | 451 | ||
| 285 | Service::SM::ServiceManager& System::ServiceManager() { | 452 | Service::SM::ServiceManager& System::ServiceManager() { |
| 286 | return *service_manager; | 453 | return *impl->service_manager; |
| 287 | } | 454 | } |
| 288 | 455 | ||
| 289 | const Service::SM::ServiceManager& System::ServiceManager() const { | 456 | const 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 |