From 99a1a0133ddb4f575f49ae740a5cfa1e41d63ba7 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 27 Jan 2024 01:58:21 +0200 Subject: [PATCH] Initial idle skip implementation --- include/cpu_dynarmic.hpp | 2 ++ include/kernel/kernel.hpp | 1 + include/kernel/kernel_types.hpp | 1 + src/core/kernel/events.cpp | 2 ++ src/core/kernel/threads.cpp | 35 ++++++++++++++++++++++++++------- 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/cpu_dynarmic.hpp b/include/cpu_dynarmic.hpp index 97acddd0..048bc62a 100644 --- a/include/cpu_dynarmic.hpp +++ b/include/cpu_dynarmic.hpp @@ -179,6 +179,8 @@ class CPU { return scheduler; } + void addTicks(u64 ticks) { env.AddTicks(ticks); } + void clearCache() { jit->ClearCache(); } void runFrame(); }; \ No newline at end of file diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index d35d9031..e78a588a 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -95,6 +95,7 @@ public: void releaseMutex(Mutex* moo); void cancelTimer(Timer* timer); void signalTimer(Handle timerHandle, Timer* timer); + u64 getWakeupTick(s64 ns); // Wake up the thread with the highest priority out of all threads in the waitlist // Returns the index of the woken up thread diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index 79684e17..2c357037 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -123,6 +123,7 @@ struct Thread { bool waitAll; // For WaitSynchronizationN: The "out" pointer u32 outPointer; + u64 wakeupTick; // Thread context used for switching between threads std::array gprs; diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 7da4788e..b5b1c06e 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -127,6 +127,7 @@ void Kernel::waitSynchronization1() { t.waitList.resize(1); t.status = ThreadStatus::WaitSync1; t.sleepTick = cpu.getTicks(); + t.wakeupTick = getWakeupTick(ns); t.waitingNanoseconds = ns; t.waitList[0] = handle; @@ -222,6 +223,7 @@ void Kernel::waitSynchronizationN() { t.outPointer = outPointer; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); + t.wakeupTick = getWakeupTick(ns); for (s32 i = 0; i < handleCount; i++) { t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 239a6617..cea48bd2 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -52,14 +52,8 @@ bool Kernel::canThreadRun(const Thread& t) { return true; } else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) { - const u64 elapsedTicks = cpu.getTicks() - t.sleepTick; - - constexpr double ticksPerSec = double(CPU::ticksPerSec); - constexpr double nsPerTick = ticksPerSec / 1000000000.0; - // TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All} - const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick); - return elapsedNs >= t.waitingNanoseconds; + return cpu.getTicks() >= t.wakeupTick; } // Handle timeouts and stuff here @@ -82,6 +76,14 @@ std::optional Kernel::getNextThread() { return std::nullopt; } +u64 Kernel::getWakeupTick(s64 ns) { + if (ns == -1) { + return std::numeric_limits::max(); + } + + return cpu.getTicks() + Scheduler::nsToCycles(ns); +} + // See if there is a higher priority, ready thread and switch to that void Kernel::rescheduleThreads() { Thread& current = threads[currentThreadIndex]; // Current running thread @@ -368,6 +370,24 @@ void Kernel::sleepThread(s64 ns) { if (index != idleThreadIndex) { switchThread(index); } + } else { + if (currentThreadIndex == idleThreadIndex) { + const Scheduler& scheduler = cpu.getScheduler(); + u64 timestamp = scheduler.nextTimestamp; + + for (auto i : threadIndices) { + const Thread& t = threads[i]; + if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAny || + t.status == ThreadStatus::WaitSyncAll) { + timestamp = std::min(timestamp, t.wakeupTick); + } + } + + if (timestamp > scheduler.currentTimestamp) { + u64 idleCycles = timestamp - scheduler.currentTimestamp; + cpu.addTicks(idleCycles); + } + } } } else { // If we're sleeping for >= 0 ns Thread& t = threads[currentThreadIndex]; @@ -375,6 +395,7 @@ void Kernel::sleepThread(s64 ns) { t.status = ThreadStatus::WaitSleep; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); + t.wakeupTick = getWakeupTick(ns); requireReschedule(); }