From 061c80fd11029aeffa7bc8b32fcec6afa214dbc0 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 1 Oct 2023 23:51:08 +0300 Subject: [PATCH 1/3] Properly join emulator thread --- include/panda_qt/main_window.hpp | 5 ++++- src/panda_qt/main_window.cpp | 17 +++++++++++++---- src/panda_qt/screen.cpp | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/include/panda_qt/main_window.hpp b/include/panda_qt/main_window.hpp index 07b3a509..4e36dae5 100644 --- a/include/panda_qt/main_window.hpp +++ b/include/panda_qt/main_window.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "emulator.hpp" @@ -24,9 +25,11 @@ class MainWindow : public QMainWindow { Emulator* emu = nullptr; std::thread emuThread; + std::atomic appRunning = true; // Is the application itself running? + + ScreenWidget screen; QComboBox* themeSelect = nullptr; QMenuBar* menuBar = nullptr; - ScreenWidget screen; Theme currentTheme; void setTheme(Theme theme); diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 2a73ac67..9f99a2b7 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -4,15 +4,21 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) setWindowTitle("Alber"); // Enable drop events for loading ROMs setAcceptDrops(true); - resize(400, 240 * 2); + resize(800, 240 * 4); screen.show(); + appRunning = true; + // Set our menu bar up menuBar = new QMenuBar(this); setMenuBar(menuBar); - auto pandaMenu = menuBar->addMenu(tr("PANDA")); - auto pandaAction = pandaMenu->addAction(tr("panda...")); + auto fileMenu = menuBar->addMenu(tr("File")); + auto pandaAction = fileMenu->addAction(tr("panda...")); + + auto emulationMenu = menuBar->addMenu(tr("Emulation")); + auto helpMenu = menuBar->addMenu(tr("Help")); + auto aboutMenu = menuBar->addMenu(tr("About")); // Set up theme selection setTheme(Theme::Dark); @@ -53,7 +59,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) Helpers::panic("Failed to load ROM"); } - while (true) { + while (appRunning) { emu->runFrame(); swapEmuBuffer(); } @@ -70,6 +76,9 @@ void MainWindow::swapEmuBuffer() { // Cleanup when the main window closes MainWindow::~MainWindow() { + appRunning = false; // Set our running atomic to false in order to make the emulator thread stop, and join it + emuThread.join(); + delete emu; delete menuBar; delete themeSelect; diff --git a/src/panda_qt/screen.cpp b/src/panda_qt/screen.cpp index 354a7f42..5a254e79 100644 --- a/src/panda_qt/screen.cpp +++ b/src/panda_qt/screen.cpp @@ -20,7 +20,7 @@ #ifdef PANDA3DS_ENABLE_OPENGL ScreenWidget::ScreenWidget(QWidget* parent) : QWidget(parent) { // Create a native window for use with our graphics API of choice - resize(400, 240 * 2); + resize(800, 240 * 4); setAutoFillBackground(false); setAttribute(Qt::WA_NativeWindow, true); From c648b6c62d90b590d8b59403cf1c30411b7672fd Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 2 Oct 2023 00:59:14 +0300 Subject: [PATCH 2/3] [Qt] Add file picker --- include/panda_qt/main_window.hpp | 5 +++ src/panda_qt/main_window.cpp | 55 ++++++++++++++++++++++++++------ 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/include/panda_qt/main_window.hpp b/include/panda_qt/main_window.hpp index 4e36dae5..9f9f1014 100644 --- a/include/panda_qt/main_window.hpp +++ b/include/panda_qt/main_window.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "emulator.hpp" #include "panda_qt/screen.hpp" @@ -26,6 +27,8 @@ class MainWindow : public QMainWindow { std::thread emuThread; std::atomic appRunning = true; // Is the application itself running? + std::atomic needToLoadROM = false; + std::filesystem::path romToLoad = ""; ScreenWidget screen; QComboBox* themeSelect = nullptr; @@ -34,6 +37,8 @@ class MainWindow : public QMainWindow { Theme currentTheme; void setTheme(Theme theme); void swapEmuBuffer(); + void emuThreadMainLoop(); + void selectROM(); // Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer bool usingGL = false; diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 9f99a2b7..998f711e 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -1,5 +1,8 @@ #include "panda_qt/main_window.hpp" +#include +#include + MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), screen(this) { setWindowTitle("Alber"); // Enable drop events for loading ROMs @@ -15,6 +18,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) auto fileMenu = menuBar->addMenu(tr("File")); auto pandaAction = fileMenu->addAction(tr("panda...")); + connect(pandaAction, &QAction::triggered, this, &MainWindow::selectROM); auto emulationMenu = menuBar->addMenu(tr("Emulation")); auto helpMenu = menuBar->addMenu(tr("Help")); @@ -54,16 +58,28 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) Helpers::panic("Unsupported graphics backend for Qt frontend!"); } - bool success = emu->loadROM("OoT.3ds"); - if (!success) { - Helpers::panic("Failed to load ROM"); + // Enter our emulator main loop + emuThreadMainLoop(); + }); +} + +// This is the main loop that the emulator thread runs after being initialized +void MainWindow::emuThreadMainLoop() { + while (appRunning) { + if (needToLoadROM.load()) { + bool success = emu->loadROM(romToLoad); + if (!success) { + printf("Failed to load ROM"); + } + + needToLoadROM.store(false, std::memory_order::seq_cst); } - while (appRunning) { - emu->runFrame(); - swapEmuBuffer(); - } - }); + emu->runFrame(); + swapEmuBuffer(); + } + + printf("Emulator thread returned"); } void MainWindow::swapEmuBuffer() { @@ -74,10 +90,31 @@ void MainWindow::swapEmuBuffer() { } } +void MainWindow::selectROM() { + // Are we already waiting for a ROM to be loaded? Then complain about it! + if (needToLoadROM.load()) { + QMessageBox::warning(this, tr("Already loading ROM"), tr("Panda3DS is already busy loading a ROM, please wait")); + return; + } + + auto path = + QFileDialog::getOpenFileName(this, tr("Select 3DS ROM to load"), "", tr("Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.3dsx *.elf *.axf)")); + + if (!path.isEmpty()) { + romToLoad = path.toStdU16String(); + needToLoadROM.store(true, std::memory_order_seq_cst); + } +} + // Cleanup when the main window closes MainWindow::~MainWindow() { + printf("Destroying window class\n"); appRunning = false; // Set our running atomic to false in order to make the emulator thread stop, and join it - emuThread.join(); + + if (emuThread.joinable()) { + emuThread.join(); + } + printf("Emu thread joined!\n"); delete emu; delete menuBar; From 9d9b0f9c41bc7706bff197e9428b7944a901d027 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 2 Oct 2023 02:09:13 +0300 Subject: [PATCH 3/3] [Qt] Fix deadlock --- src/panda_qt/main_window.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 998f711e..b4887454 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -40,7 +40,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) emu->setOutputSize(screen.surfaceWidth, screen.surfaceHeight); // The emulator graphics context for the thread should be initialized in the emulator thread due to how GL contexts work - emuThread = std::thread([&]() { + emuThread = std::thread([this]() { const RendererType rendererType = emu->getConfig().rendererType; usingGL = (rendererType == RendererType::OpenGL || rendererType == RendererType::Software || rendererType == RendererType::Null); usingVk = (rendererType == RendererType::Vulkan); @@ -58,12 +58,10 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) Helpers::panic("Unsupported graphics backend for Qt frontend!"); } - // Enter our emulator main loop emuThreadMainLoop(); }); } -// This is the main loop that the emulator thread runs after being initialized void MainWindow::emuThreadMainLoop() { while (appRunning) { if (needToLoadROM.load()) { @@ -79,7 +77,10 @@ void MainWindow::emuThreadMainLoop() { swapEmuBuffer(); } - printf("Emulator thread returned"); + // Unbind GL context if we're using GL, otherwise some setups seem to be unable to join this thread + if (usingGL) { + screen.getGLContext()->DoneCurrent(); + } } void MainWindow::swapEmuBuffer() { @@ -108,13 +109,11 @@ void MainWindow::selectROM() { // Cleanup when the main window closes MainWindow::~MainWindow() { - printf("Destroying window class\n"); appRunning = false; // Set our running atomic to false in order to make the emulator thread stop, and join it if (emuThread.joinable()) { emuThread.join(); } - printf("Emu thread joined!\n"); delete emu; delete menuBar;