diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/core.cpp | 30 | ||||
| -rw-r--r-- | src/core/core.h | 24 | ||||
| -rw-r--r-- | src/core/core_cpu.cpp | 25 | ||||
| -rw-r--r-- | src/core/core_cpu.h | 33 | 
4 files changed, 94 insertions, 18 deletions
| diff --git a/src/core/core.cpp b/src/core/core.cpp index 0af78c18c..066423f23 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -27,6 +27,13 @@ namespace Core {  System::~System() = default; +/// 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); +    } +} +  System::ResultStatus System::RunLoop(bool tight_loop) {      status = ResultStatus::Success; @@ -109,7 +116,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file  }  void System::PrepareReschedule() { -    cpu_cores[0]->PrepareReschedule(); +    CurrentCpuCore().PrepareReschedule();  }  PerfStats::Results System::GetAndResetPerfStats() { @@ -123,14 +130,13 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {      current_process = Kernel::Process::Create("main"); -    for (auto& cpu_core : cpu_cores) { -        cpu_core = std::make_unique<Cpu>(); +    cpu_barrier = std::make_shared<CpuBarrier>(); +    for (size_t index = 0; index < cpu_cores.size(); ++index) { +        cpu_cores[index] = std::make_shared<Cpu>(cpu_barrier, index);      }      gpu_core = std::make_unique<Tegra::GPU>(); -      telemetry_session = std::make_unique<Core::TelemetrySession>(); -      service_manager = std::make_shared<Service::SM::ServiceManager>();      HW::Init(); @@ -142,6 +148,14 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {          return ResultStatus::ErrorVideoCore;      } +    // 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]; +    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]; +    } +      NGLOG_DEBUG(Core, "Initialized OK");      // Reset counters and set time origin to current frame @@ -171,9 +185,15 @@ void System::Shutdown() {      telemetry_session.reset();      gpu_core.reset(); +    // Close all CPU/threading state +    thread_to_cpu.clear();      for (auto& cpu_core : cpu_cores) {          cpu_core.reset();      } +    for (auto& thread : cpu_core_threads) { +        thread->join(); +        thread.reset(); +    }      CoreTiming::Shutdown(); diff --git a/src/core/core.h b/src/core/core.h index 6e6cc7579..21a0b074b 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,6 +7,7 @@  #include <array>  #include <memory>  #include <string> +#include <thread>  #include "common/common_types.h"  #include "core/core_cpu.h"  #include "core/hle/kernel/kernel.h" @@ -112,7 +113,7 @@ public:       * @returns A reference to the emulated CPU.       */      ARM_Interface& CPU() { -        return cpu_cores[0]->CPU(); +        return CurrentCpuCore().CPU();      }      Tegra::GPU& GPU() { @@ -120,7 +121,7 @@ public:      }      Kernel::Scheduler& Scheduler() { -        return cpu_cores[0]->Scheduler(); +        return CurrentCpuCore().Scheduler();      }      Kernel::SharedPtr<Kernel::Process>& CurrentProcess() { @@ -157,6 +158,14 @@ public:      }  private: +    /// Returns the current CPU core based on the calling host thread +    Cpu& CurrentCpuCore() { +        const auto& search = thread_to_cpu.find(std::this_thread::get_id()); +        ASSERT(search != thread_to_cpu.end()); +        ASSERT(search->second); +        return *search->second; +    } +      /**       * Initialize the emulated system.       * @param emu_window Pointer to the host-system window used for video output and keyboard input. @@ -167,14 +176,12 @@ private:      /// AppLoader used to load the current executing application      std::unique_ptr<Loader::AppLoader> app_loader; - -    std::array<std::unique_ptr<Cpu>, 4> cpu_cores;      std::unique_ptr<Tegra::GPU> gpu_core;      std::shared_ptr<Tegra::DebugContext> debug_context;      Kernel::SharedPtr<Kernel::Process> current_process; - -    /// When true, signals that a reschedule should happen -    bool reschedule_pending{}; +    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;      /// Service manager      std::shared_ptr<Service::SM::ServiceManager> service_manager; @@ -186,6 +193,9 @@ private:      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& CPU() { diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index 81c0e212d..6bdfdd7df 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp @@ -2,6 +2,9 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. +#include <condition_variable> +#include <mutex> +  #include "common/logging/log.h"  #ifdef ARCHITECTURE_x86_64  #include "core/arm/dynarmic/arm_dynarmic.h" @@ -16,7 +19,9 @@  namespace Core { -Cpu::Cpu() { +Cpu::Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) +    : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { +      if (Settings::values.use_cpu_jit) {  #ifdef ARCHITECTURE_x86_64          arm_interface = std::make_shared<ARM_Dynarmic>(); @@ -32,15 +37,25 @@ Cpu::Cpu() {  }  void Cpu::RunLoop(bool tight_loop) { +    // Wait for all other CPU cores to complete the previous slice, such that they run in lock-step +    cpu_barrier->Rendezvous(); +      // If we don't have a currently active thread then don't execute instructions,      // instead advance to the next event and try to yield to the next thread      if (Kernel::GetCurrentThread() == nullptr) { -        NGLOG_TRACE(Core, "Idling"); -        CoreTiming::Idle(); -        CoreTiming::Advance(); +        NGLOG_TRACE(Core, "Core-{} idling", core_index); + +        if (IsMainCore()) { +            CoreTiming::Idle(); +            CoreTiming::Advance(); +        } +          PrepareReschedule();      } else { -        CoreTiming::Advance(); +        if (IsMainCore()) { +            CoreTiming::Advance(); +        } +          if (tight_loop) {              arm_interface->Run();          } else { diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 312db1655..e6ed698cc 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h @@ -4,7 +4,9 @@  #pragma once +#include <condition_variable>  #include <memory> +#include <mutex>  #include <string>  #include "common/common_types.h" @@ -16,9 +18,32 @@ class Scheduler;  namespace Core { +constexpr unsigned NUM_CPU_CORES{4}; + +class CpuBarrier { +public: +    void Rendezvous() { +        std::unique_lock<std::mutex> lock(mutex); + +        --cores_waiting; +        if (!cores_waiting) { +            cores_waiting = NUM_CPU_CORES; +            condition.notify_all(); +            return; +        } + +        condition.wait(lock); +    } + +private: +    unsigned cores_waiting{NUM_CPU_CORES}; +    std::mutex mutex; +    std::condition_variable condition; +}; +  class Cpu {  public: -    Cpu(); +    Cpu(std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index);      void RunLoop(bool tight_loop = true); @@ -34,13 +59,19 @@ public:          return *scheduler;      } +    bool IsMainCore() const { +        return core_index == 0; +    } +  private:      void Reschedule();      std::shared_ptr<ARM_Interface> arm_interface; +    std::shared_ptr<CpuBarrier> cpu_barrier;      std::unique_ptr<Kernel::Scheduler> scheduler;      bool reschedule_pending{}; +    size_t core_index;  };  } // namespace Core | 
