core: Partially persist emulation state across game boots.
This commit is contained in:
		| @@ -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
	
	Block a user