Qt: Add checking and downgrading OpenGL support outside of the painter
Vicki Pfau vi@endrift.com
Thu, 03 Dec 2020 23:18:55 -0800
5 files changed,
84 insertions(+),
26 deletions(-)
M
CHANGES
→
CHANGES
@@ -78,6 +78,7 @@ - Qt: Fix cancelling pausing before the frame ends
- Qt: Fix gamepad event dispatching (fixes mgba.io/i/1922) - Qt: Pre-attach GDB stub when launching with -g (fixes mgba.io/i/1950) - Qt: Fix crash when editing shortcuts with none selected (fixes mgba.io/i/1964) + - Qt: Fix crashing when no OpenGL context can be obtained - SM83: Simplify register pair access on big endian - SM83: Disassemble STOP as one byte - Wii: Fix crash on unloading irregularly sized GBA ROMs
M
src/platform/qt/Display.cpp
→
src/platform/qt/Display.cpp
@@ -7,6 +7,7 @@ #include "Display.h"
#include "DisplayGL.h" #include "DisplayQt.h" +#include "LogController.h" using namespace QGBA;@@ -27,16 +28,31 @@ switch (s_driver) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) case Driver::OPENGL: if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { - format.setVersion(3, 0); + format.setVersion(2, 0); } else { format.setVersion(3, 2); } format.setProfile(QSurfaceFormat::CoreProfile); + if (!DisplayGL::supportsFormat(format)) { +#ifdef BUILD_GL + LOG(QT, WARN) << ("Failed to create an OpenGL Core context, trying old-style..."); + format.setVersion(1, 4); + format.setOption(QSurfaceFormat::DeprecatedFunctions); + if (!DisplayGL::supportsFormat(format)) { + return nullptr; + } +#else + return nullptr; +#endif + } return new DisplayGL(format, parent); #endif #ifdef BUILD_GL case Driver::OPENGL1: format.setVersion(1, 4); + if (!DisplayGL::supportsFormat(format)) { + return nullptr; + } return new DisplayGL(format, parent); #endif
M
src/platform/qt/DisplayGL.cpp
→
src/platform/qt/DisplayGL.cpp
@@ -10,9 +10,10 @@
#include "CoreController.h" #include <QApplication> +#include <QMutexLocker> +#include <QOffscreenSurface> #include <QOpenGLContext> #include <QOpenGLPaintDevice> -#include <QMutexLocker> #include <QResizeEvent> #include <QScreen> #include <QTimer>@@ -33,6 +34,15 @@ #endif
#endif using namespace QGBA; + +QHash<QSurfaceFormat, bool> DisplayGL::s_supports; + +uint qHash(const QSurfaceFormat& format, uint seed) { + QByteArray representation; + QDataStream stream(&representation, QIODevice::WriteOnly); + stream << format.version() << format.renderableType() << format.profile(); + return qHash(representation, seed); +} DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent) : Display(parent)@@ -98,6 +108,44 @@ #endif
setUpdatesEnabled(false); } +bool DisplayGL::supportsFormat(const QSurfaceFormat& format) { + if (!s_supports.contains(format)) { + QOpenGLContext context; + context.setFormat(format); + if (!context.create()) { + s_supports[format] = false; + return false; + } + auto foundVersion = context.format().version(); + if (foundVersion == format.version()) { + // Match! + s_supports[format] = true; + } else if (format.version() >= qMakePair(3, 2) && foundVersion > format.version()) { + // At least as good + s_supports[format] = true; + } else if (format.majorVersion() == 1 && (foundVersion < qMakePair(3, 0) || + context.format().profile() == QSurfaceFormat::CompatibilityProfile || + context.format().testOption(QSurfaceFormat::DeprecatedFunctions))) { + // Supports the old stuff + s_supports[format] = true; + } else if (!context.isOpenGLES() && format.version() >= qMakePair(2, 1) && foundVersion < qMakePair(3, 0) && foundVersion >= qMakePair(2, 1)) { + // Weird edge case we support if ARB_framebuffer_object is present + QOffscreenSurface surface; + surface.create(); + if (!context.makeCurrent(&surface)) { + s_supports[format] = false; + return false; + } + s_supports[format] = context.hasExtension("GL_ARB_framebuffer_object"); + context.doneCurrent(); + } else { + // No match + s_supports[format] = false; + } + } + return s_supports[format]; +} + void DisplayGL::stopDrawing() { if (m_drawThread) { m_isDrawing = false;@@ -236,6 +284,7 @@ PainterGL::PainterGL(QWindow* surface, const QSurfaceFormat& format)
: m_surface(surface) , m_format(format) { + m_supportsShaders = m_format.version() >= qMakePair(2, 0); for (auto& buf : m_buffers) { m_free.append(&buf.front()); }@@ -260,24 +309,6 @@ m_gl->setFormat(m_format);
m_gl->create(); makeCurrent(); - auto version = m_gl->format().version(); - QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' '); - - int forceVersion = 0; - if (m_format.majorVersion() < 2) { - forceVersion = 1; - } - - if ((version == qMakePair(2, 1) && !extensions.contains("GL_ARB_framebuffer_object")) || version == qMakePair(2, 0)) { - QSurfaceFormat newFormat(m_format); - newFormat.setVersion(1, 4); - forceVersion = 1; - m_gl->doneCurrent(); - m_gl->setFormat(newFormat); - m_gl->create(); - makeCurrent(); - } - #ifdef BUILD_GL mGLContext* glBackend; #endif@@ -288,13 +319,11 @@
m_window = std::make_unique<QOpenGLPaintDevice>(); #ifdef BUILD_GLES2 - version = m_gl->format().version(); - extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' '); - if (forceVersion != 1 && ((version == qMakePair(2, 1) && extensions.contains("GL_ARB_framebuffer_object")) || version.first > 2)) { + auto version = m_format.version(); + if (version >= qMakePair(2, 0)) { gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context))); mGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; - m_supportsShaders = true; } #endif@@ -303,7 +332,6 @@ if (!m_backend) {
glBackend = static_cast<mGLContext*>(malloc(sizeof(mGLContext))); mGLContextCreate(glBackend); m_backend = &glBackend->d; - m_supportsShaders = false; } #endif m_backend->swap = [](VideoBackend* v) {
M
src/platform/qt/DisplayGL.h
→
src/platform/qt/DisplayGL.h
@@ -18,9 +18,10 @@ #endif
#include <QAtomicInt> #include <QElapsedTimer> -#include <QOpenGLContext> +#include <QHash> #include <QList> #include <QMouseEvent> +#include <QOpenGLContext> #include <QPainter> #include <QQueue> #include <QThread>@@ -33,6 +34,8 @@
#include "platform/video-backend.h" class QOpenGLPaintDevice; + +uint qHash(const QSurfaceFormat&, uint seed = 0); namespace QGBA {@@ -51,6 +54,8 @@ VideoShader* shaders() override;
void setVideoProxy(std::shared_ptr<VideoProxy>) override; int framebufferHandle() override; + static bool supportsFormat(const QSurfaceFormat&); + public slots: void stopDrawing() override; void pauseDrawing() override;@@ -73,6 +78,8 @@ virtual void resizeEvent(QResizeEvent*) override;
private: void resizePainter(); + + static QHash<QSurfaceFormat, bool> s_supports; bool m_isDrawing = false; bool m_hasStarted = false;
M
src/platform/qt/Window.cpp
→
src/platform/qt/Window.cpp
@@ -884,6 +884,12 @@ m_display->stopDrawing();
detachWidget(m_display.get()); } m_display = std::unique_ptr<Display>(Display::create(this)); + if (!m_display) { + LOG(QT, ERROR) << tr("Failed to create an appropriate display device, falling back to software display. " + "Games may run slowly, especially with larger windows."); + Display::setDriver(Display::Driver::QT); + m_display = std::unique_ptr<Display>(Display::create(this)); + } #if defined(BUILD_GL) || defined(BUILD_GLES2) m_shaderView.reset(); m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config);