From de6e324bdee6a48b7cb2e26af299cc740b8674e0 Mon Sep 17 00:00:00 2001 From: iris Date: Mon, 15 Jun 2026 11:52:17 +0200 Subject: [PATCH] separate emu thread --- CMakeLists.txt | 4 +- .../parallel-rdp-standalone/vulkan/device.cpp | 14 +- .../parallel-rdp-standalone/vulkan/device.hpp | 2 +- src/frontend/EmuThread.cpp | 126 ++++++++++++++++ src/frontend/EmuThread.hpp | 21 +++ src/frontend/KaizenGui.cpp | 141 +----------------- src/frontend/KaizenGui.hpp | 12 +- src/frontend/RenderWidget.hpp | 1 - 8 files changed, 167 insertions(+), 154 deletions(-) create mode 100644 src/frontend/EmuThread.cpp create mode 100644 src/frontend/EmuThread.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d72cd4f..94a4b29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,7 +173,9 @@ qt_add_executable(kaizen src/utils/Options.cpp src/utils/File.cpp src/frontend/RomsList.hpp - src/frontend/RomsList.cpp) + src/frontend/RomsList.cpp + src/frontend/EmuThread.hpp + src/frontend/EmuThread.cpp) if (WIN32) set(MIO_LIB mio::mio_full_winapi) diff --git a/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp b/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp index 3e6b5f5..d93e3b0 100644 --- a/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp +++ b/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.cpp @@ -1462,7 +1462,7 @@ Helper::BatchComposer::BatchComposer() void Helper::BatchComposer::begin_batch() { - if (!waits[submit_index].empty() || !cmds[submit_index].empty() || !signals[submit_index].empty()) + if (!waits[submit_index].empty() || !cmds[submit_index].empty() || !signals_[submit_index].empty()) { submit_index = submits.size(); submits.emplace_back(); @@ -1491,8 +1491,8 @@ Helper::BatchComposer::bake(int profiling_iteration) submit = { VK_STRUCTURE_TYPE_SUBMIT_INFO_2 }; submit.commandBufferInfoCount = uint32_t(cmds[i].size()); submit.pCommandBufferInfos = cmds[i].data(); - submit.signalSemaphoreInfoCount = uint32_t(signals[i].size()); - submit.pSignalSemaphoreInfos = signals[i].data(); + submit.signalSemaphoreInfoCount = uint32_t(signals_[i].size()); + submit.pSignalSemaphoreInfos = signals_[i].data(); submit.waitSemaphoreInfoCount = uint32_t(waits[i].size()); submit.pWaitSemaphoreInfos = waits[i].data(); @@ -1523,7 +1523,7 @@ Helper::BatchComposer::bake(int profiling_iteration) void Helper::BatchComposer::add_command_buffer(VkCommandBuffer cmd) { - if (!signals[submit_index].empty()) + if (!signals_[submit_index].empty()) begin_batch(); VkCommandBufferSubmitInfo info = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO }; @@ -1537,12 +1537,12 @@ void Helper::BatchComposer::add_signal_semaphore(VkSemaphore sem, VkPipelineStag info.semaphore = sem; info.stageMask = stages; info.value = timeline; - signals[submit_index].push_back(info); + signals_[submit_index].push_back(info); } void Helper::BatchComposer::add_wait_semaphore(SemaphoreHolder &sem, VkPipelineStageFlags2 stage) { - if (!cmds[submit_index].empty() || !signals[submit_index].empty()) + if (!cmds[submit_index].empty() || !signals_[submit_index].empty()) begin_batch(); bool is_timeline = sem.get_semaphore_type() == VK_SEMAPHORE_TYPE_TIMELINE; @@ -1556,7 +1556,7 @@ void Helper::BatchComposer::add_wait_semaphore(SemaphoreHolder &sem, VkPipelineS void Helper::BatchComposer::add_wait_semaphore(VkSemaphore sem, VkPipelineStageFlags2 stage) { - if (!cmds[submit_index].empty() || !signals[submit_index].empty()) + if (!cmds[submit_index].empty() || !signals_[submit_index].empty()) begin_batch(); VkSemaphoreSubmitInfo info = { VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO }; diff --git a/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp b/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp index 2aba17e..1f42c79 100644 --- a/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp +++ b/external/parallel-rdp/parallel-rdp-standalone/vulkan/device.hpp @@ -144,7 +144,7 @@ private: VkPerformanceQuerySubmitInfoKHR profiling_infos[Helper::BatchComposer::MaxSubmissions]; Util::SmallVector waits[MaxSubmissions]; - Util::SmallVector signals[MaxSubmissions]; + Util::SmallVector signals_[MaxSubmissions]; Util::SmallVector cmds[MaxSubmissions]; unsigned submit_index = 0; diff --git a/src/frontend/EmuThread.cpp b/src/frontend/EmuThread.cpp new file mode 100644 index 0000000..1c8e01f --- /dev/null +++ b/src/frontend/EmuThread.cpp @@ -0,0 +1,126 @@ +#include + +void EmuThread::run() { + core.parallel.Init(vulkanWidget->wsiPlatform, vulkanWidget->windowInfo, vulkanWidget->qtVkInstanceFactory.get(), + core.GetMem().GetRDRAMPtr()); + while (!isInterruptionRequested()) { + if (!core.romLoaded || core.pause) + continue; + + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_EVENT_GAMEPAD_ADDED) { + if (!gamepad) { + const auto index = e.gdevice.which; + + gamepad = SDL_OpenGamepad(index); + warn("Found controller!"); + warn("Name: {}", SDL_GetGamepadName(gamepad)); + warn("Vendor: {}", SDL_GetGamepadVendor(gamepad)); + } + } + + if (e.type == SDL_EVENT_GAMEPAD_REMOVED) { + if (gamepad) { + SDL_CloseGamepad(gamepad); + gamepad = nullptr; + } + } + } + + updateKeys(); + updateAxis(); + + core.parallel.SetFramerateUnlocked(unlockFrameratePressed); + + auto timeStart = SDL_GetTicks(); + core.Run(); + core.parallel.UpdateScreen(); + elapsed = SDL_GetTicks() - timeStart; + } +} + +void EmuThread::updateKeys() { + auto &pif = core.mem->mmio.si.pif; + if (gamepad) { + pif.UpdateButton(0, n64::Controller::Z, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); + pif.UpdateButton(0, n64::Controller::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); + pif.UpdateButton(0, n64::Controller::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); + pif.UpdateButton(0, n64::Controller::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); + pif.UpdateButton(0, n64::Controller::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); + pif.UpdateButton(0, n64::Controller::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); + pif.UpdateButton(0, n64::Controller::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); + pif.UpdateButton(0, n64::Controller::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); + pif.UpdateButton(0, n64::Controller::CUp, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MIN); + pif.UpdateButton(0, n64::Controller::CDown, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MAX); + pif.UpdateButton(0, n64::Controller::CLeft, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MIN); + pif.UpdateButton(0, n64::Controller::CRight, + SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MAX); + return; + } + pif.UpdateButton(0, n64::Controller::Z, pressedKeys.test(0)); + pif.UpdateButton(0, n64::Controller::A, pressedKeys.test(1)); + pif.UpdateButton(0, n64::Controller::B, pressedKeys.test(2)); + pif.UpdateButton(0, n64::Controller::LT, pressedKeys.test(3)); + pif.UpdateButton(0, n64::Controller::RT, pressedKeys.test(4)); + pif.UpdateButton(0, n64::Controller::Start, pressedKeys.test(5)); + pif.UpdateButton(0, n64::Controller::DUp, pressedKeys.test(6)); + pif.UpdateButton(0, n64::Controller::DDown, pressedKeys.test(7)); + pif.UpdateButton(0, n64::Controller::DLeft, pressedKeys.test(8)); + pif.UpdateButton(0, n64::Controller::DRight, pressedKeys.test(9)); + pif.UpdateButton(0, n64::Controller::CUp, pressedKeys.test(10)); + pif.UpdateButton(0, n64::Controller::CDown, pressedKeys.test(11)); + pif.UpdateButton(0, n64::Controller::CLeft, pressedKeys.test(12)); + pif.UpdateButton(0, n64::Controller::CRight, pressedKeys.test(13)); +} + +void EmuThread::updateAxis() { + auto &pif = core.mem->mmio.si.pif; + + if (gamepad) { + float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); + if (xclamped < 0) { + xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); + } else { + xclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + xclamped *= 86; + + float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); + if (yclamped < 0) { + yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); + } else { + yclamped /= SDL_JOYSTICK_AXIS_MAX; + } + + yclamped *= 86; + + pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); + pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); + + return; + } + + s16 x = 0, y = 0; + if (pressedKeys.test(14)) // up + y += 86; + + if (pressedKeys.test(15)) // down + y -= 86; + + if (pressedKeys.test(16)) // left + x -= 86; + + if (pressedKeys.test(17)) // right + x += 86; + + core.mem->mmio.si.pif.UpdateAxis(0, n64::Controller::X, x); + core.mem->mmio.si.pif.UpdateAxis(0, n64::Controller::Y, y); +} diff --git a/src/frontend/EmuThread.hpp b/src/frontend/EmuThread.hpp new file mode 100644 index 0000000..fa130e4 --- /dev/null +++ b/src/frontend/EmuThread.hpp @@ -0,0 +1,21 @@ +#pragma once +#include +#include +#include +#include + +class EmuThread : public QThread { + Q_OBJECT + RenderWidget *vulkanWidget; + n64::Core &core = n64::Core::GetInstance(); + void updateAxis(); + void updateKeys(); + + public: + EmuThread(RenderWidget *rw) : vulkanWidget(rw) {} + void run() override; + std::bitset<18> pressedKeys{}; + std::atomic_bool unlockFrameratePressed = false; + float elapsed = 0.f; + SDL_Gamepad *gamepad = nullptr; +}; diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index a921f01..c918591 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -82,39 +82,12 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User setAcceptDrops(true); - SDLeventsTimer = new QTimer(); - SDLeventsTimer->setInterval(1); statusBarTimer = new QTimer(); statusBarTimer->setInterval(500); - connect(SDLeventsTimer, &QTimer::timeout, this, [&] { - SDL_Event e; - while (SDL_PollEvent(&e)) { - if (e.type == SDL_EVENT_GAMEPAD_ADDED) { - if (!gamepad) { - const auto index = e.gdevice.which; - - gamepad = SDL_OpenGamepad(index); - warn("Found controller!"); - warn("Name: {}", SDL_GetGamepadName(gamepad)); - warn("Vendor: {}", SDL_GetGamepadVendor(gamepad)); - } - } - - if (e.type == SDL_EVENT_GAMEPAD_REMOVED) { - if (gamepad) { - SDL_CloseGamepad(gamepad); - gamepad = nullptr; - } - } - } - }); - - SDLeventsTimer->start(); - connect(statusBarTimer, &QTimer::timeout, this, [&] { pause->setText("Pause"); - fpsLabel->setText(std::format("FPS: {:.2f}", 1000.f / elapsed).c_str()); + fpsLabel->setText(std::format("FPS: {:.2f}", 1000.f / emuThread->elapsed).c_str()); if (core.pause) { pause->setText("Resume"); fpsLabel->setText("Paused"); @@ -168,7 +141,8 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User auto unlockFramerate = emulationMenu->addAction("Unlock framerate"); unlockFramerate->setCheckable(true); - connect(unlockFramerate, &QAction::triggered, this, [&](bool checked) { unlockFrameratePressed = checked; }); + connect(unlockFramerate, &QAction::triggered, this, + [&](bool checked) { emuThread->unlockFrameratePressed = checked; }); pause->setDisabled(true); pause->setShortcut(QKeyCombination(Qt::CTRL, Qt::Key_P)); @@ -212,115 +186,12 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User show(); - emuThread = QThread::create([&] { - core.parallel.Init(vulkanWidget->wsiPlatform, vulkanWidget->windowInfo, vulkanWidget->qtVkInstanceFactory.get(), - core.GetMem().GetRDRAMPtr()); - while (!emuThread->isInterruptionRequested()) { - if (!core.romLoaded || core.pause) - continue; - - updateKeys(); - updateAxis(); - - core.parallel.SetFramerateUnlocked(unlockFrameratePressed); - - auto timeStart = SDL_GetTicks(); - core.Run(); - core.parallel.UpdateScreen(); - elapsed = SDL_GetTicks() - timeStart; - } - }); - + emuThread = new EmuThread(vulkanWidget); emuThread->start(); } KaizenGui::~KaizenGui() { cleanup(); } -void KaizenGui::updateKeys() { - auto &pif = core.mem->mmio.si.pif; - if (gamepad) { - pif.UpdateButton(0, n64::Controller::Z, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH)); - pif.UpdateButton(0, n64::Controller::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST)); - pif.UpdateButton(0, n64::Controller::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER)); - pif.UpdateButton(0, n64::Controller::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START)); - pif.UpdateButton(0, n64::Controller::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP)); - pif.UpdateButton(0, n64::Controller::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN)); - pif.UpdateButton(0, n64::Controller::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT)); - pif.UpdateButton(0, n64::Controller::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT)); - pif.UpdateButton(0, n64::Controller::CUp, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MIN); - pif.UpdateButton(0, n64::Controller::CDown, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) == SDL_JOYSTICK_AXIS_MAX); - pif.UpdateButton(0, n64::Controller::CLeft, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MIN); - pif.UpdateButton(0, n64::Controller::CRight, - SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) == SDL_JOYSTICK_AXIS_MAX); - return; - } - pif.UpdateButton(0, n64::Controller::Z, pressedKeys.test(0)); - pif.UpdateButton(0, n64::Controller::A, pressedKeys.test(1)); - pif.UpdateButton(0, n64::Controller::B, pressedKeys.test(2)); - pif.UpdateButton(0, n64::Controller::LT, pressedKeys.test(3)); - pif.UpdateButton(0, n64::Controller::RT, pressedKeys.test(4)); - pif.UpdateButton(0, n64::Controller::Start, pressedKeys.test(5)); - pif.UpdateButton(0, n64::Controller::DUp, pressedKeys.test(6)); - pif.UpdateButton(0, n64::Controller::DDown, pressedKeys.test(7)); - pif.UpdateButton(0, n64::Controller::DLeft, pressedKeys.test(8)); - pif.UpdateButton(0, n64::Controller::DRight, pressedKeys.test(9)); - pif.UpdateButton(0, n64::Controller::CUp, pressedKeys.test(10)); - pif.UpdateButton(0, n64::Controller::CDown, pressedKeys.test(11)); - pif.UpdateButton(0, n64::Controller::CLeft, pressedKeys.test(12)); - pif.UpdateButton(0, n64::Controller::CRight, pressedKeys.test(13)); -} - -void KaizenGui::updateAxis() { - auto &pif = core.mem->mmio.si.pif; - - if (gamepad) { - float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX); - if (xclamped < 0) { - xclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MAX)); - } else { - xclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - xclamped *= 86; - - float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY); - if (yclamped < 0) { - yclamped /= static_cast(std::abs(SDL_JOYSTICK_AXIS_MIN)); - } else { - yclamped /= SDL_JOYSTICK_AXIS_MAX; - } - - yclamped *= 86; - - pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast(-yclamped)); - pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast(xclamped)); - - return; - } - - s16 x = 0, y = 0; - if (pressedKeys.test(14)) // up - y += 86; - - if (pressedKeys.test(15)) // down - y -= 86; - - if (pressedKeys.test(16)) // left - x -= 86; - - if (pressedKeys.test(17)) // right - x += 86; - - core.mem->mmio.si.pif.UpdateAxis(0, n64::Controller::X, x); - core.mem->mmio.si.pif.UpdateAxis(0, n64::Controller::Y, y); -} - void KaizenGui::LoadROM(const std::string &path) noexcept { core.LoadROM(path); pause->setEnabled(true); @@ -360,14 +231,14 @@ void KaizenGui::dragEnterEvent(QDragEnterEvent *event) { void KaizenGui::keyPressEvent(QKeyEvent *event) { for (int i = 0; i < settingsWindow->input->mapping.size(); i++) { if (settingsWindow->input->mapping[i] == event->key()) - pressedKeys.set(i); + emuThread->pressedKeys.set(i); } } void KaizenGui::keyReleaseEvent(QKeyEvent *event) { for (int i = 0; i < settingsWindow->input->mapping.size(); i++) { if (settingsWindow->input->mapping[i] == event->key()) - pressedKeys.reset(i); + emuThread->pressedKeys.reset(i); } } diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index cbbdcbe..a7929b4 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include class KaizenGui final : QMainWindow { Q_OBJECT @@ -19,22 +19,16 @@ class KaizenGui final : QMainWindow { bool fastForward = false; bool minimized = false; - SDL_Gamepad *gamepad = nullptr; - bool gamepadConnected; - float elapsed = 0.f; n64::Core &core = n64::Core::GetInstance(); - std::bitset<18> pressedKeys{}; - std::atomic_bool unlockFrameratePressed = false; - QWidget *currentHomeWidget; QLabel *romPathNotSet = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")"); RomsListTable *romsListTable; QStackedWidget *centralWidget = new QStackedWidget(); SettingsWindow *settingsWindow; RenderWidget *vulkanWidget; QSettings settings; - QThread *emuThread; - QTimer *statusBarTimer, *SDLeventsTimer; + EmuThread *emuThread; + QTimer *statusBarTimer; QLabel *fpsLabel; QLabel *cpuTypeLabel; QLabel *idleSkipLabel; diff --git a/src/frontend/RenderWidget.hpp b/src/frontend/RenderWidget.hpp index a215522..f36ab60 100644 --- a/src/frontend/RenderWidget.hpp +++ b/src/frontend/RenderWidget.hpp @@ -1,5 +1,4 @@ #pragma once -#undef signals #include #include #include