Merge pull request #1204 from lioncash/pimpl

core: Make the main System class use the PImpl idiom
This commit is contained in:
bunnei 2018-08-31 11:31:20 -04:00 committed by GitHub
commit f08d24e9c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 410 additions and 302 deletions

View file

@ -27,71 +27,9 @@ namespace Core {
/*static*/ System System::s_instance; /*static*/ System System::s_instance;
System::System() = default; namespace {
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
System::~System() = default; const std::string& path) {
/// 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()) {
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;
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files. // To account for split 00+01+etc files.
std::string dir_name; std::string dir_name;
std::string filename; std::string filename;
@ -121,41 +59,267 @@ static FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem
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(std::shared_ptr<Cpu> cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
}
}
} // Anonymous namespace
struct System::Impl {
Cpu& CurrentCpuCore() {
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];
}
ResultStatus 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()) {
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;
}
ResultStatus Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK");
CoreTiming::Init();
kernel.Initialize();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create(kernel, "main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// 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;
}
ResultStatus Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, 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));
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(emu_window)};
if (init_result != ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to initialize system (Error {})!",
static_cast<int>(init_result));
Shutdown();
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
}
void 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
renderer.reset();
GDBStub::Shutdown();
Service::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();
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
}
Loader::ResultStatus GetGameName(std::string& out) const {
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
void SetStatus(ResultStatus new_status, const char* details = nullptr) {
status = new_status;
if (details) {
status_details = details;
}
}
PerfStats::Results GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
Kernel::KernelCore kernel;
/// RealVfsFilesystem instance
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
System::System() : impl{std::make_unique<Impl>()} {}
System::~System() = default;
Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
System::ResultStatus System::SingleStep() {
return RunLoop(false);
}
void System::InvalidateCpuInstructionCaches() {
for (auto& cpu : impl->cpu_cores) {
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) {
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath)); return impl->Load(emu_window, filepath);
}
if (!app_loader) { bool System::IsPoweredOn() const {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); return impl->cpu_barrier && impl->cpu_barrier->IsAlive();
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));
return ResultStatus::ErrorSystemMode;
}
ResultStatus init_result{Init(emu_window)};
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 (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
System::Shutdown();
return static_cast<ResultStatus>(static_cast<u32>(ResultStatus::ErrorLoader) +
static_cast<u32>(load_result));
}
status = ResultStatus::Success;
return status;
} }
void System::PrepareReschedule() { void System::PrepareReschedule() {
@ -163,131 +327,134 @@ void System::PrepareReschedule() {
} }
PerfStats::Results System::GetAndResetPerfStats() { PerfStats::Results System::GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs()); return impl->GetAndResetPerfStats();
}
Core::TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
size_t System::CurrentCoreIndex() {
return CurrentCpuCore().CoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
} }
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES); ASSERT(core_index < NUM_CPU_CORES);
return cpu_cores[core_index]->Scheduler(); return impl->cpu_cores[core_index]->Scheduler();
} }
Kernel::KernelCore& System::Kernel() { Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
return kernel; return impl->current_process;
}
const Kernel::KernelCore& System::Kernel() const {
return kernel;
} }
ARM_Interface& System::ArmInterface(size_t core_index) { ARM_Interface& System::ArmInterface(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES); ASSERT(core_index < NUM_CPU_CORES);
return cpu_cores[core_index]->ArmInterface(); return impl->cpu_cores[core_index]->ArmInterface();
} }
Cpu& System::CpuCore(size_t core_index) { Cpu& System::CpuCore(size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES); ASSERT(core_index < NUM_CPU_CORES);
return *cpu_cores[core_index]; return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
return *impl->gpu_core;
}
const Tegra::GPU& System::GPU() const {
return *impl->gpu_core;
}
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
}
const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer;
}
Kernel::KernelCore& System::Kernel() {
return impl->kernel;
}
const Kernel::KernelCore& System::Kernel() const {
return impl->kernel;
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
return impl->frame_limiter;
}
const Core::FrameLimiter& System::FrameLimiter() const {
return impl->frame_limiter;
}
Loader::ResultStatus System::GetGameName(std::string& out) const {
return impl->GetGameName(out);
}
void System::SetStatus(ResultStatus new_status, const char* details) {
impl->SetStatus(new_status, details);
}
const std::string& System::GetStatusDetails() const {
return impl->status_details;
}
Loader::AppLoader& System::GetAppLoader() const {
return *impl->app_loader;
}
void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
impl->debug_context = std::move(context);
}
std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const {
return impl->debug_context;
}
void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
impl->virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem System::GetFilesystem() const {
return impl->virtual_filesystem;
} }
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
LOG_DEBUG(HW_Memory, "initialized OK"); return impl->Init(emu_window);
CoreTiming::Init();
kernel.Initialize();
// Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
current_process = Kernel::Process::Create(kernel, "main");
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core = std::make_unique<Tegra::GPU>(renderer->Rasterizer());
// 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() { void System::Shutdown() {
// Log last frame performance stats impl->Shutdown();
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
renderer.reset();
GDBStub::Shutdown();
Service::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();
// Shutdown kernel and core timing
kernel.Shutdown();
CoreTiming::Shutdown();
// Close app loader
app_loader.reset();
LOG_DEBUG(Core, "Shutdown OK");
} }
Service::SM::ServiceManager& System::ServiceManager() { Service::SM::ServiceManager& System::ServiceManager() {
return *service_manager; return *impl->service_manager;
} }
const Service::SM::ServiceManager& System::ServiceManager() const { const Service::SM::ServiceManager& System::ServiceManager() const {
return *service_manager; return *impl->service_manager;
} }
} // namespace Core } // namespace Core

