Merge pull request #1777 from lioncash/core-mgr

core: Relocate CPU core management to its own class
This commit is contained in:
bunnei 2018-11-23 09:00:41 -08:00 committed by GitHub
commit f1969ee1f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 225 additions and 97 deletions

View File

@ -12,6 +12,8 @@ add_library(core STATIC
core_timing.h core_timing.h
core_timing_util.cpp core_timing_util.cpp
core_timing_util.h core_timing_util.h
cpu_core_manager.cpp
cpu_core_manager.h
crypto/aes_util.cpp crypto/aes_util.cpp
crypto/aes_util.h crypto/aes_util.h
crypto/encryption_layer.cpp crypto/encryption_layer.cpp

View File

@ -14,6 +14,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/core_cpu.h" #include "core/core_cpu.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/mode.h" #include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h" #include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h" #include "core/file_sys/vfs_real.h"
@ -28,7 +29,6 @@
#include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h" #include "core/loader/loader.h"
#include "core/perf_stats.h" #include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h" #include "core/telemetry_session.h"
#include "frontend/applets/software_keyboard.h" #include "frontend/applets/software_keyboard.h"
#include "video_core/debug_utils/debug_utils.h" #include "video_core/debug_utils/debug_utils.h"
@ -71,64 +71,22 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read); return vfs->OpenFile(path, FileSys::Mode::Read);
} }
/// Runs a CPU core while the system is powered on
void RunCpuCore(Cpu& cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state.RunLoop(true);
}
}
} // Anonymous namespace } // Anonymous namespace
struct System::Impl { struct System::Impl {
Cpu& CurrentCpuCore() { Cpu& CurrentCpuCore() {
if (Settings::values.use_multi_core) { return cpu_core_manager.GetCurrentCore();
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];
} }
ResultStatus RunLoop(bool tight_loop) { ResultStatus RunLoop(bool tight_loop) {
status = ResultStatus::Success; status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread cpu_core_manager.RunLoop(tight_loop);
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
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()) {
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;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
return status; return status;
} }
ResultStatus Init(Frontend::EmuWindow& emu_window) { ResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK"); LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init(); CoreTiming::Init();
@ -145,12 +103,6 @@ struct System::Impl {
auto main_process = Kernel::Process::Create(kernel, "main"); auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get()); kernel.MakeCurrentProcess(main_process.get());
cpu_barrier = std::make_unique<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>(); telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>(); service_manager = std::make_shared<Service::SM::ServiceManager>();
@ -164,17 +116,8 @@ struct System::Impl {
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer()); gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// Create threads for CPU cores 1-3, and build thread_to_cpu map cpu_core_manager.Initialize(system);
// CPU core 0 is run on the main thread is_powered_on = true;
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (Settings::values.use_multi_core) {
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
}
}
LOG_DEBUG(Core, "Initialized OK"); LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame // Reset counters and set time origin to current frame
@ -184,7 +127,8 @@ struct System::Impl {
return ResultStatus::Success; return ResultStatus::Success;
} }
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) { if (!app_loader) {
@ -201,7 +145,7 @@ struct System::Impl {
return ResultStatus::ErrorSystemMode; return ResultStatus::ErrorSystemMode;
} }
ResultStatus init_result{Init(emu_window)}; ResultStatus init_result{Init(system, emu_window)};
if (init_result != ResultStatus::Success) { if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!", LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result)); static_cast<int>(init_result));
@ -231,6 +175,8 @@ struct System::Impl {
Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0); perf_results.frametime * 1000.0);
is_powered_on = false;
// Shutdown emulation session // Shutdown emulation session
renderer.reset(); renderer.reset();
GDBStub::Shutdown(); GDBStub::Shutdown();
@ -240,19 +186,7 @@ struct System::Impl {
gpu_core.reset(); gpu_core.reset();
// Close all CPU/threading state // Close all CPU/threading state
cpu_barrier->NotifyEnd(); cpu_core_manager.Shutdown();
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_exclusive_monitor.reset();
cpu_barrier.reset();
// Shutdown kernel and core timing // Shutdown kernel and core timing
kernel.Shutdown(); kernel.Shutdown();
@ -289,11 +223,8 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer; std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core; std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context; std::shared_ptr<Tegra::DebugContext> debug_context;
std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor; CpuCoreManager cpu_core_manager;
std::unique_ptr<CpuBarrier> cpu_barrier; bool is_powered_on = false;
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
/// Frontend applets /// Frontend applets
std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard; std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet> software_keyboard;
@ -307,9 +238,6 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success; ResultStatus status = ResultStatus::Success;
std::string status_details = ""; std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
Core::PerfStats perf_stats; Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter; Core::FrameLimiter frame_limiter;
}; };
@ -334,17 +262,15 @@ System::ResultStatus System::SingleStep() {
} }
void System::InvalidateCpuInstructionCaches() { void System::InvalidateCpuInstructionCaches() {
for (auto& cpu : impl->cpu_cores) { impl->cpu_core_manager.InvalidateAllInstructionCaches();
cpu->ArmInterface().ClearInstructionCache();
}
} }
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) { System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
return impl->Load(emu_window, filepath); return impl->Load(*this, emu_window, filepath);
} }
bool System::IsPoweredOn() const { bool System::IsPoweredOn() const {
return impl->cpu_barrier && impl->cpu_barrier->IsAlive(); return impl->is_powered_on;
} }
void System::PrepareReschedule() { void System::PrepareReschedule() {
@ -408,21 +334,20 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
} }
Cpu& System::CpuCore(std::size_t core_index) { Cpu& System::CpuCore(std::size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES); return impl->cpu_core_manager.GetCore(core_index);
return *impl->cpu_cores[core_index];
} }
const Cpu& System::CpuCore(std::size_t core_index) const { const Cpu& System::CpuCore(std::size_t core_index) const {
ASSERT(core_index < NUM_CPU_CORES); ASSERT(core_index < NUM_CPU_CORES);
return *impl->cpu_cores[core_index]; return impl->cpu_core_manager.GetCore(core_index);
} }
ExclusiveMonitor& System::Monitor() { ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor; return impl->cpu_core_manager.GetExclusiveMonitor();
} }
const ExclusiveMonitor& System::Monitor() const { const ExclusiveMonitor& System::Monitor() const {
return *impl->cpu_exclusive_monitor; return impl->cpu_core_manager.GetExclusiveMonitor();
} }
Tegra::GPU& System::GPU() { Tegra::GPU& System::GPU() {
@ -506,7 +431,7 @@ const Core::Frontend::SoftwareKeyboardApplet& System::GetSoftwareKeyboard() cons
} }
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(emu_window); return impl->Init(*this, emu_window);
} }
void System::Shutdown() { void System::Shutdown() {

View File

@ -0,0 +1,142 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/cpu_core_manager.h"
#include "core/gdbstub/gdbstub.h"
#include "core/settings.h"
namespace Core {
namespace {
void RunCpuCore(const System& system, Cpu& cpu_state) {
while (system.IsPoweredOn()) {
cpu_state.RunLoop(true);
}
}
} // Anonymous namespace
CpuCoreManager::CpuCoreManager() = default;
CpuCoreManager::~CpuCoreManager() = default;
void CpuCoreManager::Initialize(System& system) {
barrier = std::make_unique<CpuBarrier>();
exclusive_monitor = Cpu::MakeExclusiveMonitor(cores.size());
for (std::size_t index = 0; index < cores.size(); ++index) {
cores[index] = std::make_unique<Cpu>(*exclusive_monitor, *barrier, index);
}
// 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()] = cores[0].get();
if (!Settings::values.use_multi_core) {
return;
}
for (std::size_t index = 0; index < core_threads.size(); ++index) {
core_threads[index] = std::make_unique<std::thread>(RunCpuCore, std::cref(system),
std::ref(*cores[index + 1]));
thread_to_cpu[core_threads[index]->get_id()] = cores[index + 1].get();
}
}
void CpuCoreManager::Shutdown() {
barrier->NotifyEnd();
if (Settings::values.use_multi_core) {
for (auto& thread : core_threads) {
thread->join();
thread.reset();
}
}
thread_to_cpu.clear();
for (auto& cpu_core : cores) {
cpu_core.reset();
}
exclusive_monitor.reset();
barrier.reset();
}
Cpu& CpuCoreManager::GetCore(std::size_t index) {
return *cores.at(index);
}
const Cpu& CpuCoreManager::GetCore(std::size_t index) const {
return *cores.at(index);
}
ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() {
return *exclusive_monitor;
}
const ExclusiveMonitor& CpuCoreManager::GetExclusiveMonitor() const {
return *exclusive_monitor;
}
Cpu& CpuCoreManager::GetCurrentCore() {
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 *cores[active_core];
}
const Cpu& CpuCoreManager::GetCurrentCore() const {
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 *cores[active_core];
}
void CpuCoreManager::RunLoop(bool tight_loop) {
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cores[0].get();
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()) {
tight_loop = false;
} else {
return;
}
}
}
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
cores[active_core]->RunLoop(tight_loop);
if (Settings::values.use_multi_core) {
// Cores 1-3 are run on other threads in this mode
break;
}
}
if (GDBStub::IsServerEnabled()) {
GDBStub::SetCpuStepFlag(false);
}
}
void CpuCoreManager::InvalidateAllInstructionCaches() {
for (auto& cpu : cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
} // namespace Core

View File

@ -0,0 +1,59 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <map>
#include <memory>
#include <thread>
namespace Core {
class Cpu;
class CpuBarrier;
class ExclusiveMonitor;
class System;
class CpuCoreManager {
public:
CpuCoreManager();
CpuCoreManager(const CpuCoreManager&) = delete;
CpuCoreManager(CpuCoreManager&&) = delete;
~CpuCoreManager();
CpuCoreManager& operator=(const CpuCoreManager&) = delete;
CpuCoreManager& operator=(CpuCoreManager&&) = delete;
void Initialize(System& system);
void Shutdown();
Cpu& GetCore(std::size_t index);
const Cpu& GetCore(std::size_t index) const;
Cpu& GetCurrentCore();
const Cpu& GetCurrentCore() const;
ExclusiveMonitor& GetExclusiveMonitor();
const ExclusiveMonitor& GetExclusiveMonitor() const;
void RunLoop(bool tight_loop);
void InvalidateAllInstructionCaches();
private:
static constexpr std::size_t NUM_CPU_CORES = 4;
std::unique_ptr<ExclusiveMonitor> exclusive_monitor;
std::unique_ptr<CpuBarrier> barrier;
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
/// Map of guest threads to CPU cores
std::map<std::thread::id, Cpu*> thread_to_cpu;
};
} // namespace Core