diff --git a/src/core/core.cpp b/src/core/core.cpp index 0f0eb885ad..2ca9c0be54 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -158,6 +158,8 @@ struct System::Impl { kernel.SetMulticore(is_multicore); cpu_manager.SetMulticore(is_multicore); cpu_manager.SetAsyncGpu(is_async_gpu); + core_timing.SetMulticore(is_multicore); + cpu_manager.SetRenderWindow(emu_window); core_timing.Initialize([&system]() { system.RegisterHostThread(); }); kernel.Initialize(); diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 3438f79cee..189d4aa34d 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -55,7 +55,9 @@ void CoreTiming::Initialize(std::function&& on_thread_init_) { event_fifo_id = 0; const auto empty_timed_callback = [](u64, s64) {}; ev_lost = CreateEvent("_lost_event", empty_timed_callback); - timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + if (is_multicore) { + timer_thread = std::make_unique(ThreadEntry, std::ref(*this)); + } } void CoreTiming::Shutdown() { @@ -63,7 +65,9 @@ void CoreTiming::Shutdown() { shutting_down = true; pause_event.Set(); event.Set(); - timer_thread->join(); + if (timer_thread) { + timer_thread->join(); + } ClearPendingEvents(); timer_thread.reset(); has_started = false; @@ -78,12 +82,14 @@ void CoreTiming::SyncPause(bool is_paused) { return; } Pause(is_paused); - if (!is_paused) { - pause_event.Set(); + if (timer_thread) { + if (!is_paused) { + pause_event.Set(); + } + event.Set(); + while (paused_set != is_paused) + ; } - event.Set(); - while (paused_set != is_paused) - ; } bool CoreTiming::IsRunning() const { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 032eb08aad..03f9a5c764 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -67,6 +67,11 @@ public: /// Tears down all timing related functionality. void Shutdown(); + /// Sets if emulation is multicore or single core, must be set before Initialize + void SetMulticore(bool is_multicore) { + this->is_multicore = is_multicore; + } + /// Pauses/Unpauses the execution of the timer thread. void Pause(bool is_paused); @@ -147,6 +152,8 @@ private: std::atomic has_started{}; std::function on_thread_init{}; + bool is_multicore{}; + std::array, Core::Hardware::NUM_CPU_CORES> ticks_count{}; }; diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index d7bd162bc5..2aea95a257 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -242,8 +242,11 @@ void CpuManager::SingleCoreRunGuestLoop() { break; } } - physical_core.ClearExclusive(); system.ExitDynarmicProfile(); + thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + thread->SetPhantomMode(false); + physical_core.ClearExclusive(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); scheduler.TryDoContextSwitch(); @@ -255,6 +258,7 @@ void CpuManager::SingleCoreRunIdleThread() { while (true) { auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(); + idle_count++; auto& scheduler = physical_core.Scheduler(); scheduler.TryDoContextSwitch(); } @@ -280,15 +284,24 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore() { preemption_count = 0; std::size_t old_core = current_core; - current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); auto& scheduler = system.Kernel().Scheduler(old_core); Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + if (idle_count >= 4) { + current_thread->SetPhantomMode(true); + system.CoreTiming().Advance(); + current_thread->SetPhantomMode(false); + } + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); scheduler.Unload(); auto& next_scheduler = system.Kernel().Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); /// May have changed scheduler auto& current_scheduler = system.Kernel().Scheduler(current_core); current_scheduler.Reload(); + auto* currrent_thread2 = current_scheduler.GetCurrentThread(); + if (!currrent_thread2->IsIdleThread()) { + idle_count = 0; + } } void CpuManager::SingleCorePause(bool paused) { diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 37cef2b122..e6b8612f0e 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h @@ -104,6 +104,7 @@ private: bool is_multicore{}; std::atomic current_core{}; std::size_t preemption_count{}; + std::size_t idle_count{}; static constexpr std::size_t max_cycle_runs = 5; Core::Frontend::EmuWindow* render_window; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d2f5f9bf23..a19cd7a1ff 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -303,7 +303,7 @@ struct KernelCore::Impl { } const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); const Kernel::Thread* current = sched.GetCurrentThread(); - if (current != nullptr) { + if (current != nullptr && !current->IsPhantomMode()) { result.guest_handle = current->GetGlobalHandle(); } else { result.guest_handle = InvalidHandle; diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f42d7bd136..f998890c4b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -597,6 +597,14 @@ public: is_continuous_on_svc = is_continuous; } + bool IsPhantomMode() const { + return is_phantom_mode; + } + + void SetPhantomMode(bool phantom) { + is_phantom_mode = phantom; + } + private: friend class GlobalScheduler; friend class Scheduler; @@ -699,6 +707,7 @@ private: bool is_continuous_on_svc = false; bool will_be_terminated = false; + bool is_phantom_mode = false; bool was_running = false;