View file

@ -94,11 +94,7 @@ public:
* This function should only be used by GDB Stub to support breakpoints, memory updates and * This function should only be used by GDB Stub to support breakpoints, memory updates and
* step/continue commands. * step/continue commands.
*/ */
void InvalidateCpuInstructionCaches() { void InvalidateCpuInstructionCaches();
for (auto& cpu : cpu_cores) {
cpu->ArmInterface().ClearInstructionCache();
}
}
/// Shutdown the emulated system. /// Shutdown the emulated system.
void Shutdown(); void Shutdown();
@ -117,17 +113,13 @@ public:
* application). * application).
* @returns True if the emulated system is powered on, otherwise false. * @returns True if the emulated system is powered on, otherwise false.
*/ */
bool IsPoweredOn() const { bool IsPoweredOn() const;
return cpu_barrier && cpu_barrier->IsAlive();
}
/** /**
* Returns a reference to the telemetry session for this emulation session. * Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session. * @returns Reference to the telemetry session.
*/ */
Core::TelemetrySession& TelemetrySession() const { Core::TelemetrySession& TelemetrySession() const;
return *telemetry_session;
}
/// Prepare the core emulation for a reschedule /// Prepare the core emulation for a reschedule
void PrepareReschedule(); void PrepareReschedule();
@ -136,14 +128,13 @@ public:
PerfStats::Results GetAndResetPerfStats(); PerfStats::Results GetAndResetPerfStats();
/// Gets an ARM interface to the CPU core that is currently running /// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface() { ARM_Interface& CurrentArmInterface();
return CurrentCpuCore().ArmInterface();
}
/// Gets the index of the currently running CPU core /// Gets the index of the currently running CPU core
size_t CurrentCoreIndex() { size_t CurrentCoreIndex();
return CurrentCpuCore().CoreIndex();
} /// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
/// Gets an ARM interface to the CPU core with the specified index /// Gets an ARM interface to the CPU core with the specified index
ARM_Interface& ArmInterface(size_t core_index); ARM_Interface& ArmInterface(size_t core_index);
@ -151,43 +142,26 @@ public:
/// Gets a CPU interface to the CPU core with the specified index /// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(size_t core_index); Cpu& CpuCore(size_t core_index);
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor();
/// Gets a mutable reference to the GPU interface /// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU() { Tegra::GPU& GPU();
return *gpu_core;
}
/// Gets an immutable reference to the GPU interface. /// Gets an immutable reference to the GPU interface.
const Tegra::GPU& GPU() const { const Tegra::GPU& GPU() const;
return *gpu_core;
}
/// Gets a mutable reference to the renderer. /// Gets a mutable reference to the renderer.
VideoCore::RendererBase& Renderer() { VideoCore::RendererBase& Renderer();
return *renderer;
}
/// Gets an immutable reference to the renderer. /// Gets an immutable reference to the renderer.
const VideoCore::RendererBase& Renderer() const { const VideoCore::RendererBase& Renderer() const;
return *renderer;
}
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
}
/// Gets the exclusive monitor
ExclusiveMonitor& Monitor() {
return *cpu_exclusive_monitor;
}
/// Gets the scheduler for the CPU core with the specified index /// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index); const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index);
/// Gets the current process /// Gets the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
return current_process;
}
/// Provides a reference to the kernel instance. /// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel(); Kernel::KernelCore& Kernel();
@ -195,49 +169,37 @@ public:
/// Provides a constant reference to the kernel instance. /// Provides a constant reference to the kernel instance.
const Kernel::KernelCore& Kernel() const; const Kernel::KernelCore& Kernel() const;
/// Provides a reference to the internal PerfStats instance.
Core::PerfStats& GetPerfStats();
/// Provides a constant reference to the internal PerfStats instance.
const Core::PerfStats& GetPerfStats() const;
/// Provides a reference to the frame limiter;
Core::FrameLimiter& FrameLimiter();
/// Provides a constant referent to the frame limiter
const Core::FrameLimiter& FrameLimiter() const;
/// Gets the name of the current game /// Gets the name of the current game
Loader::ResultStatus GetGameName(std::string& out) const { Loader::ResultStatus GetGameName(std::string& out) const;
if (app_loader == nullptr)
return Loader::ResultStatus::ErrorNotInitialized;
return app_loader->ReadTitle(out);
}
PerfStats perf_stats; void SetStatus(ResultStatus new_status, const char* details);
FrameLimiter frame_limiter;
void SetStatus(ResultStatus new_status, const char* details = nullptr) { const std::string& GetStatusDetails() const;
status = new_status;
if (details) {
status_details = details;
}
}
const std::string& GetStatusDetails() const { Loader::AppLoader& GetAppLoader() const;
return status_details;
}
Loader::AppLoader& GetAppLoader() const {
return *app_loader;
}
Service::SM::ServiceManager& ServiceManager(); Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const; const Service::SM::ServiceManager& ServiceManager() const;
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
debug_context = std::move(context);
}
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const { std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const;
return debug_context;
}
void SetFilesystem(FileSys::VirtualFilesystem vfs) { void SetFilesystem(FileSys::VirtualFilesystem vfs);
virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem GetFilesystem() const { FileSys::VirtualFilesystem GetFilesystem() const;
return virtual_filesystem;
}
private: private:
System(); System();
@ -253,34 +215,10 @@ private:
*/ */
ResultStatus Init(Frontend::EmuWindow& emu_window); ResultStatus Init(Frontend::EmuWindow& emu_window);
Kernel::KernelCore kernel; struct Impl;
/// RealVfsFilesystem instance std::unique_ptr<Impl> impl;
FileSys::VirtualFilesystem virtual_filesystem;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
Kernel::SharedPtr<Kernel::Process> current_process;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
size_t active_core{}; ///< Active core, only used in single thread mode
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
/// Telemetry session for this emulation session
std::unique_ptr<Core::TelemetrySession> telemetry_session;
static System s_instance; static System s_instance;
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
}; };
inline ARM_Interface& CurrentArmInterface() { inline ARM_Interface& CurrentArmInterface() {

View file

@ -7,6 +7,7 @@
#include "core/core.h" #include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvmap.h" #include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/perf_stats.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
@ -31,7 +32,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3
transform, crop_rect}; transform, crop_rect};
auto& instance = Core::System::GetInstance(); auto& instance = Core::System::GetInstance();
instance.perf_stats.EndGameFrame(); instance.GetPerfStats().EndGameFrame();
instance.Renderer().SwapBuffers(framebuffer); instance.Renderer().SwapBuffers(framebuffer);
} }

