From 8dd5b990be4f0624d4b7fc17bfc85a171f3bbcc1 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 28 Jul 2023 19:49:11 +0300 Subject: [PATCH 01/13] Thread bonk part 1: Better rescheduling maybe --- include/kernel/kernel.hpp | 6 +++- src/core/kernel/events.cpp | 10 +++--- src/core/kernel/kernel.cpp | 7 ++++ src/core/kernel/threads.cpp | 67 ++++++++++++++++++------------------- 4 files changed, 48 insertions(+), 42 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 2db7cdda..23764dc0 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -52,6 +52,9 @@ class Kernel { // Top 8 bits are the major version, bottom 8 are the minor version u16 kernelVersion = 0; + // Shows whether a reschedule will be need + bool needReschedule = false; + Handle makeArbiter(); Handle makeProcess(u32 id); Handle makePort(const char* name); @@ -73,7 +76,6 @@ private: void switchThread(int newThreadIndex); void sortThreads(); std::optional getNextThread(); - void switchToNextThread(); void rescheduleThreads(); bool canThreadRun(const Thread& t); bool shouldWaitOnObject(KernelObject* object); @@ -168,6 +170,8 @@ public: void serviceSVC(u32 svc); void reset(); + void requireReschedule() { needReschedule = true; } + Handle makeObject(KernelObjectType type) { if (handleCounter > KernelHandles::Max) [[unlikely]] { Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index e117dd62..04a27408 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -48,7 +48,7 @@ bool Kernel::signalEvent(Handle handle) { // We must reschedule our threads if we signalled one. Some games such as FE: Awakening rely on this // If this does not happen, we can have phenomena such as a thread waiting up a higher priority thread, // and the higher priority thread just never running - rescheduleThreads(); + requireReschedule(); } return true; @@ -121,7 +121,6 @@ void Kernel::waitSynchronization1() { if (!shouldWaitOnObject(object)) { acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready regs[0] = Result::Success; - rescheduleThreads(); } else { // Timeout is 0, don't bother waiting, instantly timeout if (ns == 0) { @@ -141,7 +140,7 @@ void Kernel::waitSynchronization1() { // Add the current thread to the object's wait list object->getWaitlist() |= (1ull << currentThreadIndex); - switchToNextThread(); + requireReschedule(); } } @@ -204,14 +203,13 @@ void Kernel::waitSynchronizationN() { auto& t = threads[currentThreadIndex]; - // We only need to wait on one object. Easy...?! + // We only need to wait on one object. Easy. if (!waitAll) { // If there's ready objects, acquire the first one and return if (oneObjectReady) { regs[0] = Result::Success; regs[1] = firstReadyObjectIndex; // Return index of the acquired object acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object - rescheduleThreads(); return; } @@ -229,7 +227,7 @@ void Kernel::waitSynchronizationN() { waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist } - switchToNextThread(); + requireReschedule(); } else { Helpers::panic("WaitSynchronizatioN with waitAll"); } diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 8f3aeda0..a8cf40a1 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -61,6 +61,11 @@ void Kernel::serviceSVC(u32 svc) { case 0x3D: outputDebugString(); break; default: Helpers::panic("Unimplemented svc: %X @ %08X", svc, regs[15]); break; } + + if (needReschedule) { + needReschedule = false; + rescheduleThreads(); + } } void Kernel::setVersion(u8 major, u8 minor) { @@ -140,6 +145,8 @@ void Kernel::reset() { threadIndices.clear(); serviceManager.reset(); + needReschedule = false; + // Allocate handle #0 to a dummy object and make a main process object makeObject(KernelObjectType::Dummy); currentProcess = makeProcess(1); // Use ID = 1 for main process diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 587d5fc4..ae5deb06 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -82,32 +82,34 @@ std::optional Kernel::getNextThread() { return std::nullopt; } -void Kernel::switchToNextThread() { - std::optional newThreadIndex = getNextThread(); - - if (!newThreadIndex.has_value()) { - log("Kernel tried to switch to the next thread but none found. Switching to random thread\n"); - assert(aliveThreadCount != 0); - Helpers::panic("rpog"); - - int index; - do { - index = rand() % threadCount; - } while (threads[index].status == ThreadStatus::Dead); // TODO: Pray this doesn't hang - - switchThread(index); - } else { - switchThread(newThreadIndex.value()); - } -} - -// See if there;s a higher priority, ready thread and switch to that +// See if there is a higher priority, ready thread and switch to that void Kernel::rescheduleThreads() { - std::optional newThreadIndex = getNextThread(); + Thread& current = threads[currentThreadIndex]; // Current running thread + ThreadStatus currentStatus = current.status; // Its status - if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) { - threads[currentThreadIndex].status = ThreadStatus::Ready; + // If the current thread is running and hasn't gone to sleep or whatever, set it to Ready instead of Running + // Since rescheduleThreads will put it to wait and run another thread in the meantime + if (currentStatus == ThreadStatus::Running) { + currentStatus = ThreadStatus::Ready; + } + + current.status = ThreadStatus::Dead; // Temporarily mark it as dead so getNextThread will ignore it + std::optional newThreadIndex = getNextThread(); + current.status = currentStatus; // Restore old status + + // Case 1: Another thread can run (that is not the idle thread) + if (newThreadIndex.has_value() && newThreadIndex.value() != idleThreadIndex) { switchThread(newThreadIndex.value()); + } + + // Case 2: No other thread can run but this one can + else if (currentStatus == ThreadStatus::Running) { + switchThread(currentThreadIndex); + } + + // Case 3: No thread can run other than the idle thread + else { + switchThread(idleThreadIndex); } } @@ -194,7 +196,7 @@ void Kernel::releaseMutex(Mutex* moo) { moo->ownerThread = index; } - rescheduleThreads(); + requireReschedule(); } } @@ -210,7 +212,7 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) { t.status = ThreadStatus::WaitArbiter; t.waitingAddress = waitingAddress; - switchToNextThread(); + requireReschedule(); } // Acquires an object that is **ready to be acquired** without waiting on it @@ -339,19 +341,14 @@ void Kernel::sleepThread(s64 ns) { if (ns < 0) { Helpers::panic("Sleeping a thread for a negative amount of ns"); } else if (ns == 0) { // Used when we want to force a thread switch - std::optional newThreadIndex = getNextThread(); - // If there's no other thread waiting, don't bother yielding - if (newThreadIndex.has_value()) { - threads[currentThreadIndex].status = ThreadStatus::Ready; - switchThread(newThreadIndex.value()); - } + requireReschedule(); } else { // If we're sleeping for > 0 ns Thread& t = threads[currentThreadIndex]; t.status = ThreadStatus::WaitSleep; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); - switchToNextThread(); + requireReschedule(); } } @@ -374,7 +371,7 @@ void Kernel::createThread() { regs[0] = Result::Success; regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready); - rescheduleThreads(); + requireReschedule(); } // void SleepThread(s64 nanoseconds) @@ -448,7 +445,7 @@ void Kernel::setThreadPriority() { } } sortThreads(); - rescheduleThreads(); + requireReschedule(); } void Kernel::exitThread() { @@ -472,7 +469,7 @@ void Kernel::exitThread() { t.threadsWaitingForTermination = 0; // No other threads waiting } - switchToNextThread(); + requireReschedule(); } void Kernel::svcCreateMutex() { From e082625002684ec45ff4557deef4fbd794db7789 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Fri, 28 Jul 2023 20:05:26 +0300 Subject: [PATCH 02/13] Properly handle current thread in rescheduling --- src/core/kernel/threads.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index ae5deb06..8dd6f9e2 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -85,29 +85,21 @@ std::optional Kernel::getNextThread() { // See if there is a higher priority, ready thread and switch to that void Kernel::rescheduleThreads() { Thread& current = threads[currentThreadIndex]; // Current running thread - ThreadStatus currentStatus = current.status; // Its status // If the current thread is running and hasn't gone to sleep or whatever, set it to Ready instead of Running - // Since rescheduleThreads will put it to wait and run another thread in the meantime - if (currentStatus == ThreadStatus::Running) { - currentStatus = ThreadStatus::Ready; + // So that getNextThread will evaluate it properly + if (current.status == ThreadStatus::Running) { + current.status = ThreadStatus::Ready; } - - current.status = ThreadStatus::Dead; // Temporarily mark it as dead so getNextThread will ignore it + ThreadStatus currentStatus = current.status; std::optional newThreadIndex = getNextThread(); - current.status = currentStatus; // Restore old status - // Case 1: Another thread can run (that is not the idle thread) - if (newThreadIndex.has_value() && newThreadIndex.value() != idleThreadIndex) { + // Case 1: A thread can run + if (newThreadIndex.has_value()) { switchThread(newThreadIndex.value()); } - // Case 2: No other thread can run but this one can - else if (currentStatus == ThreadStatus::Running) { - switchThread(currentThreadIndex); - } - - // Case 3: No thread can run other than the idle thread + // Case 2: No other thread can run, straight to the idle thread else { switchThread(idleThreadIndex); } From 2eb84e053be47a179064da77e2053776ef790e43 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:03:46 +0300 Subject: [PATCH 03/13] [Kernel] Undo enormous, embarassing mistake no one must know about --- src/core/kernel/address_arbiter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/address_arbiter.cpp b/src/core/kernel/address_arbiter.cpp index 9fb9f7be..8c07b423 100644 --- a/src/core/kernel/address_arbiter.cpp +++ b/src/core/kernel/address_arbiter.cpp @@ -87,7 +87,7 @@ void Kernel::arbitrateAddress() { Helpers::panic("ArbitrateAddress: Unimplemented type %s", arbitrationTypeToString(type)); } - rescheduleThreads(); + requireReschedule(); } // Signal up to "threadCount" threads waiting on the arbiter indicated by "waitingAddress" From fbc10bf3daae0abf87acd3fb6fa9c775a8ff6880 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:10:06 +0300 Subject: [PATCH 04/13] Fix typo --- src/core/kernel/events.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 04a27408..13f2b8cb 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -229,6 +229,6 @@ void Kernel::waitSynchronizationN() { requireReschedule(); } else { - Helpers::panic("WaitSynchronizatioN with waitAll"); + Helpers::panic("WaitSynchronizationN with waitAll"); } } \ No newline at end of file From 7047bf6ec82a9c72b1212bf6df8d3e521a354ea6 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sat, 29 Jul 2023 15:37:34 +0300 Subject: [PATCH 05/13] [Mutex[ Request reschedule when acquiring mutex --- src/core/kernel/threads.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 8dd6f9e2..aff9028e 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -220,7 +220,13 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) { case KernelObjectType::Mutex: { Mutex* moo = object->getData(); - moo->locked = true; // Set locked to true, whether it's false or not because who cares + + // Only reschedule if we're acquiring the mutex for the first time + if (!moo->locked) { + moo->locked = true; + requireReschedule(); + } + // Increment lock count by 1. If a thread acquires a mootex multiple times, it needs to release it until count == 0 // For the mootex to be free. moo->lockCount++; From 65de2637aee039dbcf15adb6ca7e60b7b356d3c5 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 1 Aug 2023 23:45:43 +0300 Subject: [PATCH 06/13] More threading fixes --- src/core/kernel/events.cpp | 17 +++++------------ src/core/kernel/ports.cpp | 5 +++++ src/core/kernel/threads.cpp | 17 ++++++++++++++--- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 13f2b8cb..ca72add1 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -35,22 +35,15 @@ bool Kernel::signalEvent(Handle handle) { // Check if there's any thread waiting on this event if (event->waitlist != 0) { - // One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time + wakeupAllThreads(event->waitlist, handle); + event->waitlist = 0; // No threads waiting; + if (event->resetType == ResetType::OneShot) { - int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority - event->waitlist ^= (1ull << index); // Remove thread from waitlist event->fired = false; - } else { - wakeupAllThreads(event->waitlist, handle); - event->waitlist = 0; // No threads waiting; } - - // We must reschedule our threads if we signalled one. Some games such as FE: Awakening rely on this - // If this does not happen, we can have phenomena such as a thread waiting up a higher priority thread, - // and the higher priority thread just never running - requireReschedule(); } - + + rescheduleThreads(); return true; } diff --git a/src/core/kernel/ports.cpp b/src/core/kernel/ports.cpp index a7351fd0..84c8cc05 100644 --- a/src/core/kernel/ports.cpp +++ b/src/core/kernel/ports.cpp @@ -76,6 +76,11 @@ void Kernel::sendSyncRequest() { u32 messagePointer = getTLSPointer() + 0x80; // The message is stored starting at TLS+0x80 logSVC("SendSyncRequest(session handle = %X)\n", handle); + // Service calls via SendSyncRequest and file access needs to put the caller to sleep for a given amount of time + // To make sure that the other threads don't get starved. Various games rely on this (including Sonic Boom: Shattering Crystal it seems) + constexpr u64 syncRequestDelayNs = 39000; + sleepThread(syncRequestDelayNs); + // The sync request is being sent at a service rather than whatever port, so have the service manager intercept it if (KernelHandles::isServiceHandle(handle)) { // The service call might cause a reschedule and change threads. Hence, set r0 before executing the service call diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index aff9028e..8d4c477b 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -178,6 +178,7 @@ void Kernel::releaseMutex(Mutex* moo) { // If the lock count reached 0 then the thread no longer owns the mootex and it can be given to a new one if (moo->lockCount == 0) { moo->locked = false; + if (moo->waitlist != 0) { int index = wakeupOneThread(moo->waitlist, moo->handle); // Wake up one thread and get its index moo->waitlist ^= (1ull << index); // Remove thread from waitlist @@ -338,10 +339,20 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) { void Kernel::sleepThread(s64 ns) { if (ns < 0) { Helpers::panic("Sleeping a thread for a negative amount of ns"); - } else if (ns == 0) { // Used when we want to force a thread switch - requireReschedule(); - } else { // If we're sleeping for > 0 ns + } else if (ns == 0) { + // TODO: This is garbage, absolutely not getting merged Thread& t = threads[currentThreadIndex]; + + t.status = ThreadStatus::Dead; + auto nextThreadIndex = getNextThread(); + t.status = ThreadStatus::Ready; + + if (nextThreadIndex.has_value()) { + switchThread(nextThreadIndex.value()); + } + } else { // If we're sleeping for >= 0 ns + Thread& t = threads[currentThreadIndex]; + t.status = ThreadStatus::WaitSleep; t.waitingNanoseconds = ns; t.sleepTick = cpu.getTicks(); From 5aa2e3c1d286ffc35e4d03f13ebd9ee4c8db26c2 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 00:10:17 +0300 Subject: [PATCH 07/13] [Shader JIT] Add DPH --- .../PICA/dynapica/shader_rec_emitter_x64.hpp | 1 + .../PICA/dynapica/shader_rec_emitter_x64.cpp | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/PICA/dynapica/shader_rec_emitter_x64.hpp b/include/PICA/dynapica/shader_rec_emitter_x64.hpp index d22ed371..51e6a43d 100644 --- a/include/PICA/dynapica/shader_rec_emitter_x64.hpp +++ b/include/PICA/dynapica/shader_rec_emitter_x64.hpp @@ -91,6 +91,7 @@ class ShaderEmitter : public Xbyak::CodeGenerator { void recCMP(const PICAShader& shader, u32 instruction); void recDP3(const PICAShader& shader, u32 instruction); void recDP4(const PICAShader& shader, u32 instruction); + void recDPH(const PICAShader& shader, u32 instruction); void recEMIT(const PICAShader& shader, u32 instruction); void recEND(const PICAShader& shader, u32 instruction); void recEX2(const PICAShader& shader, u32 instruction); diff --git a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp index 13eb630e..523c41db 100644 --- a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp +++ b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp @@ -143,6 +143,7 @@ void ShaderEmitter::compileInstruction(const PICAShader& shaderUnit) { break; case ShaderOpcodes::DP3: recDP3(shaderUnit, instruction); break; case ShaderOpcodes::DP4: recDP4(shaderUnit, instruction); break; + case ShaderOpcodes::DPH: recDPH(shaderUnit, instruction); break; case ShaderOpcodes::END: recEND(shaderUnit, instruction); break; case ShaderOpcodes::EX2: recEX2(shaderUnit, instruction); break; case ShaderOpcodes::FLR: recFLR(shaderUnit, instruction); break; @@ -525,6 +526,30 @@ void ShaderEmitter::recDP4(const PICAShader& shader, u32 instruction) { storeRegister(src1_xmm, shader, dest, operandDescriptor); } +void ShaderEmitter::recDPH(const PICAShader& shader, u32 instruction) { + const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f]; + const u32 src1 = getBits<12, 7>(instruction); + const u32 src2 = getBits<7, 5>(instruction); // src2 coming first because PICA moment + const u32 idx = getBits<19, 2>(instruction); + const u32 dest = getBits<21, 5>(instruction); + + // TODO: Safe multiplication equivalent (Multiplication is not IEEE compliant on the PICA) + loadRegister<1>(src1_xmm, shader, src1, idx, operandDescriptor); + loadRegister<2>(src2_xmm, shader, src2, 0, operandDescriptor); + + // Attach 1.0 to the w component of src1 + if (haveSSE4_1) { + blendps(src1_xmm, xword[rip + onesVector], 0b1000); + } else { + movaps(scratch1, src1_xmm); + unpckhps(scratch1, xword[rip + onesVector]); + unpcklpd(src1_xmm, scratch1); + } + + dpps(src1_xmm, src2_xmm, 0b11111111); // 4-lane dot product between the 2 registers, store the result in all lanes of scratch1 similarly to PICA + storeRegister(src1_xmm, shader, dest, operandDescriptor); +} + void ShaderEmitter::recMAX(const PICAShader& shader, u32 instruction) { const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f]; const u32 src1 = getBits<12, 7>(instruction); From 66847d4570e21c7ba036d1fc4b0c93ec8e387aa5 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 02:11:55 +0300 Subject: [PATCH 08/13] Positional lighting maybe --- src/host_shaders/opengl_fragment_shader.frag | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag index f6461094..1ae5b6ff 100644 --- a/src/host_shaders/opengl_fragment_shader.frag +++ b/src/host_shaders/opengl_fragment_shader.frag @@ -228,10 +228,18 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_HIGH, 0, 16), 5, 10) )); - // Positional Light - if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0) error_unimpl = true; + vec3 half_vector; - vec3 half_vector = normalize(normalize(light_vector) + view); + // Positional Light + if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0) { + error_unimpl = true; + //half_vector = normalize(normalize(light_vector + v_view) + view); + } + + // Directional light + else { + half_vector = normalize(normalize(light_vector) + view); + } for (int c = 0; c < 7; c++) { if (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, 16 + c, 1) == 0) { From 60de6a65f5a0888d1aa1ad3c3f46f0496ed7a6df Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 18:47:53 +0300 Subject: [PATCH 09/13] Kernel: Don't idle on svcSleepThread(0) --- src/core/kernel/threads.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 8d4c477b..7d3d9f14 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -348,7 +348,11 @@ void Kernel::sleepThread(s64 ns) { t.status = ThreadStatus::Ready; if (nextThreadIndex.has_value()) { - switchThread(nextThreadIndex.value()); + const auto index = nextThreadIndex.value(); + + if (index != idleThreadIndex) { + switchThread(index); + } } } else { // If we're sleeping for >= 0 ns Thread& t = threads[currentThreadIndex]; From 3d5f1ea517b4af9b86e2ad63ea472828556e9bf4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 19:18:15 +0300 Subject: [PATCH 10/13] [Kernel] Add evalReschedule function --- include/kernel/kernel.hpp | 7 +++++++ src/core/kernel/kernel.cpp | 5 +---- src/emulator.cpp | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 23764dc0..99687ee1 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -172,6 +172,13 @@ public: void requireReschedule() { needReschedule = true; } + void evalReschedule() { + if (needReschedule) { + needReschedule = false; + rescheduleThreads(); + } + } + Handle makeObject(KernelObjectType type) { if (handleCounter > KernelHandles::Max) [[unlikely]] { Helpers::panic("Hlep we somehow created enough kernel objects to overflow this thing"); diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index a8cf40a1..c48c8f18 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -62,10 +62,7 @@ void Kernel::serviceSVC(u32 svc) { default: Helpers::panic("Unimplemented svc: %X @ %08X", svc, regs[15]); break; } - if (needReschedule) { - needReschedule = false; - rescheduleThreads(); - } + evalReschedule(); } void Kernel::setVersion(u8 major, u8 minor) { diff --git a/src/emulator.cpp b/src/emulator.cpp index fd5efe6b..2e7cd521 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -357,6 +357,8 @@ void Emulator::run() { hid.updateInputs(cpu.getTicks()); } + // TODO: Should this be uncommented? + // kernel.evalReschedule(); // Update inputs in the HID module SDL_GL_SwapWindow(window); From f73138c5de481b53c7f393eabad76d6998a21809 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 19:30:35 +0300 Subject: [PATCH 11/13] [Shader JIT] Implement SGE(I) --- .../PICA/dynapica/shader_rec_emitter_x64.hpp | 1 - include/PICA/shader.hpp | 1 + .../PICA/dynapica/shader_rec_emitter_x64.cpp | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/include/PICA/dynapica/shader_rec_emitter_x64.hpp b/include/PICA/dynapica/shader_rec_emitter_x64.hpp index 51e6a43d..0338911c 100644 --- a/include/PICA/dynapica/shader_rec_emitter_x64.hpp +++ b/include/PICA/dynapica/shader_rec_emitter_x64.hpp @@ -112,7 +112,6 @@ class ShaderEmitter : public Xbyak::CodeGenerator { void recRSQ(const PICAShader& shader, u32 instruction); void recSETEMIT(const PICAShader& shader, u32 instruction); void recSGE(const PICAShader& shader, u32 instruction); - void recSGEI(const PICAShader& shader, u32 instruction); void recSLT(const PICAShader& shader, u32 instruction); MAKE_LOG_FUNCTION(log, shaderJITLogger) diff --git a/include/PICA/shader.hpp b/include/PICA/shader.hpp index 0f3154f1..48db777d 100644 --- a/include/PICA/shader.hpp +++ b/include/PICA/shader.hpp @@ -23,6 +23,7 @@ namespace ShaderOpcodes { LG2 = 0x06, LIT = 0x07, MUL = 0x08, + SGE = 0x09, SLT = 0x0A, FLR = 0x0B, MAX = 0x0C, diff --git a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp index 523c41db..7bcf4b46 100644 --- a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp +++ b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp @@ -180,6 +180,10 @@ void ShaderEmitter::compileInstruction(const PICAShader& shaderUnit) { case ShaderOpcodes::SLTI: recSLT(shaderUnit, instruction); break; + case ShaderOpcodes::SGE: + case ShaderOpcodes::SGEI: + recSGE(shaderUnit, instruction); break; + default: Helpers::panic("Shader JIT: Unimplemented PICA opcode %X", opcode); } @@ -681,6 +685,24 @@ void ShaderEmitter::recSLT(const PICAShader& shader, u32 instruction) { storeRegister(src1_xmm, shader, dest, operandDescriptor); } +void ShaderEmitter::recSGE(const PICAShader& shader, u32 instruction) { + const bool isSGEI = (instruction >> 26) == ShaderOpcodes::SGEI; + const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f]; + + const u32 src1 = isSGEI ? getBits<14, 5>(instruction) : getBits<12, 7>(instruction); + const u32 src2 = isSGEI ? getBits<7, 7>(instruction) : getBits<7, 5>(instruction); + const u32 idx = getBits<19, 2>(instruction); + const u32 dest = getBits<21, 5>(instruction); + + loadRegister<1>(src1_xmm, shader, src1, isSGEI ? 0 : idx, operandDescriptor); + loadRegister<2>(src2_xmm, shader, src2, isSGEI ? idx : 0, operandDescriptor); + + // SSE does not have a cmpgeps instruction so we turn src1 >= src2 to src2 <= src1, result in src2 + cmpleps(src2_xmm, src1_xmm); + andps(src2_xmm, xword[rip + onesVector]); + storeRegister(src2_xmm, shader, dest, operandDescriptor); +} + void ShaderEmitter::recCMP(const PICAShader& shader, u32 instruction) { const u32 operandDescriptor = shader.operandDescriptors[instruction & 0x7f]; const u32 src1 = getBits<12, 7>(instruction); From 8f8f442f0f903863167ce67b97f694e35eb2fcc2 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 19:47:13 +0300 Subject: [PATCH 12/13] Comments --- src/core/kernel/threads.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 7d3d9f14..2e39b620 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -340,9 +340,11 @@ void Kernel::sleepThread(s64 ns) { if (ns < 0) { Helpers::panic("Sleeping a thread for a negative amount of ns"); } else if (ns == 0) { - // TODO: This is garbage, absolutely not getting merged + // TODO: This is garbage, but it works so eh we can keep it for now Thread& t = threads[currentThreadIndex]; + // See if a thread other than this and the idle thread is waiting to run by temp marking the current function as dead and searching + // If there is another thread to run, then run it. Otherwise, go back to this thread, not to the idle thread t.status = ThreadStatus::Dead; auto nextThreadIndex = getNextThread(); t.status = ThreadStatus::Ready; From 2c4473bb6ee1598e020a4705fe89ab11c823e9de Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Wed, 2 Aug 2023 20:05:36 +0300 Subject: [PATCH 13/13] clang-format --- src/host_shaders/opengl_fragment_shader.frag | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag index 1ae5b6ff..5b6e6830 100644 --- a/src/host_shaders/opengl_fragment_shader.frag +++ b/src/host_shaders/opengl_fragment_shader.frag @@ -230,16 +230,16 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { vec3 half_vector; - // Positional Light - if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0) { + // Positional Light + if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0) { error_unimpl = true; - //half_vector = normalize(normalize(light_vector + v_view) + view); - } - - // Directional light - else { - half_vector = normalize(normalize(light_vector) + view); - } + // half_vector = normalize(normalize(light_vector + v_view) + view); + } + + // Directional light + else { + half_vector = normalize(normalize(light_vector) + view); + } for (int c = 0; c < 7; c++) { if (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, 16 + c, 1) == 0) {