From 98cfaef6ec2a62e6b0cb97b67a0d51b99dd67e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 31 Jan 2018 12:05:18 +0100 Subject: [PATCH] Rough fix for threaded GL for Qt. --- Qt/QtMain.cpp | 97 ++++++++++++++++++++++++++++++++++++++++----------- Qt/QtMain.h | 43 +++++++++++++++++++++-- 2 files changed, 117 insertions(+), 23 deletions(-) diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 3030f64354..ea364b08a5 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -29,6 +29,7 @@ #include "QtMain.h" #include "gfx_es2/gpu_features.h" #include "math/math_util.h" +#include "thread/threadutil.h" #include @@ -188,31 +189,64 @@ static int mainInternal(QApplication &a) return a.exec(); } +void MainUI::EmuThreadFunc() { + ILOG("In emu thread"); + setCurrentThreadName("Emu"); + + // There's no real requirement that NativeInit happen on this thread. + // We just call the update/render loop here. + emuThreadState = (int)EmuThreadState::RUNNING; + while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) { + #ifdef SDL + SDL_PumpEvents(); + #endif + updateAccelerometer(); + time_update(); + UpdateRunLoop(); + } + emuThreadState = (int)EmuThreadState::STOPPED; +} + +void MainUI::EmuThreadStart() { + emuThreadState = (int)EmuThreadState::START_REQUESTED; + emuThread = std::thread([&]() { this->EmuThreadFunc(); } ); +} + +void MainUI::EmuThreadStop() { + emuThreadState = (int)EmuThreadState::QUIT_REQUESTED; + emuThread.join(); + emuThread = std::thread(); +} + MainUI::MainUI(QWidget *parent): - QGLWidget(parent) + QGLWidget(parent) { - setAttribute(Qt::WA_AcceptTouchEvents); + emuThreadState = (int)EmuThreadState::DISABLED; + setAttribute(Qt::WA_AcceptTouchEvents); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - setAttribute(Qt::WA_LockLandscapeOrientation); + setAttribute(Qt::WA_LockLandscapeOrientation); #endif #if defined(MOBILE_DEVICE) - acc = new QAccelerometer(this); - acc->start(); + acc = new QAccelerometer(this); + acc->start(); #endif - setFocus(); - setFocusPolicy(Qt::StrongFocus); - startTimer(16); + setFocus(); + setFocusPolicy(Qt::StrongFocus); + startTimer(16); } MainUI::~MainUI() { + if (useThread_) { + EmuThreadStop(); + } #if defined(MOBILE_DEVICE) - delete acc; + delete acc; #endif - NativeShutdownGraphics(); - graphicsContext->Shutdown(); - delete graphicsContext; - graphicsContext = nullptr; + NativeShutdownGraphics(); + graphicsContext->Shutdown(); + delete graphicsContext; + graphicsContext = nullptr; } QString MainUI::InputBoxGetQString(QString title, QString defaultValue) @@ -330,18 +364,36 @@ void MainUI::initializeGL() if (gl_extensions.IsCoreContext) glGetError(); #endif + ILOG("Initializing graphics context"); graphicsContext = new QtDummyGraphicsContext(); NativeInitGraphics(graphicsContext); + + // OpenGL uses a background thread to do the main processing and only renders on the gl thread. + useThread_ = g_Config.iGPUBackend == (int)GPUBackend::OPENGL; + + if (useThread_) { + ILOG("Using thread, starting emu thread"); + EmuThreadStart(); + + graphicsContext->ThreadStart(); + } else { + ILOG("Not using thread, backend=%d", (int)g_Config.iGPUBackend); + } } void MainUI::paintGL() { -#ifdef SDL - SDL_PumpEvents(); -#endif - updateAccelerometer(); - time_update(); - UpdateRunLoop(); + if (useThread_) { + graphicsContext->ThreadFrame(); + // Do the rest in EmuThreadFunc + } else { + #ifdef SDL + SDL_PumpEvents(); + #endif + updateAccelerometer(); + time_update(); + UpdateRunLoop(); + } } void MainUI::updateAccelerometer() @@ -454,14 +506,17 @@ int main(int argc, char *argv[]) #endif savegame_dir += "/"; external_dir += "/"; - + bool fullscreenCLI=false; for (int i = 1; i < argc; i++) { if (!strcmp(argv[i],"--fullscreen")) fullscreenCLI=true; } NativeInit(argc, (const char **)argv, savegame_dir.c_str(), external_dir.c_str(), nullptr, fullscreenCLI); - + + // TODO: Support other backends than GL, like Vulkan, in the Qt backend. + g_Config.iGPUBackend = (int)GPUBackend::OPENGL; + int ret = mainInternal(a); NativeShutdownGraphics(); diff --git a/Qt/QtMain.h b/Qt/QtMain.h index f4f1b3c5c2..8f0538f7c6 100644 --- a/Qt/QtMain.h +++ b/Qt/QtMain.h @@ -19,6 +19,8 @@ QTM_USE_NAMESPACE #endif #include +#include +#include #include "base/display.h" #include "base/logging.h" @@ -37,6 +39,7 @@ QTM_USE_NAMESPACE #include "Core/Core.h" #include "Core/Config.h" #include "Core/System.h" +#include "thin3d/GLRenderManager.h" // Input void SimulateGamepad(); @@ -47,21 +50,48 @@ public: CheckGLExtensions(); draw_ = Draw::T3DCreateGLContext(); SetGPUBackend(GPUBackend::OPENGL); + renderManager_ = (GLRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); bool success = draw_->CreatePresets(); assert(success); } + ~QtDummyGraphicsContext() { delete draw_; + draw_ = nullptr; + renderManager_ = nullptr; } Draw::DrawContext *GetDrawContext() override { return draw_; } + + void ThreadStart() override { + renderManager_->ThreadStart(); + } + + bool ThreadFrame() override { + return renderManager_->ThreadFrame(); + } + + void ThreadEnd() override { + renderManager_->ThreadEnd(); + } + private: - Draw::DrawContext *draw_; + Draw::DrawContext *draw_ = nullptr; + GLRenderManager *renderManager_ = nullptr; }; -//GUI +enum class EmuThreadState { + DISABLED, + START_REQUESTED, + RUNNING, + QUIT_REQUESTED, + STOPPED, +}; + + +// GUI, thread manager class MainUI : public QGLWidget { Q_OBJECT @@ -87,6 +117,10 @@ protected: void updateAccelerometer(); + void EmuThreadFunc(); + void EmuThreadStart(); + void EmuThreadStop(); + private: QtDummyGraphicsContext *graphicsContext; @@ -94,6 +128,11 @@ private: #if defined(MOBILE_DEVICE) QAccelerometer* acc; #endif + + std::thread emuThread; + std::atomic emuThreadState; + + bool useThread_ = false; }; extern MainUI* emugl;