diff --git a/src/core/core.cpp b/src/core/core.cpp index 59b8768e7..ecc5f541d 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include "audio_core/audio_core.h" #include "common/logging/log.h" @@ -26,6 +27,11 @@ namespace Core { /*static*/ System System::s_instance; +std::thread CpuThread; +std::atomic CpuThreadRunning; +std::atomic RunCPU; +std::atomic TightLoop; + System::ResultStatus System::RunLoop(int tight_loop) { status = ResultStatus::Success; if (!cpu_core) { @@ -47,6 +53,15 @@ System::ResultStatus System::RunLoop(int tight_loop) { } } + TightLoop += tight_loop; + RunCPU = true; + + HW::Update(); + + return status; +} + +void System::RunLoopInner() { // 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) { @@ -55,13 +70,20 @@ System::ResultStatus System::RunLoop(int tight_loop) { CoreTiming::Advance(); PrepareReschedule(); } else { - cpu_core->Run(tight_loop); + cpu_core->Run(TightLoop); } - HW::Update(); + TightLoop = 0; Reschedule(); +} - return status; +void System::RunLoopInThread() { + while (CpuThreadRunning) { + if (RunCPU) { + RunLoopInner(); + RunCPU = false; + } + } } System::ResultStatus System::SingleStep() { @@ -164,10 +186,16 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { GetAndResetPerfStats(); perf_stats.BeginSystemFrame(); + CpuThreadRunning = true; + CpuThread = std::thread(&System::RunLoopInThread, this); + return ResultStatus::Success; } void System::Shutdown() { + CpuThreadRunning = false; + CpuThread.join(); + // Log last frame performance stats auto perf_results = GetAndResetPerfStats(); Telemetry().AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed", diff --git a/src/core/core.h b/src/core/core.h index 9805cc694..2022df6ee 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -54,6 +54,8 @@ public: * @return Result status, indicating whethor or not the operation succeeded. */ ResultStatus RunLoop(int tight_loop = 1000); + void RunLoopInThread(); + void RunLoopInner(); /** * Step the CPU one instruction diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 83ad9d898..d5857349d 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -28,6 +29,14 @@ namespace GPU { Regs g_regs; +struct WriteThing { + u32 addr; + u32 data; +}; + +std::atomic VBlankGo = false; +boost::lockfree::queue WriteQueue(128); + /// 268MHz CPU clocks / 60Hz frames per second const u64 frame_ticks = static_cast(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE); /// Event id for CoreTiming @@ -392,6 +401,11 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { template inline void Write(u32 addr, const T data) { + WriteQueue.push(WriteThing{addr, static_cast(data)}); +} + +template +inline void WriteE(u32 addr, const T data) { addr -= HW::VADDR_GPU; u32 index = addr / 4; @@ -515,8 +529,6 @@ template void Write(u32 addr, const u8 data); /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { - VideoCore::g_renderer->SwapBuffers(); - // Signal to GSP that GPU interrupt has occurred // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub // screen, or if both use the same interrupts and these two instead determine the @@ -524,6 +536,7 @@ static void VBlankCallback(u64 userdata, int cycles_late) { // two different intervals. Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC0); Service::GSP::SignalInterrupt(Service::GSP::InterruptId::PDC1); + VBlankGo = true; // Reschedule recurrent event CoreTiming::ScheduleEvent(frame_ticks - cycles_late, vblank_event); @@ -565,6 +578,17 @@ void Init() { LOG_DEBUG(HW_GPU, "initialized OK"); } +void Update() { + WriteThing thing; + while (WriteQueue.pop(thing)) { + WriteE(thing.addr, thing.data); + } + if (VBlankGo) { + VideoCore::g_renderer->SwapBuffers(); + VBlankGo = false; + } +} + /// Shutdown hardware void Shutdown() { LOG_DEBUG(HW_GPU, "shutdown OK"); diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index e3d0a0e08..1e2758c35 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -328,6 +328,8 @@ void Write(u32 addr, const T data); /// Initialize hardware void Init(); +void Update(); + /// Shutdown hardware void Shutdown(); diff --git a/src/core/hw/hw.cpp b/src/core/hw/hw.cpp index 8499f2ce6..4e4915ab9 100644 --- a/src/core/hw/hw.cpp +++ b/src/core/hw/hw.cpp @@ -82,7 +82,9 @@ template void Write(u32 addr, const u16 data); template void Write(u32 addr, const u8 data); /// Update hardware -void Update() {} +void Update() { + GPU::Update(); +} /// Initialize hardware void Init() {