Qt: Add checking and downgrading OpenGL support outside of the painter

This commit is contained in:
Vicki Pfau 2020-12-03 23:18:55 -08:00
parent e31de6b470
commit a8a7372083
5 changed files with 84 additions and 26 deletions

View File

@ -78,6 +78,7 @@ Other fixes:
- Qt: Fix gamepad event dispatching (fixes mgba.io/i/1922) - 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: 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 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: Simplify register pair access on big endian
- SM83: Disassemble STOP as one byte - SM83: Disassemble STOP as one byte
- Wii: Fix crash on unloading irregularly sized GBA ROMs - Wii: Fix crash on unloading irregularly sized GBA ROMs

View File

@ -7,6 +7,7 @@
#include "DisplayGL.h" #include "DisplayGL.h"
#include "DisplayQt.h" #include "DisplayQt.h"
#include "LogController.h"
using namespace QGBA; using namespace QGBA;
@ -27,16 +28,31 @@ Display* Display::create(QWidget* parent) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
case Driver::OPENGL: case Driver::OPENGL:
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
format.setVersion(3, 0); format.setVersion(2, 0);
} else { } else {
format.setVersion(3, 2); format.setVersion(3, 2);
} }
format.setProfile(QSurfaceFormat::CoreProfile); 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); return new DisplayGL(format, parent);
#endif #endif
#ifdef BUILD_GL #ifdef BUILD_GL
case Driver::OPENGL1: case Driver::OPENGL1:
format.setVersion(1, 4); format.setVersion(1, 4);
if (!DisplayGL::supportsFormat(format)) {
return nullptr;
}
return new DisplayGL(format, parent); return new DisplayGL(format, parent);
#endif #endif

View File

@ -10,9 +10,10 @@
#include "CoreController.h" #include "CoreController.h"
#include <QApplication> #include <QApplication>
#include <QMutexLocker>
#include <QOffscreenSurface>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QOpenGLPaintDevice> #include <QOpenGLPaintDevice>
#include <QMutexLocker>
#include <QResizeEvent> #include <QResizeEvent>
#include <QScreen> #include <QScreen>
#include <QTimer> #include <QTimer>
@ -34,6 +35,15 @@
using namespace QGBA; 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) DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
: Display(parent) : Display(parent)
{ {
@ -98,6 +108,44 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
setUpdatesEnabled(false); 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() { void DisplayGL::stopDrawing() {
if (m_drawThread) { if (m_drawThread) {
m_isDrawing = false; m_isDrawing = false;
@ -236,6 +284,7 @@ PainterGL::PainterGL(QWindow* surface, const QSurfaceFormat& format)
: m_surface(surface) : m_surface(surface)
, m_format(format) , m_format(format)
{ {
m_supportsShaders = m_format.version() >= qMakePair(2, 0);
for (auto& buf : m_buffers) { for (auto& buf : m_buffers) {
m_free.append(&buf.front()); m_free.append(&buf.front());
} }
@ -260,24 +309,6 @@ void PainterGL::create() {
m_gl->create(); m_gl->create();
makeCurrent(); 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 #ifdef BUILD_GL
mGLContext* glBackend; mGLContext* glBackend;
#endif #endif
@ -288,13 +319,11 @@ void PainterGL::create() {
m_window = std::make_unique<QOpenGLPaintDevice>(); m_window = std::make_unique<QOpenGLPaintDevice>();
#ifdef BUILD_GLES2 #ifdef BUILD_GLES2
version = m_gl->format().version(); auto version = m_format.version();
extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' '); if (version >= qMakePair(2, 0)) {
if (forceVersion != 1 && ((version == qMakePair(2, 1) && extensions.contains("GL_ARB_framebuffer_object")) || version.first > 2)) {
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context))); gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
mGLES2ContextCreate(gl2Backend); mGLES2ContextCreate(gl2Backend);
m_backend = &gl2Backend->d; m_backend = &gl2Backend->d;
m_supportsShaders = true;
} }
#endif #endif
@ -303,7 +332,6 @@ void PainterGL::create() {
glBackend = static_cast<mGLContext*>(malloc(sizeof(mGLContext))); glBackend = static_cast<mGLContext*>(malloc(sizeof(mGLContext)));
mGLContextCreate(glBackend); mGLContextCreate(glBackend);
m_backend = &glBackend->d; m_backend = &glBackend->d;
m_supportsShaders = false;
} }
#endif #endif
m_backend->swap = [](VideoBackend* v) { m_backend->swap = [](VideoBackend* v) {

View File

@ -18,9 +18,10 @@
#include <QAtomicInt> #include <QAtomicInt>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QOpenGLContext> #include <QHash>
#include <QList> #include <QList>
#include <QMouseEvent> #include <QMouseEvent>
#include <QOpenGLContext>
#include <QPainter> #include <QPainter>
#include <QQueue> #include <QQueue>
#include <QThread> #include <QThread>
@ -34,6 +35,8 @@
class QOpenGLPaintDevice; class QOpenGLPaintDevice;
uint qHash(const QSurfaceFormat&, uint seed = 0);
namespace QGBA { namespace QGBA {
class PainterGL; class PainterGL;
@ -51,6 +54,8 @@ public:
void setVideoProxy(std::shared_ptr<VideoProxy>) override; void setVideoProxy(std::shared_ptr<VideoProxy>) override;
int framebufferHandle() override; int framebufferHandle() override;
static bool supportsFormat(const QSurfaceFormat&);
public slots: public slots:
void stopDrawing() override; void stopDrawing() override;
void pauseDrawing() override; void pauseDrawing() override;
@ -74,6 +79,8 @@ protected:
private: private:
void resizePainter(); void resizePainter();
static QHash<QSurfaceFormat, bool> s_supports;
bool m_isDrawing = false; bool m_isDrawing = false;
bool m_hasStarted = false; bool m_hasStarted = false;
std::unique_ptr<PainterGL> m_painter; std::unique_ptr<PainterGL> m_painter;

View File

@ -884,6 +884,12 @@ void Window::reloadDisplayDriver() {
detachWidget(m_display.get()); detachWidget(m_display.get());
} }
m_display = std::unique_ptr<Display>(Display::create(this)); 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) #if defined(BUILD_GL) || defined(BUILD_GLES2)
m_shaderView.reset(); m_shaderView.reset();
m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config); m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config);