separate emu thread

This commit is contained in:
2026-06-15 11:52:17 +02:00
parent 10d3daf86a
commit de6e324bde
8 changed files with 167 additions and 154 deletions
+3 -1
View File
@@ -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)
@@ -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 };
@@ -144,7 +144,7 @@ private:
VkPerformanceQuerySubmitInfoKHR profiling_infos[Helper::BatchComposer::MaxSubmissions];
Util::SmallVector<VkSemaphoreSubmitInfo> waits[MaxSubmissions];
Util::SmallVector<VkSemaphoreSubmitInfo> signals[MaxSubmissions];
Util::SmallVector<VkSemaphoreSubmitInfo> signals_[MaxSubmissions];
Util::SmallVector<VkCommandBufferSubmitInfo> cmds[MaxSubmissions];
unsigned submit_index = 0;
+126
View File
@@ -0,0 +1,126 @@
#include <EmuThread.hpp>
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<float>(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<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(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);
}
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include <QThread>
#include <RenderWidget.hpp>
#include <Core.hpp>
#include <bitset>
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;
};
+6 -135
View File
@@ -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<float>(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<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(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);
}
}
+3 -9
View File
@@ -9,7 +9,7 @@
#include <QDragEnterEvent>
#include <RomsList.hpp>
#include <Core.hpp>
#include <bitset>
#include <EmuThread.hpp>
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;
-1
View File
@@ -1,5 +1,4 @@
#pragma once
#undef signals
#include <ParallelRDPWrapper.hpp>
#include <QVulkanWindow>
#include <QMainWindow>