diff options
Diffstat (limited to 'src/frontend/qt_sdl')
-rw-r--r-- | src/frontend/qt_sdl/CMakeLists.txt | 56 | ||||
-rw-r--r-- | src/frontend/qt_sdl/OSD.cpp | 126 | ||||
-rw-r--r-- | src/frontend/qt_sdl/OSD.h | 8 | ||||
-rw-r--r-- | src/frontend/qt_sdl/VideoSettingsDialog.cpp | 37 | ||||
-rw-r--r-- | src/frontend/qt_sdl/VideoSettingsDialog.h | 3 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 595 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.h | 74 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main_shaders.h | 5 |
8 files changed, 533 insertions, 371 deletions
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index a8d6e4b..bfa58bf 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -45,8 +45,11 @@ set(SOURCES_QT_SDL ../FrontendUtil.h ../mic_blow.h + ../glad/glad.c + ../duckstation/gl/context.cpp + ${CMAKE_SOURCE_DIR}/res/melon.qrc - ) +) if (APPLE) option(USE_QT6 "Build using Qt 6 instead of 5" ON) @@ -71,12 +74,13 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) +find_package(Threads REQUIRED) +find_package(PkgConfig REQUIRED) + if (BUILD_STATIC) list(APPEND PKG_CONFIG_EXECUTABLE "--static") endif() -find_package(Threads REQUIRED) -find_package(PkgConfig REQUIRED) pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp) pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive) @@ -87,6 +91,47 @@ add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) add_executable(melonDS ${SOURCES_QT_SDL}) +if (WIN32) + target_link_libraries(melonDS PUBLIC opengl32) + + target_sources(melonDS PRIVATE + ../duckstation/gl/context_wgl.cpp + + ../glad/glad_wgl.c + ) +elseif (APPLE) + if (NOT USE_QT6) + find_library(COCOA_LIB Cocoa) + target_link_libraries(melonDS PRIVATE ${COCOA_LIB}) + endif() + target_sources(melonDS PRIVATE + ../duckstation/gl/context_agl.mm + ) +else() + # we only need ECM for Wayland + # so we only require it from here + find_package(ECM REQUIRED NO_MODULE) + list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") + + find_package(X11 REQUIRED) + find_package(EGL REQUIRED) + find_package(Wayland REQUIRED Client) + + target_sources(melonDS PRIVATE + ../duckstation/gl/context_egl.cpp + ../duckstation/gl/context_egl_x11.cpp + ../duckstation/gl/context_glx.cpp + ../duckstation/gl/context_egl_wayland.cpp + ../duckstation/gl/x11_window.cpp + + ../glad/glad_egl.c + ../glad/glad_glx.c + ) + target_link_libraries(melonDS PRIVATE "${X11_LIBRARIES}" "${EGL_LIBRARIES}") + target_include_directories(melonDS PRIVATE "${X11_INCLUDE_DIR}") +endif() + + if (BUILD_STATIC) qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin) target_link_options(melonDS PRIVATE -static) @@ -95,6 +140,11 @@ endif() target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..") +if (USE_QT6) + target_include_directories(melonDS PUBLIC ${Qt6Gui_PRIVATE_INCLUDE_DIRS}) +else() + target_include_directories(melonDS PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) +endif() target_link_libraries(melonDS PRIVATE core) target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive) target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS}) diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index 6f060a9..d3becc1 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -23,6 +23,7 @@ #include "../types.h" #include "main.h" +#include "OpenGLSupport.h" #include <QPainter> #include "OSD.h" @@ -52,42 +53,37 @@ struct Item bool GLTextureLoaded; GLuint GLTexture; - }; std::deque<Item> ItemQueue; -QOpenGLShaderProgram* Shader; +GLuint Shader[3]; GLint uScreenSize, uOSDPos, uOSDSize; GLfloat uScaleFactor; GLuint OSDVertexArray; GLuint OSDVertexBuffer; -volatile bool Rendering; +QMutex Rendering; -bool Init(QOpenGLFunctions_3_2_Core* f) +bool Init(bool openGL) { - if (f) + if (openGL) { - Shader = new QOpenGLShaderProgram(); - Shader->addShaderFromSourceCode(QOpenGLShader::Vertex, kScreenVS_OSD); - Shader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS_OSD); - - GLuint pid = Shader->programId(); - f->glBindAttribLocation(pid, 0, "vPosition"); - f->glBindFragDataLocation(pid, 0, "oColor"); + OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, Shader, "OSDShader"); - Shader->link(); + GLuint pid = Shader[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindFragDataLocation(pid, 0, "oColor"); - Shader->bind(); - Shader->setUniformValue("OSDTex", (GLint)0); - Shader->release(); + OpenGL::LinkShaderProgram(Shader); + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0); - uScreenSize = Shader->uniformLocation("uScreenSize"); - uOSDPos = Shader->uniformLocation("uOSDPos"); - uOSDSize = Shader->uniformLocation("uOSDSize"); - uScaleFactor = Shader->uniformLocation("uScaleFactor"); + uScreenSize = glGetUniformLocation(pid, "uScreenSize"); + uOSDPos = glGetUniformLocation(pid, "uOSDPos"); + uOSDSize = glGetUniformLocation(pid, "uOSDSize"); + uScaleFactor = glGetUniformLocation(pid, "uScaleFactor"); float vertices[6*2] = { @@ -99,32 +95,30 @@ bool Init(QOpenGLFunctions_3_2_Core* f) 1, 1 }; - f->glGenBuffers(1, &OSDVertexBuffer); - f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - f->glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glGenBuffers(1, &OSDVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - f->glGenVertexArrays(1, &OSDVertexArray); - f->glBindVertexArray(OSDVertexArray); - f->glEnableVertexAttribArray(0); // position - f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + glGenVertexArrays(1, &OSDVertexArray); + glBindVertexArray(OSDVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); } return true; } -void DeInit(QOpenGLFunctions_3_2_Core* f) +void DeInit() { for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) { Item& item = *it; - if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture); + if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); if (item.Bitmap) delete[] item.Bitmap; it = ItemQueue.erase(it); } - - if (f) delete Shader; } @@ -329,7 +323,7 @@ void AddMessage(u32 color, const char* text) { if (!Config::ShowOSD) return; - while (Rendering); + Rendering.lock(); Item item; @@ -342,27 +336,29 @@ void AddMessage(u32 color, const char* text) item.GLTextureLoaded = false; ItemQueue.push_back(item); + + Rendering.unlock(); } -void Update(QOpenGLFunctions_3_2_Core* f) +void Update() { if (!Config::ShowOSD) { - Rendering = true; + Rendering.lock(); for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) { Item& item = *it; - if (item.GLTextureLoaded && f) f->glDeleteTextures(1, &item.GLTexture); + if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); if (item.Bitmap) delete[] item.Bitmap; it = ItemQueue.erase(it); } - Rendering = false; + Rendering.unlock(); return; } - Rendering = true; + Rendering.lock(); Uint32 tick_now = SDL_GetTicks(); Uint32 tick_min = tick_now - 2500; @@ -373,7 +369,7 @@ void Update(QOpenGLFunctions_3_2_Core* f) if (item.Timestamp < tick_min) { - if (item.GLTextureLoaded) f->glDeleteTextures(1, &item.GLTexture); + if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); if (item.Bitmap) delete[] item.Bitmap; it = ItemQueue.erase(it); @@ -388,14 +384,14 @@ void Update(QOpenGLFunctions_3_2_Core* f) it++; } - Rendering = false; + Rendering.unlock(); } void DrawNative(QPainter& painter) { if (!Config::ShowOSD) return; - Rendering = true; + Rendering.lock(); u32 y = kOSDMargin; @@ -417,30 +413,30 @@ void DrawNative(QPainter& painter) it++; } - Rendering = false; + Rendering.unlock(); } -void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h) +void DrawGL(float w, float h) { if (!Config::ShowOSD) return; if (!mainWindow || !mainWindow->panel) return; - Rendering = true; + Rendering.lock(); u32 y = kOSDMargin; - Shader->bind(); + glUseProgram(Shader[2]); - f->glUniform2f(uScreenSize, w, h); - f->glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF()); + glUniform2f(uScreenSize, w, h); + glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF()); - f->glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - f->glBindVertexArray(OSDVertexArray); + glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); + glBindVertexArray(OSDVertexArray); - f->glActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); - f->glEnable(GL_BLEND); - f->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) { @@ -448,30 +444,30 @@ void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h) if (!item.GLTextureLoaded) { - f->glGenTextures(1, &item.GLTexture); - f->glBindTexture(GL_TEXTURE_2D, item.GLTexture); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap); + glGenTextures(1, &item.GLTexture); + glBindTexture(GL_TEXTURE_2D, item.GLTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap); item.GLTextureLoaded = true; } - f->glBindTexture(GL_TEXTURE_2D, item.GLTexture); - f->glUniform2i(uOSDPos, kOSDMargin, y); - f->glUniform2i(uOSDSize, item.Width, item.Height); - f->glDrawArrays(GL_TRIANGLES, 0, 2*3); + glBindTexture(GL_TEXTURE_2D, item.GLTexture); + glUniform2i(uOSDPos, kOSDMargin, y); + glUniform2i(uOSDSize, item.Width, item.Height); + glDrawArrays(GL_TRIANGLES, 0, 2*3); y += item.Height; it++; } - f->glDisable(GL_BLEND); - Shader->release(); + glDisable(GL_BLEND); + glUseProgram(0); - Rendering = false; + Rendering.unlock(); } } diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h index d624fc6..907496f 100644 --- a/src/frontend/qt_sdl/OSD.h +++ b/src/frontend/qt_sdl/OSD.h @@ -22,14 +22,14 @@ namespace OSD { -bool Init(QOpenGLFunctions_3_2_Core* f); -void DeInit(QOpenGLFunctions_3_2_Core* f); +bool Init(bool openGL); +void DeInit(); void AddMessage(u32 color, const char* text); -void Update(QOpenGLFunctions_3_2_Core* f); +void Update(); void DrawNative(QPainter& painter); -void DrawGL(QOpenGLFunctions_3_2_Core* f, float w, float h); +void DrawGL(float w, float h); } diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 87a796d..95ec7d3 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -28,6 +28,11 @@ #include "ui_VideoSettingsDialog.h" +inline bool UsesGL() +{ + return (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); +} + VideoSettingsDialog* VideoSettingsDialog::currentDlg = nullptr; @@ -73,6 +78,7 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( if (!Config::ScreenVSync) ui->sbVSyncInterval->setEnabled(false); + setVsyncControlEnable(UsesGL()); if (Config::_3DRenderer == 0) { @@ -88,14 +94,6 @@ VideoSettingsDialog::VideoSettingsDialog(QWidget* parent) : QDialog(parent), ui( ui->cbxGLResolution->setEnabled(true); ui->cbBetterPolygons->setEnabled(true); } - - // sorry - ui->cbVSync->hide(); - ui->cbVSync->setEnabled(false); - ui->sbVSyncInterval->hide(); - ui->sbVSyncInterval->setEnabled(false); - ui->label_2->hide(); - ui->groupBox->layout()->addItem(new QSpacerItem(1, 1, QSizePolicy::Minimum, QSizePolicy::Expanding)); } VideoSettingsDialog::~VideoSettingsDialog() @@ -112,7 +110,7 @@ void VideoSettingsDialog::on_VideoSettingsDialog_accepted() void VideoSettingsDialog::on_VideoSettingsDialog_rejected() { - bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + bool old_gl = UsesGL(); Config::_3DRenderer = oldRenderer; Config::ScreenUseGL = oldGLDisplay; @@ -122,12 +120,17 @@ void VideoSettingsDialog::on_VideoSettingsDialog_rejected() Config::GL_ScaleFactor = oldGLScale; Config::GL_BetterPolygons = oldGLBetterPolygons; - bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); - emit updateVideoSettings(old_gl != new_gl); + emit updateVideoSettings(old_gl != UsesGL()); closeDlg(); } +void VideoSettingsDialog::setVsyncControlEnable(bool hasOGL) +{ + ui->cbVSync->setEnabled(hasOGL); + ui->sbVSyncInterval->setEnabled(hasOGL); +} + void VideoSettingsDialog::onChange3DRenderer(int renderer) { bool old_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); @@ -149,8 +152,7 @@ void VideoSettingsDialog::onChange3DRenderer(int renderer) ui->cbBetterPolygons->setEnabled(true); } - bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); - emit updateVideoSettings(old_gl != new_gl); + emit updateVideoSettings(old_gl != UsesGL()); } void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state) @@ -159,8 +161,9 @@ void VideoSettingsDialog::on_cbGLDisplay_stateChanged(int state) Config::ScreenUseGL = (state != 0); - bool new_gl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); - emit updateVideoSettings(old_gl != new_gl); + setVsyncControlEnable(UsesGL()); + + emit updateVideoSettings(old_gl != UsesGL()); } void VideoSettingsDialog::on_cbVSync_stateChanged(int state) @@ -168,11 +171,13 @@ void VideoSettingsDialog::on_cbVSync_stateChanged(int state) bool vsync = (state != 0); ui->sbVSyncInterval->setEnabled(vsync); Config::ScreenVSync = vsync; + emit updateVideoSettings(false); } void VideoSettingsDialog::on_sbVSyncInterval_valueChanged(int val) { Config::ScreenVSyncInterval = val; + emit updateVideoSettings(false); } void VideoSettingsDialog::on_cbSoftwareThreaded_stateChanged(int state) @@ -189,6 +194,8 @@ void VideoSettingsDialog::on_cbxGLResolution_currentIndexChanged(int idx) Config::GL_ScaleFactor = idx+1; + setVsyncControlEnable(UsesGL()); + emit updateVideoSettings(false); } diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index 527cc93..7fee5bb 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -67,8 +67,9 @@ private slots: void on_cbBetterPolygons_stateChanged(int state); void on_cbSoftwareThreaded_stateChanged(int state); - private: + void setVsyncControlEnable(bool hasOGL); + Ui::VideoSettingsDialog* ui; QButtonGroup* grp3DRenderer; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 88704b6..cd0ea52 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -37,17 +37,20 @@ #include <QMimeData> #include <QVector> #ifndef _WIN32 +#include <QGuiApplication> #include <QSocketNotifier> #include <unistd.h> #include <sys/socket.h> #include <signal.h> +#ifndef APPLE +#include <qpa/qplatformnativeinterface.h> +#endif #endif #include <SDL2/SDL.h> -#ifdef OGLRENDERER_ENABLED #include "OpenGLSupport.h" -#endif +#include "duckstation/gl/context.h" #include "main.h" #include "Input.h" @@ -369,51 +372,121 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); - if (mainWindow->hasOGL) initOpenGL(); + static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this); } -void EmuThread::initOpenGL() +void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix) { - QOpenGLContext* windowctx = mainWindow->getOGLContext(); - QSurfaceFormat format = windowctx->format(); - - format.setSwapInterval(0); + screenSettingsLock.lock(); - oglSurface = new QOffscreenSurface(); - oglSurface->setFormat(format); - oglSurface->create(); - if (!oglSurface->isValid()) + if (lastScreenWidth != windowInfo.surface_width || lastScreenHeight != windowInfo.surface_height) { - // TODO handle this! - printf("oglSurface shat itself :(\n"); - delete oglSurface; - return; + if (oglContext) + oglContext->ResizeSurface(windowInfo.surface_width, windowInfo.surface_height); + lastScreenWidth = windowInfo.surface_width; + lastScreenHeight = windowInfo.surface_height; } - oglContext = new QOpenGLContext(); - oglContext->setFormat(oglSurface->format()); - oglContext->setShareContext(windowctx); - if (!oglContext->create()) + this->filter = filter; + this->windowInfo = windowInfo; + this->numScreens = numScreens; + memcpy(this->screenKind, screenKind, sizeof(int)*numScreens); + memcpy(this->screenMatrix, screenMatrix, sizeof(float)*numScreens*6); + + screenSettingsLock.unlock(); +} + +void EmuThread::initOpenGL() +{ + GL::Context* windowctx = mainWindow->getOGLContext(); + + oglContext = windowctx; + oglContext->MakeCurrent(); + + OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader"); + GLuint pid = screenShaderProgram[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindAttribLocation(pid, 1, "vTexcoord"); + glBindFragDataLocation(pid, 0, "oColor"); + + OpenGL::LinkShaderProgram(screenShaderProgram); + + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0); + + screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); + screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); + + // to prevent bleeding between both parts of the screen + // with bilinear filtering enabled + const int paddedHeight = 192*2+2; + const float padPixels = 1.f / paddedHeight; + + const float vertices[] = { - // TODO handle this! - printf("oglContext shat itself :(\n"); - delete oglContext; - delete oglSurface; - return; - } + 0.f, 0.f, 0.f, 0.f, + 0.f, 192.f, 0.f, 0.5f - padPixels, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 0.f, 0.f, 0.f, 0.f, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 256.f, 0.f, 1.f, 0.f, + + 0.f, 0.f, 0.f, 0.5f + padPixels, + 0.f, 192.f, 0.f, 1.f, + 256.f, 192.f, 1.f, 1.f, + 0.f, 0.f, 0.f, 0.5f + padPixels, + 256.f, 192.f, 1.f, 1.f, + 256.f, 0.f, 1.f, 0.5f + padPixels + }; + + glGenBuffers(1, &screenVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &screenVertexArray); + glBindVertexArray(screenVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); + glEnableVertexAttribArray(1); // texcoord + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); + + glGenTextures(1, &screenTexture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screenTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + // fill the padding + u8 zeroData[256*4*4]; + memset(zeroData, 0, sizeof(zeroData)); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); - oglContext->moveToThread(this); + OSD::Init(true); + + oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); } void EmuThread::deinitOpenGL() { - delete oglContext; - delete oglSurface; + glDeleteTextures(1, &screenTexture); + + glDeleteVertexArrays(1, &screenVertexArray); + glDeleteBuffers(1, &screenVertexBuffer); + + OpenGL::DeleteShaderProgram(screenShaderProgram); + + OSD::DeInit(); + + oglContext->DoneCurrent(); + oglContext = nullptr; + + lastScreenWidth = lastScreenHeight = -1; } void EmuThread::run() { - bool hasOGL = mainWindow->hasOGL; u32 mainScreenPos[3]; NDS::Init(); @@ -428,14 +501,12 @@ void EmuThread::run() videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; -#ifdef OGLRENDERER_ENABLED - if (hasOGL) + if (mainWindow->hasOGL) { - oglContext->makeCurrent(oglSurface); + initOpenGL(); videoRenderer = Config::_3DRenderer; } else -#endif { videoRenderer = 0; } @@ -498,25 +569,24 @@ void EmuThread::run() if (EmuRunning == 3) EmuRunning = 2; // update render settings if needed - if (videoSettingsDirty) + // HACK: + // once the fast forward hotkey is released, we need to update vsync + // to the old setting again + if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) { - if (hasOGL != mainWindow->hasOGL) + if (oglContext) { - hasOGL = mainWindow->hasOGL; -#ifdef OGLRENDERER_ENABLED - if (hasOGL) - { - oglContext->makeCurrent(oglSurface); - videoRenderer = Config::_3DRenderer; - } - else -#endif - { - videoRenderer = 0; - } + oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + videoRenderer = Config::_3DRenderer; } +#ifdef OGLRENDERER_ENABLED else - videoRenderer = hasOGL ? Config::_3DRenderer : 0; +#endif + { + videoRenderer = 0; + } + + videoRenderer = oglContext ? Config::_3DRenderer : 0; videoSettingsDirty = false; @@ -570,15 +640,6 @@ void EmuThread::run() } } -#ifdef OGLRENDERER_ENABLED - if (videoRenderer == 1) - { - FrontBufferLock.lock(); - if (FrontBufferReverseSyncs[FrontBuffer ^ 1]) - glWaitSync(FrontBufferReverseSyncs[FrontBuffer ^ 1], 0, GL_TIMEOUT_IGNORED); - FrontBufferLock.unlock(); - } -#endif // emulate u32 nlines = NDS::RunFrame(); @@ -589,21 +650,17 @@ void EmuThread::run() if (ROMManager::GBASave) ROMManager::GBASave->CheckFlush(); - FrontBufferLock.lock(); - FrontBuffer = GPU::FrontBuffer; -#ifdef OGLRENDERER_ENABLED - if (videoRenderer == 1) + if (!oglContext) { - if (FrontBufferSyncs[FrontBuffer]) - glDeleteSync(FrontBufferSyncs[FrontBuffer]); - FrontBufferSyncs[FrontBuffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - // this is hacky but this is the easiest way to call - // this function without dealling with a ton of - // macro mess - epoxy_glFlush(); + FrontBufferLock.lock(); + FrontBuffer = GPU::FrontBuffer; + FrontBufferLock.unlock(); + } + else + { + FrontBuffer = GPU::FrontBuffer; + drawScreenGL(); } -#endif - FrontBufferLock.unlock(); #ifdef MELONCAP MelonCap::Update(); @@ -612,7 +669,7 @@ void EmuThread::run() if (EmuRunning == 0) break; winUpdateCount++; - if (winUpdateCount >= winUpdateFreq) + if (winUpdateCount >= winUpdateFreq && !oglContext) { emit windowUpdate(); winUpdateCount = 0; @@ -620,6 +677,11 @@ void EmuThread::run() bool fastforward = Input::HotkeyDown(HK_FastForward); + if (fastforward && oglContext && Config::ScreenVSync) + { + oglContext->SetSwapInterval(0); + } + if (Config::AudioSync && !fastforward && audioDevice) { SDL_LockMutex(audioSyncLock); @@ -700,6 +762,21 @@ void EmuThread::run() changeWindowTitle(melontitle); SDL_Delay(75); + + if (oglContext) + drawScreenGL(); + + int contextRequest = ContextRequest; + if (contextRequest == 1) + { + initOpenGL(); + ContextRequest = 0; + } + else if (contextRequest == 2) + { + deinitOpenGL(); + ContextRequest = 0; + } } } @@ -708,12 +785,6 @@ void EmuThread::run() GPU::DeInitRenderer(); NDS::DeInit(); //Platform::LAN_DeInit(); - - if (hasOGL) - { - oglContext->doneCurrent(); - deinitOpenGL(); - } } void EmuThread::changeWindowTitle(char* title) @@ -733,6 +804,18 @@ void EmuThread::emuRun() micOpen(); } +void EmuThread::initContext() +{ + ContextRequest = 1; + while (ContextRequest != 0); +} + +void EmuThread::deinitContext() +{ + ContextRequest = 2; + while (ContextRequest != 0); +} + void EmuThread::emuPause() { EmuPause++; @@ -784,6 +867,72 @@ bool EmuThread::emuIsActive() return (RunningSomething == 1); } +void EmuThread::drawScreenGL() +{ + int w = windowInfo.surface_width; + int h = windowInfo.surface_height; + float factor = windowInfo.surface_scale; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, w, h); + + glUseProgram(screenShaderProgram[2]); + glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); + + int frontbuf = FrontBuffer; + glActiveTexture(GL_TEXTURE0); + +#ifdef OGLRENDERER_ENABLED + if (GPU::Renderer != 0) + { + // hardware-accelerated render + GPU::CurGLCompositor->BindOutputTexture(frontbuf); + } + else +#endif + { + // regular render + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + } + } + + screenSettingsLock.lock(); + + GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBindVertexArray(screenVertexArray); + + for (int i = 0; i < numScreens; i++) + { + glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); + glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); + } + + screenSettingsLock.unlock(); + + OSD::Update(); + OSD::DrawGL(w, h); + + oglContext->SwapBuffers(); +} + ScreenHandler::ScreenHandler(QWidget* widget) { widget->setMouseTracking(true); @@ -1001,12 +1150,12 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenH screenTrans[0].reset(); screenTrans[1].reset(); - OSD::Init(nullptr); + OSD::Init(false); } ScreenPanelNative::~ScreenPanelNative() { - OSD::DeInit(nullptr); + OSD::DeInit(); } void ScreenPanelNative::setupScreenLayout() @@ -1057,7 +1206,7 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) } } - OSD::Update(nullptr); + OSD::Update(); OSD::DrawNative(painter); } @@ -1105,188 +1254,115 @@ void ScreenPanelNative::onScreenLayoutChanged() } -ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent), ScreenHandler(this) -{} - -ScreenPanelGL::~ScreenPanelGL() +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QWidget(parent), ScreenHandler(this) { - makeCurrent(); - - OSD::DeInit(this); - - glDeleteTextures(1, &screenTexture); + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_KeyCompression, false); + setFocusPolicy(Qt::StrongFocus); + setMinimumSize(screenGetMinSize()); +} - glDeleteVertexArrays(1, &screenVertexArray); - glDeleteBuffers(1, &screenVertexBuffer); +ScreenPanelGL::~ScreenPanelGL() +{} - delete screenShader; +bool ScreenPanelGL::createContext() +{ + std::optional<WindowInfo> windowInfo = getWindowInfo(); + std::array<GL::Context::Version, 2> versionsToTry = { + GL::Context::Version{GL::Context::Profile::Core, 4, 3}, + GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; + if (windowInfo.has_value()) + { + glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + glContext->DoneCurrent(); + } - doneCurrent(); + return glContext != nullptr; } -void ScreenPanelGL::setupScreenLayout() +qreal ScreenPanelGL::devicePixelRatioFromScreen() const { - int w = width(); - int h = height(); + const QScreen* screen_for_ratio = window()->windowHandle()->screen(); + if (!screen_for_ratio) + screen_for_ratio = QGuiApplication::primaryScreen(); - screenSetupLayout(w, h); + return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1); } -void ScreenPanelGL::initializeGL() +int ScreenPanelGL::scaledWindowWidth() const { - initializeOpenGLFunctions(); - - const GLubyte* renderer = glGetString(GL_RENDERER); // get renderer string - const GLubyte* version = glGetString(GL_VERSION); // version as a string - printf("OpenGL: renderer: %s\n", renderer); - printf("OpenGL: version: %s\n", version); - - glClearColor(0, 0, 0, 1); - - screenShader = new QOpenGLShaderProgram(this); - screenShader->addShaderFromSourceCode(QOpenGLShader::Vertex, kScreenVS); - screenShader->addShaderFromSourceCode(QOpenGLShader::Fragment, kScreenFS); - - GLuint pid = screenShader->programId(); - glBindAttribLocation(pid, 0, "vPosition"); - glBindAttribLocation(pid, 1, "vTexcoord"); - glBindFragDataLocation(pid, 0, "oColor"); - - screenShader->link(); + return std::max(static_cast<int>(std::ceil(static_cast<qreal>(width()) * devicePixelRatioFromScreen())), 1); +} - screenShader->bind(); - screenShader->setUniformValue("ScreenTex", (GLint)0); - screenShader->release(); +int ScreenPanelGL::scaledWindowHeight() const +{ + return std::max(static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen())), 1); +} - // to prevent bleeding between both parts of the screen - // with bilinear filtering enabled - const int paddedHeight = 192*2+2; - const float padPixels = 1.f / paddedHeight; +std::optional<WindowInfo> ScreenPanelGL::getWindowInfo() +{ + WindowInfo wi; - const float vertices[] = + // Windows and Apple are easy here since there's no display connection. + #if defined(_WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast<void*>(winId()); + #elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast<void*>(winId()); + #else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) { - 0.f, 0.f, 0.f, 0.f, - 0.f, 192.f, 0.f, 0.5f - padPixels, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 0.f, 0.f, 0.f, 0.f, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 256.f, 0.f, 1.f, 0.f, + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = reinterpret_cast<void*>(winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = pni->nativeResourceForWindow("surface", windowHandle()); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } + #endif - 0.f, 0.f, 0.f, 0.5f + padPixels, - 0.f, 192.f, 0.f, 1.f, - 256.f, 192.f, 1.f, 1.f, - 0.f, 0.f, 0.f, 0.5f + padPixels, - 256.f, 192.f, 1.f, 1.f, - 256.f, 0.f, 1.f, 0.5f + padPixels - }; + wi.surface_width = static_cast<u32>(scaledWindowWidth()); + wi.surface_height = static_cast<u32>(scaledWindowHeight()); + wi.surface_scale = static_cast<float>(devicePixelRatioFromScreen()); - glGenBuffers(1, &screenVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + return wi; +} - glGenVertexArrays(1, &screenVertexArray); - glBindVertexArray(screenVertexArray); - glEnableVertexAttribArray(0); // position - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); - glEnableVertexAttribArray(1); // texcoord - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); - glGenTextures(1, &screenTexture); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, screenTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - // fill the padding - u8 zeroData[256*4*4]; - memset(zeroData, 0, sizeof(zeroData)); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); - - OSD::Init(this); +QPaintEngine* ScreenPanelGL::paintEngine() const +{ + return nullptr; } -void ScreenPanelGL::paintGL() +void ScreenPanelGL::setupScreenLayout() { int w = width(); int h = height(); - float factor = devicePixelRatioF(); - - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, w*factor, h*factor); + screenSetupLayout(w, h); if (emuThread) - { - screenShader->bind(); - - screenShader->setUniformValue("uScreenSize", (float)w, (float)h); - screenShader->setUniformValue("uScaleFactor", factor); - - emuThread->FrontBufferLock.lock(); - int frontbuf = emuThread->FrontBuffer; - glActiveTexture(GL_TEXTURE0); - - #ifdef OGLRENDERER_ENABLED - if (GPU::Renderer != 0) - { - if (emuThread->FrontBufferSyncs[emuThread->FrontBuffer]) - glWaitSync(emuThread->FrontBufferSyncs[emuThread->FrontBuffer], 0, GL_TIMEOUT_IGNORED); - // hardware-accelerated render - GPU::CurGLCompositor->BindOutputTexture(frontbuf); - } - else - #endif - { - // regular render - glBindTexture(GL_TEXTURE_2D, screenTexture); - - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) - { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); - } - } - - GLint filter = Config::ScreenFilter ? GL_LINEAR : GL_NEAREST; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBindVertexArray(screenVertexArray); - - GLint transloc = screenShader->uniformLocation("uTransform"); - - for (int i = 0; i < numScreens; i++) - { - glUniformMatrix2x3fv(transloc, 1, GL_TRUE, screenMatrix[i]); - glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); - } - - screenShader->release(); - - if (emuThread->FrontBufferReverseSyncs[emuThread->FrontBuffer]) - glDeleteSync(emuThread->FrontBufferReverseSyncs[emuThread->FrontBuffer]); - emuThread->FrontBufferReverseSyncs[emuThread->FrontBuffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - emuThread->FrontBufferLock.unlock(); - } - - OSD::Update(this); - OSD::DrawGL(this, w*factor, h*factor); + transferLayout(emuThread); } void ScreenPanelGL::resizeEvent(QResizeEvent* event) { setupScreenLayout(); - QOpenGLWidget::resizeEvent(event); -} - -void ScreenPanelGL::resizeGL(int w, int h) -{ + QWidget::resizeEvent(event); } void ScreenPanelGL::mousePressEvent(QMouseEvent* event) @@ -1321,6 +1397,13 @@ bool ScreenPanelGL::event(QEvent* event) return QWidget::event(event); } +void ScreenPanelGL::transferLayout(EmuThread* thread) +{ + std::optional<WindowInfo> windowInfo = getWindowInfo(); + if (windowInfo.has_value()) + thread->updateScreenSettings(Config::ScreenFilter, *windowInfo, numScreens, screenKind, &screenMatrix[0][0]); +} + void ScreenPanelGL::onScreenLayoutChanged() { setMinimumSize(screenGetMinSize()); @@ -1817,6 +1900,18 @@ MainWindow::~MainWindow() { } +void MainWindow::closeEvent(QCloseEvent* event) +{ + if (hasOGL) + { + // we intentionally don't unpause here + emuThread->emuPause(); + emuThread->deinitContext(); + } + + QMainWindow::closeEvent(event); +} + void MainWindow::createScreenPanel() { hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); @@ -1829,17 +1924,7 @@ void MainWindow::createScreenPanel() panel = panelGL; panelWidget = panelGL; - if (!panelGL->isValid()) - hasOGL = false; - else - { - QSurfaceFormat fmt = panelGL->format(); - if (fmt.majorVersion() < 3 || (fmt.majorVersion() == 3 && fmt.minorVersion() < 2)) - hasOGL = false; - } - - if (!hasOGL) - delete panelGL; + panelGL->createContext(); } if (!hasOGL) @@ -1855,12 +1940,12 @@ void MainWindow::createScreenPanel() emit screenLayoutChange(); } -QOpenGLContext* MainWindow::getOGLContext() +GL::Context* MainWindow::getOGLContext() { if (!hasOGL) return nullptr; - QOpenGLWidget* glpanel = dynamic_cast<QOpenGLWidget*>(panel); - return glpanel->context(); + ScreenPanelGL* glpanel = static_cast<ScreenPanelGL*>(panel); + return glpanel->getContext(); } void MainWindow::resizeEvent(QResizeEvent* event) @@ -3002,13 +3087,14 @@ void MainWindow::onChangeIntegerScaling(bool checked) void MainWindow::onChangeScreenFiltering(bool checked) { Config::ScreenFilter = checked?1:0; + + emit screenLayoutChange(); } void MainWindow::onChangeShowOSD(bool checked) { Config::ShowOSD = checked?1:0; } - void MainWindow::onChangeLimitFramerate(bool checked) { Config::LimitFPS = checked?1:0; @@ -3088,19 +3174,20 @@ void MainWindow::onUpdateVideoSettings(bool glchange) if (glchange) { emuThread->emuPause(); + if (hasOGL) emuThread->deinitContext(); - if (hasOGL) - emuThread->deinitOpenGL(); delete panel; createScreenPanel(); connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); - if (hasOGL) emuThread->initOpenGL(); } videoSettingsDirty = true; if (glchange) + { + if (hasOGL) emuThread->initContext(); emuThread->emuUnpause(); + } } @@ -3137,6 +3224,8 @@ int main(int argc, char** argv) { srand(time(nullptr)); + qputenv("QT_SCALE_FACTOR", "1"); + printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); @@ -3191,14 +3280,6 @@ int main(int argc, char** argv) SANITIZE(Config::ScreenAspectBot, 0, 4); #undef SANITIZE - QSurfaceFormat format; - format.setDepthBufferSize(24); - format.setStencilBufferSize(8); - format.setVersion(3, 2); - format.setProfile(QSurfaceFormat::CoreProfile); - format.setSwapInterval(0); - QSurfaceFormat::setDefaultFormat(format); - audioMuted = false; audioSync = SDL_CreateCond(); audioSyncLock = SDL_CreateMutex(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 1977b7f..2e2a9ab 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -19,6 +19,8 @@ #ifndef MAIN_H #define MAIN_H +#include "glad/glad.h" + #include <QApplication> #include <QThread> #include <QWidget> @@ -28,15 +30,15 @@ #include <QActionGroup> #include <QTimer> #include <QMutex> +#include <QScreen> +#include <QCloseEvent> + +#include <atomic> -#include <QOffscreenSurface> -#include <QOpenGLWidget> -#include <QOpenGLContext> -#include <QOpenGLFunctions> -#include <QOpenGLFunctions_3_2_Core> -#include <QOpenGLShaderProgram> +#include <optional> #include "FrontendUtil.h" +#include "duckstation/gl/context.h" class EmuThread : public QThread { @@ -46,9 +48,6 @@ class EmuThread : public QThread public: explicit EmuThread(QObject* parent = nullptr); - void initOpenGL(); - void deinitOpenGL(); - void changeWindowTitle(char* title); // to be called from the UI thread @@ -61,11 +60,13 @@ public: bool emuIsRunning(); bool emuIsActive(); + void initContext(); + void deinitContext(); + int FrontBuffer = 0; QMutex FrontBufferLock; - GLsync FrontBufferReverseSyncs[2] = {nullptr, nullptr}; - GLsync FrontBufferSyncs[2] = {nullptr, nullptr}; + void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); signals: void windowUpdate(); @@ -86,13 +87,31 @@ signals: void swapScreensToggle(); private: - volatile int EmuStatus; + void drawScreenGL(); + void initOpenGL(); + void deinitOpenGL(); + + std::atomic<int> EmuStatus; int PrevEmuStatus; int EmuRunning; int EmuPause; - QOffscreenSurface* oglSurface; - QOpenGLContext* oglContext; + std::atomic<int> ContextRequest = 0; + + GL::Context* oglContext = nullptr; + GLuint screenVertexBuffer, screenVertexArray; + GLuint screenTexture; + GLuint screenShaderProgram[3]; + GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; + + QMutex screenSettingsLock; + WindowInfo windowInfo; + float screenMatrix[Frontend::MaxScreenTransforms][6]; + int screenKind[Frontend::MaxScreenTransforms]; + int numScreens; + bool filter; + + int lastScreenWidth = -1, lastScreenHeight = -1; }; @@ -158,7 +177,7 @@ private: }; -class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpenGLFunctions_3_2_Core +class ScreenPanelGL : public QWidget, public ScreenHandler { Q_OBJECT @@ -166,13 +185,22 @@ public: explicit ScreenPanelGL(QWidget* parent); virtual ~ScreenPanelGL(); + std::optional<WindowInfo> getWindowInfo(); + + bool createContext(); + + GL::Context* getContext() { return glContext.get(); } + + void transferLayout(EmuThread* thread); protected: - void initializeGL() override; - void paintGL() override; + qreal devicePixelRatioFromScreen() const; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + + QPaintEngine* paintEngine() const override; void resizeEvent(QResizeEvent* event) override; - void resizeGL(int w, int h) override; void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; @@ -180,16 +208,14 @@ protected: void tabletEvent(QTabletEvent* event) override; bool event(QEvent* event) override; + private slots: void onScreenLayoutChanged(); private: void setupScreenLayout(); - QOpenGLShaderProgram* screenShader; - GLuint screenVertexBuffer; - GLuint screenVertexArray; - GLuint screenTexture; + std::unique_ptr<GL::Context> glContext; }; class MelonApplication : public QApplication @@ -210,7 +236,7 @@ public: ~MainWindow(); bool hasOGL; - QOpenGLContext* getOGLContext(); + GL::Context* getOGLContext(); bool preloadROMs(QString filename, QString gbafilename); @@ -306,6 +332,8 @@ private slots: void onFullscreenToggled(); private: + void closeEvent(QCloseEvent* event); + QStringList currentROM; QStringList currentGBAROM; QList<QString> recentFileList; diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index ca835c0..37b964d 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -23,7 +23,6 @@ const char* kScreenVS = R"(#version 140 uniform vec2 uScreenSize; uniform mat2x3 uTransform; -uniform float uScaleFactor; in vec2 vPosition; in vec2 vTexcoord; @@ -34,9 +33,9 @@ void main() { vec4 fpos; - fpos.xy = vec3(vPosition, 1.0) * uTransform * uScaleFactor; + fpos.xy = vec3(vPosition, 1.0) * uTransform; - fpos.xy = ((fpos.xy * 2.0) / (uScreenSize * uScaleFactor)) - 1.0; + fpos.xy = ((fpos.xy * 2.0) / uScreenSize) - 1.0; fpos.y *= -1; fpos.z = 0.0; fpos.w = 1.0; |