View file

@ -17,6 +17,7 @@
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h" #include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h" #include "core/hle/service/nvflinger/nvflinger.h"
#include "core/perf_stats.h"
#include "video_core/renderer_base.h" #include "video_core/renderer_base.h"
#include "video_core/video_core.h" #include "video_core/video_core.h"
@ -137,7 +138,7 @@ void NVFlinger::Compose() {
auto& system_instance = Core::System::GetInstance(); auto& system_instance = Core::System::GetInstance();
// There was no queued buffer to draw, render previous frame // There was no queued buffer to draw, render previous frame
system_instance.perf_stats.EndGameFrame(); system_instance.GetPerfStats().EndGameFrame();
system_instance.Renderer().SwapBuffers({}); system_instance.Renderer().SwapBuffers({});
continue; continue;
} }

View file

@ -14,6 +14,7 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/frontend/emu_window.h" #include "core/frontend/emu_window.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/perf_stats.h"
#include "core/settings.h" #include "core/settings.h"
#include "core/tracer/recorder.h" #include "core/tracer/recorder.h"
#include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_rasterizer.h"
@ -115,7 +116,7 @@ RendererOpenGL::~RendererOpenGL() = default;
void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) { void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&> framebuffer) {
ScopeAcquireGLContext acquire_context{render_window}; ScopeAcquireGLContext acquire_context{render_window};
Core::System::GetInstance().perf_stats.EndSystemFrame(); Core::System::GetInstance().GetPerfStats().EndSystemFrame();
// Maintain the rasterizer's state as a priority // Maintain the rasterizer's state as a priority
OpenGLState prev_state = OpenGLState::GetCurState(); OpenGLState prev_state = OpenGLState::GetCurState();
@ -140,8 +141,8 @@ void RendererOpenGL::SwapBuffers(boost::optional<const Tegra::FramebufferConfig&
render_window.PollEvents(); render_window.PollEvents();
Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs()); Core::System::GetInstance().FrameLimiter().DoFrameLimiting(CoreTiming::GetGlobalTimeUs());
Core::System::GetInstance().perf_stats.BeginSystemFrame(); Core::System::GetInstance().GetPerfStats().BeginSystemFrame();
// Restore the rasterizer state // Restore the rasterizer state
prev_state.Apply(); prev_state.Apply();