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: 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

View File

@ -7,6 +7,7 @@
#include "DisplayGL.h"
#include "DisplayQt.h"
#include "LogController.h"
using namespace QGBA;
@ -27,16 +28,31 @@ Display* Display::create(QWidget* parent) {
#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

View File

@ -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>
@ -34,6 +35,15 @@
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 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
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 @@ void PainterGL::create() {
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 @@ void PainterGL::create() {
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 @@ void PainterGL::create() {
glBackend = static_cast<mGLContext*>(malloc(sizeof(mGLContext)));
mGLContextCreate(glBackend);
m_backend = &glBackend->d;
m_supportsShaders = false;
}
#endif
m_backend->swap = [](VideoBackend* v) {

View File

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

View File

@ -884,6 +884,12 @@ void Window::reloadDisplayDriver() {
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);