diff --git a/src/core.cpp b/src/core.cpp index 23695b7..4696b48 100644 --- a/src/core.cpp +++ b/src/core.cpp @@ -213,6 +213,7 @@ void Core::stop() saveThread->join(); delete emuThread; delete saveThread; + RDP::finishThread(); } } diff --git a/src/desktop/ry_frame.cpp b/src/desktop/ry_frame.cpp index 5550915..f5f085c 100644 --- a/src/desktop/ry_frame.cpp +++ b/src/desktop/ry_frame.cpp @@ -35,6 +35,7 @@ enum FrameEvent STOP, INPUT_BINDINGS, FPS_LIMITER, + THREADED_RDP, TEX_FILTER, UPDATE_JOY }; @@ -48,6 +49,7 @@ EVT_MENU(RESTART, ryFrame::restart) EVT_MENU(STOP, ryFrame::stop) EVT_MENU(INPUT_BINDINGS, ryFrame::inputSettings) EVT_MENU(FPS_LIMITER, ryFrame::toggleFpsLimit) +EVT_MENU(THREADED_RDP, ryFrame::toggleThreadRdp) EVT_MENU(TEX_FILTER, ryFrame::toggleTexFilter) EVT_TIMER(UPDATE_JOY, ryFrame::updateJoystick) EVT_DROP_FILES(ryFrame::dropFiles) @@ -75,10 +77,12 @@ ryFrame::ryFrame(std::string path): wxFrame(nullptr, wxID_ANY, "rokuyon") settingsMenu->Append(INPUT_BINDINGS, "&Input Bindings"); settingsMenu->AppendSeparator(); settingsMenu->AppendCheckItem(FPS_LIMITER, "&FPS Limiter"); + settingsMenu->AppendCheckItem(THREADED_RDP, "&Threaded RDP"); settingsMenu->AppendCheckItem(TEX_FILTER, "&Texture Filter"); // Set the initial checkbox states settingsMenu->Check(FPS_LIMITER, Settings::fpsLimiter); + settingsMenu->Check(THREADED_RDP, Settings::threadedRdp); settingsMenu->Check(TEX_FILTER, Settings::texFilter); // Set up the menu bar @@ -313,6 +317,13 @@ void ryFrame::toggleFpsLimit(wxCommandEvent &event) Settings::save(); } +void ryFrame::toggleThreadRdp(wxCommandEvent &event) +{ + // Toggle the threaded RDP setting + Settings::threadedRdp = !Settings::threadedRdp; + Settings::save(); +} + void ryFrame::toggleTexFilter(wxCommandEvent &event) { // Toggle the texture filter setting diff --git a/src/desktop/ry_frame.h b/src/desktop/ry_frame.h index 97ba799..8f3bfbe 100644 --- a/src/desktop/ry_frame.h +++ b/src/desktop/ry_frame.h @@ -61,6 +61,7 @@ class ryFrame: public wxFrame void stop(wxCommandEvent &event); void inputSettings(wxCommandEvent &event); void toggleFpsLimit(wxCommandEvent &event); + void toggleThreadRdp(wxCommandEvent &event); void toggleTexFilter(wxCommandEvent &event); void updateJoystick(wxTimerEvent &event); void dropFiles(wxDropFilesEvent &event); diff --git a/src/rdp.cpp b/src/rdp.cpp index 37397a6..cc4acdd 100644 --- a/src/rdp.cpp +++ b/src/rdp.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include "rdp.h" #include "log.h" @@ -63,6 +66,10 @@ namespace RDP extern void (*commands[])(); extern uint8_t paramCounts[]; + std::thread *thread; + std::mutex mutex; + bool running; + uint8_t tmem[0x1000]; // 4KB TMEM uint32_t startAddr; uint32_t endAddr; @@ -71,7 +78,7 @@ namespace RDP uint32_t addrBase; uint32_t addrMask; uint8_t paramCount; - uint64_t opcode[22]; + std::vector opcode; CycleType cycleType; bool texFilter; @@ -130,7 +137,9 @@ namespace RDP bool blendPixel(bool cycle, uint32_t &color); bool drawPixel(int x, int y); + void runThreaded(); void runCommands(); + void triangle(); void triDepth(); void triTexture(); @@ -204,6 +213,7 @@ void RDP::reset() addrBase = 0xA0000000; addrMask = 0x3FFFFF; paramCount = 0; + opcode.clear(); cycleType = ONE_CYCLE; texFilter = false; blendA[0] = blendA[1] = 0; @@ -664,24 +674,84 @@ bool RDP::drawPixel(int x, int y) return false; } +void RDP::finishThread() +{ + // Stop the thread if it was running + if (running) + { + running = false; + thread->join(); + delete thread; + } +} + +void RDP::runThreaded() +{ + while (true) + { + // Parse the next command if one is queued + mutex.lock(); + uint8_t op = opcode.empty() ? 0 : ((opcode[0] >> 56) & 0x3F); + uint8_t count = paramCounts[op]; + + if (opcode.size() >= count) + { + // Execute a command once all of its parameters have been queued + mutex.unlock(); + (*commands[op])(); + mutex.lock(); + opcode.erase(opcode.begin(), opcode.begin() + count); + mutex.unlock(); + } + else + { + // If requested, stop running when the queue is empty + mutex.unlock(); + if (!running) return; + std::this_thread::yield(); + } + } +} + void RDP::runCommands() { + // Start the thread if enabled and not running + if (Settings::threadedRdp && !running) + { + running = true; + thread = new std::thread(runThreaded); + } + + mutex.lock(); + // Process RDP commands until the end address is reached while (startAddr < endAddr) { // Add a parameter to the buffer - opcode[paramCount++] = Memory::read(addrBase + (startAddr & addrMask)); + opcode.push_back(Memory::read(addrBase + (startAddr & addrMask))); + paramCount++; // Execute a command once all of its parameters have been received - if (paramCount >= paramCounts[(opcode[0] >> 56) & 0x3F]) + // When threaded, only run sync commands here; the rest will run on the thread + uint8_t op = (opcode[opcode.size() - paramCount] >> 56) & 0x3F; + if (paramCount >= paramCounts[op]) { - (*commands[(opcode[0] >> 56) & 0x3F])(); paramCount = 0; + if (!running || op == 0x29) // Sync Full + { + mutex.unlock(); + finishThread(); + mutex.lock(); + (*commands[op])(); + opcode.clear(); + } } // Move to the next parameter startAddr += 8; } + + mutex.unlock(); } void RDP::triangle() diff --git a/src/rdp.h b/src/rdp.h index db3ee1f..e54ad55 100644 --- a/src/rdp.h +++ b/src/rdp.h @@ -27,6 +27,7 @@ namespace RDP void reset(); uint32_t read(int index); void write(int index, uint32_t value); + void finishThread(); } #endif // RDP_H diff --git a/src/settings.cpp b/src/settings.cpp index 4a1aada..2567768 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -35,11 +35,13 @@ namespace Settings { std::string filename; int fpsLimiter = 1; + int threadedRdp = 0; int texFilter = 1; std::vector settings = { Setting("fpsLimiter", &fpsLimiter, false), + Setting("threadedRdp", &threadedRdp, false), Setting("texFilter", &texFilter, false) }; } diff --git a/src/settings.h b/src/settings.h index ec59770..2c2dafb 100644 --- a/src/settings.h +++ b/src/settings.h @@ -29,6 +29,7 @@ namespace Settings bool save(); extern int fpsLimiter; + extern int threadedRdp; extern int texFilter; } diff --git a/src/switch/main.cpp b/src/switch/main.cpp index 8eb766c..8b83d64 100644 --- a/src/switch/main.cpp +++ b/src/switch/main.cpp @@ -105,6 +105,7 @@ void settingsMenu() std::vector settings = { ListItem("FPS Limiter", toggle[Settings::fpsLimiter]), + ListItem("Threaded RDP", toggle[Settings::threadedRdp]), ListItem("Texture Filter", toggle[Settings::texFilter]) }; @@ -119,7 +120,8 @@ void settingsMenu() switch (index) { case 0: Settings::fpsLimiter = !Settings::fpsLimiter; break; - case 1: Settings::texFilter = !Settings::texFilter; break; + case 1: Settings::threadedRdp = !Settings::threadedRdp; break; + case 2: Settings::texFilter = !Settings::texFilter; break; } } else diff --git a/src/vi.cpp b/src/vi.cpp index 6e6adda..1343b88 100644 --- a/src/vi.cpp +++ b/src/vi.cpp @@ -28,6 +28,7 @@ #include "log.h" #include "memory.h" #include "mi.h" +#include "rdp.h" namespace VI { @@ -151,6 +152,9 @@ void VI::write(uint32_t address, uint32_t value) void VI::drawFrame() { + // Ensure the RDP thread has finished drawing + RDP::finishThread(); + // Allow up to 2 framebuffers to be queued, to preserve frame pacing if emulation runs ahead if (framebuffers.size() < 2) {