diff options
Diffstat (limited to 'src/core/cpu_manager.cpp')
-rw-r--r-- | src/core/cpu_manager.cpp | 320 |
1 files changed, 89 insertions, 231 deletions
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index cbcc54891..0dd4c2196 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -1,6 +1,5 @@ -// Copyright 2018 yuzu emulator team -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #include "common/fiber.h" #include "common/microprofile.h" @@ -9,6 +8,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/cpu_manager.h" +#include "core/hle/kernel/k_interrupt_manager.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" @@ -22,75 +22,51 @@ CpuManager::~CpuManager() = default; void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core) { - cpu_manager.RunThread(stop_token, core); + cpu_manager.RunThread(core); } void CpuManager::Initialize() { - running_mode = true; - if (is_multicore) { - for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { - core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); - } - } else { - core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); + num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1; + gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1); + + for (std::size_t core = 0; core < num_cores; core++) { + core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); } } void CpuManager::Shutdown() { - running_mode = false; - Pause(false); -} - -std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { - return GuestThreadFunction; -} - -std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() { - return IdleThreadFunction; -} - -std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() { - return SuspendThreadFunction; -} - -void CpuManager::GuestThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestThread(); - } else { - cpu_manager->SingleCoreRunGuestThread(); + for (std::size_t core = 0; core < num_cores; core++) { + if (core_data[core].host_thread.joinable()) { + core_data[core].host_thread.join(); + } } } -void CpuManager::GuestRewindFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunGuestLoop(); +void CpuManager::GuestThreadFunction() { + if (is_multicore) { + MultiCoreRunGuestThread(); } else { - cpu_manager->SingleCoreRunGuestLoop(); + SingleCoreRunGuestThread(); } } -void CpuManager::IdleThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunIdleThread(); +void CpuManager::IdleThreadFunction() { + if (is_multicore) { + MultiCoreRunIdleThread(); } else { - cpu_manager->SingleCoreRunIdleThread(); + SingleCoreRunIdleThread(); } } -void CpuManager::SuspendThreadFunction(void* cpu_manager_) { - CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_); - if (cpu_manager->is_multicore) { - cpu_manager->MultiCoreRunSuspendThread(); - } else { - cpu_manager->SingleCoreRunSuspendThread(); - } +void CpuManager::ShutdownThreadFunction() { + ShutdownThread(); } -void* CpuManager::GetStartFuncParamater() { - return static_cast<void*>(this); +void CpuManager::HandleInterrupt() { + auto& kernel = system.Kernel(); + auto core_index = kernel.CurrentPhysicalCoreIndex(); + + Kernel::KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_index)); } /////////////////////////////////////////////////////////////////////////////// @@ -98,90 +74,37 @@ void* CpuManager::GetStartFuncParamater() { /////////////////////////////////////////////////////////////////////////////// void CpuManager::MultiCoreRunGuestThread() { + // Similar to UserModeThreadStarter in HOS auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); - MultiCoreRunGuestLoop(); -} - -void CpuManager::MultiCoreRunGuestLoop() { - auto& kernel = system.Kernel(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); - system.EnterDynarmicProfile(); while (!physical_core->IsInterrupted()) { physical_core->Run(); physical_core = &kernel.CurrentPhysicalCore(); } - system.ExitDynarmicProfile(); - { - Kernel::KScopedDisableDispatch dd(kernel); - physical_core->ArmInterface().ClearExclusiveState(); - } + + HandleInterrupt(); } } void CpuManager::MultiCoreRunIdleThread() { - auto& kernel = system.Kernel(); - while (true) { - Kernel::KScopedDisableDispatch dd(kernel); - kernel.CurrentPhysicalCore().Idle(); - } -} + // Not accurate to HOS. Remove this entire method when singlecore is removed. + // See notes in KScheduler::ScheduleImpl for more information about why this + // is inaccurate. -void CpuManager::MultiCoreRunSuspendThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - while (true) { - auto core = kernel.CurrentPhysicalCoreIndex(); - auto& scheduler = *kernel.CurrentScheduler(); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); - ASSERT(scheduler.ContextSwitchPending()); - ASSERT(core == kernel.CurrentPhysicalCoreIndex()); - scheduler.RescheduleCurrentCore(); - } -} -void CpuManager::MultiCorePause(bool paused) { - if (!paused) { - bool all_not_barrier = false; - while (!all_not_barrier) { - all_not_barrier = true; - for (const auto& data : core_data) { - all_not_barrier &= !data.is_running.load() && data.initialized.load(); - } - } - for (auto& data : core_data) { - data.enter_barrier->Set(); - } - if (paused_state.load()) { - bool all_barrier = false; - while (!all_barrier) { - all_barrier = true; - for (const auto& data : core_data) { - all_barrier &= data.is_paused.load() && data.initialized.load(); - } - } - for (auto& data : core_data) { - data.exit_barrier->Set(); - } - } - } else { - /// Wait until all cores are paused. - bool all_barrier = false; - while (!all_barrier) { - all_barrier = true; - for (const auto& data : core_data) { - all_barrier &= data.is_paused.load() && data.initialized.load(); - } + while (true) { + auto& physical_core = kernel.CurrentPhysicalCore(); + if (!physical_core.IsInterrupted()) { + physical_core.Idle(); } - /// Don't release the barrier + + HandleInterrupt(); } - paused_state = paused; } /////////////////////////////////////////////////////////////////////////////// @@ -191,175 +114,110 @@ void CpuManager::MultiCorePause(bool paused) { void CpuManager::SingleCoreRunGuestThread() { auto& kernel = system.Kernel(); kernel.CurrentScheduler()->OnThreadStart(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - auto& host_context = thread->GetHostContext(); - host_context->SetRewindPoint(GuestRewindFunction, this); - SingleCoreRunGuestLoop(); -} -void CpuManager::SingleCoreRunGuestLoop() { - auto& kernel = system.Kernel(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); - system.EnterDynarmicProfile(); if (!physical_core->IsInterrupted()) { physical_core->Run(); physical_core = &kernel.CurrentPhysicalCore(); } - system.ExitDynarmicProfile(); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); kernel.SetIsPhantomModeForSingleCore(false); - physical_core->ArmInterface().ClearExclusiveState(); + PreemptSingleCore(); - auto& scheduler = kernel.Scheduler(current_core); - scheduler.RescheduleCurrentCore(); + HandleInterrupt(); } } void CpuManager::SingleCoreRunIdleThread() { auto& kernel = system.Kernel(); + kernel.CurrentScheduler()->OnThreadStart(); + while (true) { - auto& physical_core = kernel.CurrentPhysicalCore(); PreemptSingleCore(false); system.CoreTiming().AddTicks(1000U); idle_count++; - auto& scheduler = physical_core.Scheduler(); - scheduler.RescheduleCurrentCore(); + HandleInterrupt(); } } -void CpuManager::SingleCoreRunSuspendThread() { +void CpuManager::PreemptSingleCore(bool from_running_environment) { auto& kernel = system.Kernel(); - kernel.CurrentScheduler()->OnThreadStart(); - while (true) { - auto core = kernel.GetCurrentHostThreadID(); - auto& scheduler = *kernel.CurrentScheduler(); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[0].host_context); - ASSERT(scheduler.ContextSwitchPending()); - ASSERT(core == kernel.GetCurrentHostThreadID()); - scheduler.RescheduleCurrentCore(); - } -} -void CpuManager::PreemptSingleCore(bool from_running_enviroment) { - { - auto& kernel = system.Kernel(); - auto& scheduler = kernel.Scheduler(current_core); - Kernel::KThread* current_thread = scheduler.GetCurrentThread(); - if (idle_count >= 4 || from_running_enviroment) { - if (!from_running_enviroment) { - system.CoreTiming().Idle(); - idle_count = 0; - } - kernel.SetIsPhantomModeForSingleCore(true); - system.CoreTiming().Advance(); - kernel.SetIsPhantomModeForSingleCore(false); + if (idle_count >= 4 || from_running_environment) { + if (!from_running_environment) { + system.CoreTiming().Idle(); + idle_count = 0; } - current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); - system.CoreTiming().ResetTicks(); - scheduler.Unload(scheduler.GetCurrentThread()); - - auto& next_scheduler = kernel.Scheduler(current_core); - Common::Fiber::YieldTo(current_thread->GetHostContext(), *next_scheduler.ControlContext()); + kernel.SetIsPhantomModeForSingleCore(true); + system.CoreTiming().Advance(); + kernel.SetIsPhantomModeForSingleCore(false); } + current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); + system.CoreTiming().ResetTicks(); + kernel.Scheduler(current_core).PreemptSingleCore(); - // May have changed scheduler - { - auto& scheduler = system.Kernel().Scheduler(current_core); - scheduler.Reload(scheduler.GetCurrentThread()); - if (!scheduler.IsIdle()) { - idle_count = 0; - } + // We've now been scheduled again, and we may have exchanged schedulers. + // Reload the scheduler in case it's different. + if (!kernel.Scheduler(current_core).IsIdle()) { + idle_count = 0; } } -void CpuManager::SingleCorePause(bool paused) { - if (!paused) { - bool all_not_barrier = false; - while (!all_not_barrier) { - all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load(); - } - core_data[0].enter_barrier->Set(); - if (paused_state.load()) { - bool all_barrier = false; - while (!all_barrier) { - all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); - } - core_data[0].exit_barrier->Set(); - } - } else { - /// Wait until all cores are paused. - bool all_barrier = false; - while (!all_barrier) { - all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load(); - } - /// Don't release the barrier - } - paused_state = paused; +void CpuManager::GuestActivate() { + // Similar to the HorizonKernelMain callback in HOS + auto& kernel = system.Kernel(); + auto* scheduler = kernel.CurrentScheduler(); + + scheduler->Activate(); + UNREACHABLE(); } -void CpuManager::Pause(bool paused) { - if (is_multicore) { - MultiCorePause(paused); - } else { - SingleCorePause(paused); - } +void CpuManager::ShutdownThread() { + auto& kernel = system.Kernel(); + auto* thread = kernel.GetCurrentEmuThread(); + auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0; + + Common::Fiber::YieldTo(thread->GetHostContext(), *core_data[core].host_context); + UNREACHABLE(); } -void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { +void CpuManager::RunThread(std::size_t core) { /// Initialization system.RegisterCoreThread(core); std::string name; if (is_multicore) { - name = "yuzu:CPUCore_" + std::to_string(core); + name = "CPUCore_" + std::to_string(core); } else { - name = "yuzu:CPUThread"; + name = "CPUThread"; } MicroProfileOnThreadCreate(name.c_str()); Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); auto& data = core_data[core]; - data.enter_barrier = std::make_unique<Common::Event>(); - data.exit_barrier = std::make_unique<Common::Event>(); data.host_context = Common::Fiber::ThreadToFiber(); - data.is_running = false; - data.initialized = true; - const bool sc_sync = !is_async_gpu && !is_multicore; - bool sc_sync_first_use = sc_sync; // Cleanup SCOPE_EXIT({ data.host_context->Exit(); - data.enter_barrier.reset(); - data.exit_barrier.reset(); - data.initialized = false; MicroProfileOnThreadExit(); }); - /// Running - while (running_mode) { - data.is_running = false; - data.enter_barrier->Wait(); - if (sc_sync_first_use) { - system.GPU().ObtainContext(); - sc_sync_first_use = false; - } + // Running + gpu_barrier->Sync(); - // Emulation was stopped - if (stop_token.stop_requested()) { - return; - } - - auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); - data.is_running = true; - Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext()); - data.is_running = false; - data.is_paused = true; - data.exit_barrier->Wait(); - data.is_paused = false; + if (!is_async_gpu && !is_multicore) { + system.GPU().ObtainContext(); } + + auto& kernel = system.Kernel(); + auto& scheduler = *kernel.CurrentScheduler(); + auto* thread = scheduler.GetSchedulerCurrentThread(); + Kernel::SetCurrentThread(kernel, thread); + + Common::Fiber::YieldTo(data.host_context, *thread->GetHostContext()); } } // namespace Core |