3a4841e403
This would result in a lot of allocations and related object construction, just to toss it all away immediately after the call. These are definitely not intentional, and it was intended that all of these should have been accessing the static function GetInstance() through the name itself, not constructed instances.
260 lines
8.1 KiB
C++
260 lines
8.1 KiB
C++
// Copyright 2014 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include "common/logging/log.h"
|
|
#include "core/core.h"
|
|
#include "core/core_timing.h"
|
|
#include "core/gdbstub/gdbstub.h"
|
|
#include "core/hle/kernel/client_port.h"
|
|
#include "core/hle/kernel/kernel.h"
|
|
#include "core/hle/kernel/process.h"
|
|
#include "core/hle/kernel/thread.h"
|
|
#include "core/hle/service/service.h"
|
|
#include "core/hle/service/sm/controller.h"
|
|
#include "core/hle/service/sm/sm.h"
|
|
#include "core/hw/hw.h"
|
|
#include "core/loader/loader.h"
|
|
#include "core/memory_setup.h"
|
|
#include "core/settings.h"
|
|
#include "video_core/video_core.h"
|
|
|
|
namespace Core {
|
|
|
|
/*static*/ System System::s_instance;
|
|
|
|
System::~System() = default;
|
|
|
|
/// Runs a CPU core while the system is powered on
|
|
static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
|
|
while (Core::System::GetInstance().IsPoweredOn()) {
|
|
cpu_state->RunLoop(true);
|
|
}
|
|
}
|
|
|
|
Cpu& System::CurrentCpuCore() {
|
|
// If multicore is enabled, use host thread to figure out the current CPU core
|
|
if (Settings::values.use_multi_core) {
|
|
const auto& search = thread_to_cpu.find(std::this_thread::get_id());
|
|
ASSERT(search != thread_to_cpu.end());
|
|
ASSERT(search->second);
|
|
return *search->second;
|
|
}
|
|
|
|
// Otherwise, use single-threaded mode active_core variable
|
|
return *cpu_cores[active_core];
|
|
}
|
|
|
|
System::ResultStatus System::RunLoop(bool tight_loop) {
|
|
status = ResultStatus::Success;
|
|
|
|
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
|
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
|
|
|
|
if (GDBStub::IsServerEnabled()) {
|
|
GDBStub::HandlePacket();
|
|
|
|
// If the loop is halted and we want to step, use a tiny (1) number of instructions to
|
|
// execute. Otherwise, get out of the loop function.
|
|
if (GDBStub::GetCpuHaltFlag()) {
|
|
if (GDBStub::GetCpuStepFlag()) {
|
|
GDBStub::SetCpuStepFlag(false);
|
|
tight_loop = false;
|
|
} else {
|
|
return ResultStatus::Success;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
|
cpu_cores[active_core]->RunLoop(tight_loop);
|
|
if (Settings::values.use_multi_core) {
|
|
// Cores 1-3 are run on other threads in this mode
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
System::ResultStatus System::SingleStep() {
|
|
return RunLoop(false);
|
|
}
|
|
|
|
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
|
|
app_loader = Loader::GetLoader(filepath);
|
|
|
|
if (!app_loader) {
|
|
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
|
return ResultStatus::ErrorGetLoader;
|
|
}
|
|
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
|
|
app_loader->LoadKernelSystemMode();
|
|
|
|
if (system_mode.second != Loader::ResultStatus::Success) {
|
|
LOG_CRITICAL(Core, "Failed to determine system mode (Error {})!",
|
|
static_cast<int>(system_mode.second));
|
|
|
|
switch (system_mode.second) {
|
|
case Loader::ResultStatus::ErrorEncrypted:
|
|
return ResultStatus::ErrorLoader_ErrorEncrypted;
|
|
case Loader::ResultStatus::ErrorInvalidFormat:
|
|
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
|
case Loader::ResultStatus::ErrorUnsupportedArch:
|
|
return ResultStatus::ErrorUnsupportedArch;
|
|
default:
|
|
return ResultStatus::ErrorSystemMode;
|
|
}
|
|
}
|
|
|
|
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
|
|
if (init_result != ResultStatus::Success) {
|
|
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
|
|
static_cast<int>(init_result));
|
|
System::Shutdown();
|
|
return init_result;
|
|
}
|
|
|
|
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
|
|
if (Loader::ResultStatus::Success != load_result) {
|
|
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
|
|
System::Shutdown();
|
|
|
|
switch (load_result) {
|
|
case Loader::ResultStatus::ErrorEncrypted:
|
|
return ResultStatus::ErrorLoader_ErrorEncrypted;
|
|
case Loader::ResultStatus::ErrorInvalidFormat:
|
|
return ResultStatus::ErrorLoader_ErrorInvalidFormat;
|
|
case Loader::ResultStatus::ErrorUnsupportedArch:
|
|
return ResultStatus::ErrorUnsupportedArch;
|
|
default:
|
|
return ResultStatus::ErrorLoader;
|
|
}
|
|
}
|
|
status = ResultStatus::Success;
|
|
return status;
|
|
}
|
|
|
|
void System::PrepareReschedule() {
|
|
CurrentCpuCore().PrepareReschedule();
|
|
}
|
|
|
|
PerfStats::Results System::GetAndResetPerfStats() {
|
|
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
|
|
}
|
|
|
|
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
|
|
ASSERT(core_index < NUM_CPU_CORES);
|
|
return cpu_cores[core_index]->Scheduler();
|
|
}
|
|
|
|
ARM_Interface& System::ArmInterface(size_t core_index) {
|
|
ASSERT(core_index < NUM_CPU_CORES);
|
|
return cpu_cores[core_index]->ArmInterface();
|
|
}
|
|
|
|
Cpu& System::CpuCore(size_t core_index) {
|
|
ASSERT(core_index < NUM_CPU_CORES);
|
|
return *cpu_cores[core_index];
|
|
}
|
|
|
|
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
|
LOG_DEBUG(HW_Memory, "initialized OK");
|
|
|
|
CoreTiming::Init();
|
|
|
|
current_process = Kernel::Process::Create("main");
|
|
|
|
cpu_barrier = std::make_shared<CpuBarrier>();
|
|
for (size_t index = 0; index < cpu_cores.size(); ++index) {
|
|
cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);
|
|
}
|
|
|
|
gpu_core = std::make_unique<Tegra::GPU>();
|
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
|
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
|
|
|
HW::Init();
|
|
Kernel::Init(system_mode);
|
|
Service::Init(service_manager);
|
|
GDBStub::Init();
|
|
|
|
if (!VideoCore::Init(emu_window)) {
|
|
return ResultStatus::ErrorVideoCore;
|
|
}
|
|
|
|
// Create threads for CPU cores 1-3, and build thread_to_cpu map
|
|
// CPU core 0 is run on the main thread
|
|
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
|
|
if (Settings::values.use_multi_core) {
|
|
for (size_t index = 0; index < cpu_core_threads.size(); ++index) {
|
|
cpu_core_threads[index] =
|
|
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
|
|
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
|
|
}
|
|
}
|
|
|
|
LOG_DEBUG(Core, "Initialized OK");
|
|
|
|
// Reset counters and set time origin to current frame
|
|
GetAndResetPerfStats();
|
|
perf_stats.BeginSystemFrame();
|
|
|
|
return ResultStatus::Success;
|
|
}
|
|
|
|
void System::Shutdown() {
|
|
// Log last frame performance stats
|
|
auto perf_results = GetAndResetPerfStats();
|
|
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
|
perf_results.emulation_speed * 100.0);
|
|
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
|
perf_results.game_fps);
|
|
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
|
|
perf_results.frametime * 1000.0);
|
|
|
|
// Shutdown emulation session
|
|
VideoCore::Shutdown();
|
|
GDBStub::Shutdown();
|
|
Service::Shutdown();
|
|
Kernel::Shutdown();
|
|
HW::Shutdown();
|
|
service_manager.reset();
|
|
telemetry_session.reset();
|
|
gpu_core.reset();
|
|
|
|
// Close all CPU/threading state
|
|
cpu_barrier->NotifyEnd();
|
|
if (Settings::values.use_multi_core) {
|
|
for (auto& thread : cpu_core_threads) {
|
|
thread->join();
|
|
thread.reset();
|
|
}
|
|
}
|
|
thread_to_cpu.clear();
|
|
for (auto& cpu_core : cpu_cores) {
|
|
cpu_core.reset();
|
|
}
|
|
cpu_barrier.reset();
|
|
|
|
// Close core timing
|
|
CoreTiming::Shutdown();
|
|
|
|
// Close app loader
|
|
app_loader.reset();
|
|
|
|
LOG_DEBUG(Core, "Shutdown OK");
|
|
}
|
|
|
|
Service::SM::ServiceManager& System::ServiceManager() {
|
|
return *service_manager;
|
|
}
|
|
|
|
const Service::SM::ServiceManager& System::ServiceManager() const {
|
|
return *service_manager;
|
|
}
|
|
|
|
} // namespace Core
|