core: Partially persist emulation state across game boots.
This commit is contained in:
parent
1b787adbd0
commit
a4d11f4427
|
@ -133,6 +133,30 @@ struct System::Impl {
|
||||||
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
||||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
|
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
|
||||||
|
|
||||||
|
void Initialize(System& system) {
|
||||||
|
device_memory = std::make_unique<Core::DeviceMemory>();
|
||||||
|
|
||||||
|
is_multicore = Settings::values.use_multi_core.GetValue();
|
||||||
|
|
||||||
|
core_timing.SetMulticore(is_multicore);
|
||||||
|
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||||
|
|
||||||
|
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
||||||
|
const auto current_time =
|
||||||
|
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||||
|
Settings::values.custom_rtc_differential =
|
||||||
|
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||||
|
|
||||||
|
// Create a default fs if one doesn't already exist.
|
||||||
|
if (virtual_filesystem == nullptr)
|
||||||
|
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
||||||
|
if (content_provider == nullptr)
|
||||||
|
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
|
||||||
|
|
||||||
|
// Create default implementations of applets if one is not provided.
|
||||||
|
applet_manager.SetDefaultAppletsIfMissing();
|
||||||
|
}
|
||||||
|
|
||||||
SystemResultStatus Run() {
|
SystemResultStatus Run() {
|
||||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||||
status = SystemResultStatus::Success;
|
status = SystemResultStatus::Success;
|
||||||
|
@ -178,37 +202,17 @@ struct System::Impl {
|
||||||
debugger = std::make_unique<Debugger>(system, port);
|
debugger = std::make_unique<Debugger>(system, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
SystemResultStatus SetupForMainProcess(System& system, Frontend::EmuWindow& emu_window) {
|
||||||
LOG_DEBUG(Core, "initialized OK");
|
LOG_DEBUG(Core, "initialized OK");
|
||||||
|
|
||||||
device_memory = std::make_unique<Core::DeviceMemory>();
|
|
||||||
|
|
||||||
is_multicore = Settings::values.use_multi_core.GetValue();
|
|
||||||
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||||
|
|
||||||
kernel.SetMulticore(is_multicore);
|
kernel.SetMulticore(is_multicore);
|
||||||
cpu_manager.SetMulticore(is_multicore);
|
cpu_manager.SetMulticore(is_multicore);
|
||||||
cpu_manager.SetAsyncGpu(is_async_gpu);
|
cpu_manager.SetAsyncGpu(is_async_gpu);
|
||||||
core_timing.SetMulticore(is_multicore);
|
|
||||||
|
|
||||||
kernel.Initialize();
|
kernel.Initialize();
|
||||||
cpu_manager.Initialize();
|
cpu_manager.Initialize();
|
||||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
|
||||||
|
|
||||||
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
|
||||||
const auto current_time =
|
|
||||||
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
|
||||||
Settings::values.custom_rtc_differential =
|
|
||||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
|
||||||
|
|
||||||
// Create a default fs if one doesn't already exist.
|
|
||||||
if (virtual_filesystem == nullptr)
|
|
||||||
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
|
|
||||||
if (content_provider == nullptr)
|
|
||||||
content_provider = std::make_unique<FileSys::ContentProviderUnion>();
|
|
||||||
|
|
||||||
/// Create default implementations of applets if one is not provided.
|
|
||||||
applet_manager.SetDefaultAppletsIfMissing();
|
|
||||||
|
|
||||||
/// Reset all glue registrations
|
/// Reset all glue registrations
|
||||||
arp_manager.ResetAll();
|
arp_manager.ResetAll();
|
||||||
|
@ -253,11 +257,11 @@ struct System::Impl {
|
||||||
return SystemResultStatus::ErrorGetLoader;
|
return SystemResultStatus::ErrorGetLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
SystemResultStatus init_result{Init(system, emu_window)};
|
SystemResultStatus init_result{SetupForMainProcess(system, emu_window)};
|
||||||
if (init_result != SystemResultStatus::Success) {
|
if (init_result != SystemResultStatus::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));
|
||||||
Shutdown();
|
ShutdownMainProcess();
|
||||||
return init_result;
|
return init_result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +280,7 @@ struct System::Impl {
|
||||||
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
|
||||||
if (load_result != Loader::ResultStatus::Success) {
|
if (load_result != Loader::ResultStatus::Success) {
|
||||||
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
|
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", load_result);
|
||||||
Shutdown();
|
ShutdownMainProcess();
|
||||||
|
|
||||||
return static_cast<SystemResultStatus>(
|
return static_cast<SystemResultStatus>(
|
||||||
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
|
static_cast<u32>(SystemResultStatus::ErrorLoader) + static_cast<u32>(load_result));
|
||||||
|
@ -335,7 +339,7 @@ struct System::Impl {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void ShutdownMainProcess() {
|
||||||
SetShuttingDown(true);
|
SetShuttingDown(true);
|
||||||
|
|
||||||
// Log last frame performance stats if game was loded
|
// Log last frame performance stats if game was loded
|
||||||
|
@ -369,7 +373,7 @@ struct System::Impl {
|
||||||
cheat_engine.reset();
|
cheat_engine.reset();
|
||||||
telemetry_session.reset();
|
telemetry_session.reset();
|
||||||
time_manager.Shutdown();
|
time_manager.Shutdown();
|
||||||
core_timing.Shutdown();
|
core_timing.ClearPendingEvents();
|
||||||
app_loader.reset();
|
app_loader.reset();
|
||||||
audio_core.reset();
|
audio_core.reset();
|
||||||
gpu_core.reset();
|
gpu_core.reset();
|
||||||
|
@ -377,7 +381,6 @@ struct System::Impl {
|
||||||
perf_stats.reset();
|
perf_stats.reset();
|
||||||
kernel.Shutdown();
|
kernel.Shutdown();
|
||||||
memory.Reset();
|
memory.Reset();
|
||||||
applet_manager.ClearAll();
|
|
||||||
|
|
||||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||||
Network::GameInfo game_info{};
|
Network::GameInfo game_info{};
|
||||||
|
@ -520,6 +523,10 @@ const CpuManager& System::GetCpuManager() const {
|
||||||
return impl->cpu_manager;
|
return impl->cpu_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void System::Initialize() {
|
||||||
|
impl->Initialize(*this);
|
||||||
|
}
|
||||||
|
|
||||||
SystemResultStatus System::Run() {
|
SystemResultStatus System::Run() {
|
||||||
return impl->Run();
|
return impl->Run();
|
||||||
}
|
}
|
||||||
|
@ -540,8 +547,8 @@ void System::InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size) {
|
||||||
impl->kernel.InvalidateCpuInstructionCacheRange(addr, size);
|
impl->kernel.InvalidateCpuInstructionCacheRange(addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void System::Shutdown() {
|
void System::ShutdownMainProcess() {
|
||||||
impl->Shutdown();
|
impl->ShutdownMainProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool System::IsShuttingDown() const {
|
bool System::IsShuttingDown() const {
|
||||||
|
|
|
@ -142,6 +142,12 @@ public:
|
||||||
System(System&&) = delete;
|
System(System&&) = delete;
|
||||||
System& operator=(System&&) = delete;
|
System& operator=(System&&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the system
|
||||||
|
* This function will initialize core functionaility used for system emulation
|
||||||
|
*/
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the OS and Application
|
* Run the OS and Application
|
||||||
* This function will start emulation and run the relevant devices
|
* This function will start emulation and run the relevant devices
|
||||||
|
@ -166,8 +172,8 @@ public:
|
||||||
|
|
||||||
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
|
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
|
||||||
|
|
||||||
/// Shutdown the emulated system.
|
/// Shutdown the main emulated process.
|
||||||
void Shutdown();
|
void ShutdownMainProcess();
|
||||||
|
|
||||||
/// Check if the core is shutting down.
|
/// Check if the core is shutting down.
|
||||||
[[nodiscard]] bool IsShuttingDown() const;
|
[[nodiscard]] bool IsShuttingDown() const;
|
||||||
|
|
|
@ -40,7 +40,17 @@ struct CoreTiming::Event {
|
||||||
CoreTiming::CoreTiming()
|
CoreTiming::CoreTiming()
|
||||||
: clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
|
: clock{Common::CreateBestMatchingClock(Hardware::BASE_CLOCK_RATE, Hardware::CNTFREQ)} {}
|
||||||
|
|
||||||
CoreTiming::~CoreTiming() = default;
|
CoreTiming::~CoreTiming() {
|
||||||
|
paused = true;
|
||||||
|
shutting_down = true;
|
||||||
|
pause_event.Set();
|
||||||
|
event.Set();
|
||||||
|
if (timer_thread) {
|
||||||
|
timer_thread->join();
|
||||||
|
}
|
||||||
|
timer_thread.reset();
|
||||||
|
has_started = false;
|
||||||
|
}
|
||||||
|
|
||||||
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
void CoreTiming::ThreadEntry(CoreTiming& instance) {
|
||||||
constexpr char name[] = "HostTiming";
|
constexpr char name[] = "HostTiming";
|
||||||
|
@ -65,17 +75,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::Shutdown() {
|
void CoreTiming::ClearPendingEvents() {
|
||||||
paused = true;
|
event_queue.clear();
|
||||||
shutting_down = true;
|
|
||||||
pause_event.Set();
|
|
||||||
event.Set();
|
|
||||||
if (timer_thread) {
|
|
||||||
timer_thread->join();
|
|
||||||
}
|
|
||||||
ClearPendingEvents();
|
|
||||||
timer_thread.reset();
|
|
||||||
has_started = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::Pause(bool is_paused) {
|
void CoreTiming::Pause(bool is_paused) {
|
||||||
|
@ -196,10 +197,6 @@ u64 CoreTiming::GetClockTicks() const {
|
||||||
return CpuCyclesToClockCycles(ticks);
|
return CpuCyclesToClockCycles(ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CoreTiming::ClearPendingEvents() {
|
|
||||||
event_queue.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||||
std::scoped_lock lock{basic_lock};
|
std::scoped_lock lock{basic_lock};
|
||||||
|
|
||||||
|
|
|
@ -61,8 +61,8 @@ public:
|
||||||
/// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
|
/// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
|
||||||
void Initialize(std::function<void()>&& on_thread_init_);
|
void Initialize(std::function<void()>&& on_thread_init_);
|
||||||
|
|
||||||
/// Tears down all timing related functionality.
|
/// Clear all pending events. This should ONLY be done on exit.
|
||||||
void Shutdown();
|
void ClearPendingEvents();
|
||||||
|
|
||||||
/// Sets if emulation is multicore or single core, must be set before Initialize
|
/// Sets if emulation is multicore or single core, must be set before Initialize
|
||||||
void SetMulticore(bool is_multicore_) {
|
void SetMulticore(bool is_multicore_) {
|
||||||
|
@ -136,9 +136,6 @@ public:
|
||||||
private:
|
private:
|
||||||
struct Event;
|
struct Event;
|
||||||
|
|
||||||
/// Clear all pending events. This should ONLY be done on exit.
|
|
||||||
void ClearPendingEvents();
|
|
||||||
|
|
||||||
static void ThreadEntry(CoreTiming& instance);
|
static void ThreadEntry(CoreTiming& instance);
|
||||||
void ThreadLoop();
|
void ThreadLoop();
|
||||||
|
|
||||||
|
|
|
@ -40,9 +40,6 @@ struct ScopeInit final {
|
||||||
core_timing.SetMulticore(true);
|
core_timing.SetMulticore(true);
|
||||||
core_timing.Initialize([]() {});
|
core_timing.Initialize([]() {});
|
||||||
}
|
}
|
||||||
~ScopeInit() {
|
|
||||||
core_timing.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::Timing::CoreTiming core_timing;
|
Core::Timing::CoreTiming core_timing;
|
||||||
};
|
};
|
||||||
|
|
|
@ -120,8 +120,8 @@ void EmuThread::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown the core emulation
|
// Shutdown the main emulated process
|
||||||
system.Shutdown();
|
system.ShutdownMainProcess();
|
||||||
|
|
||||||
#if MICROPROFILE_ENABLED
|
#if MICROPROFILE_ENABLED
|
||||||
MicroProfileOnThreadExit();
|
MicroProfileOnThreadExit();
|
||||||
|
|
|
@ -294,6 +294,7 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
SetupSigInterrupts();
|
SetupSigInterrupts();
|
||||||
#endif
|
#endif
|
||||||
|
system->Initialize();
|
||||||
|
|
||||||
Common::Log::Initialize();
|
Common::Log::Initialize();
|
||||||
LoadTranslation();
|
LoadTranslation();
|
||||||
|
|
|
@ -302,6 +302,8 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Core::System system{};
|
Core::System system{};
|
||||||
|
system.Initialize();
|
||||||
|
|
||||||
InputCommon::InputSubsystem input_subsystem{};
|
InputCommon::InputSubsystem input_subsystem{};
|
||||||
|
|
||||||
// Apply the command line arguments
|
// Apply the command line arguments
|
||||||
|
@ -392,7 +394,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
system.DetachDebugger();
|
system.DetachDebugger();
|
||||||
void(system.Pause());
|
void(system.Pause());
|
||||||
system.Shutdown();
|
system.ShutdownMainProcess();
|
||||||
|
|
||||||
detached_tasks.WaitForAllTasks();
|
detached_tasks.WaitForAllTasks();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Reference in New Issue