mirror of https://github.com/mgba-emu/mgba.git
Qt: Create OpenGL context on a thread without moving it
This commit is contained in:
parent
7be68ffd1d
commit
74edd964da
|
@ -34,44 +34,15 @@ using namespace QGBA;
|
|||
|
||||
DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
||||
: Display(parent)
|
||||
, m_gl(nullptr)
|
||||
{
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
windowHandle()->create();
|
||||
|
||||
// This can spontaneously re-enter into this->resizeEvent before creation is done, so we
|
||||
// need to make sure it's initialized to nullptr before we assign the new object to it
|
||||
m_gl = new QOpenGLContext;
|
||||
m_gl->setFormat(format);
|
||||
m_gl->create();
|
||||
|
||||
m_gl->makeCurrent(windowHandle());
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
#endif
|
||||
auto version = m_gl->format().version();
|
||||
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
|
||||
|
||||
int forceVersion = 0;
|
||||
if (format.majorVersion() < 2) {
|
||||
forceVersion = 1;
|
||||
}
|
||||
|
||||
if ((version == qMakePair(2, 1) && !extensions.contains("GL_ARB_framebuffer_object")) || version == qMakePair(2, 0)) {
|
||||
QSurfaceFormat newFormat(format);
|
||||
newFormat.setVersion(1, 4);
|
||||
forceVersion = 1;
|
||||
m_gl->setFormat(newFormat);
|
||||
m_gl->create();
|
||||
}
|
||||
|
||||
m_painter = new PainterGL(windowHandle(), m_gl, forceVersion);
|
||||
m_painter = std::make_unique<PainterGL>(windowHandle(), format);
|
||||
}
|
||||
|
||||
DisplayGL::~DisplayGL() {
|
||||
stopDrawing();
|
||||
delete m_painter;
|
||||
delete m_gl;
|
||||
}
|
||||
|
||||
bool DisplayGL::supportsShaders() const {
|
||||
|
@ -81,7 +52,7 @@ bool DisplayGL::supportsShaders() const {
|
|||
VideoShader* DisplayGL::shaders() {
|
||||
VideoShader* shaders = nullptr;
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "shaders", Qt::BlockingQueuedConnection, Q_RETURN_ARG(VideoShader*, shaders));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "shaders", Qt::BlockingQueuedConnection, Q_RETURN_ARG(VideoShader*, shaders));
|
||||
} else {
|
||||
shaders = m_painter->shaders();
|
||||
}
|
||||
|
@ -96,16 +67,13 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
|||
m_painter->setContext(controller);
|
||||
m_painter->setMessagePainter(messagePainter());
|
||||
m_context = controller;
|
||||
m_painter->resize(size());
|
||||
m_drawThread = new QThread(this);
|
||||
m_drawThread->setObjectName("Painter Thread");
|
||||
m_gl->doneCurrent();
|
||||
m_gl->moveToThread(m_drawThread);
|
||||
m_painter->moveToThread(m_drawThread);
|
||||
if (videoProxy()) {
|
||||
videoProxy()->moveToThread(m_drawThread);
|
||||
}
|
||||
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
||||
connect(m_drawThread, &QThread::started, m_painter.get(), &PainterGL::start);
|
||||
m_drawThread->start();
|
||||
|
||||
lockAspectRatio(isAspectRatioLocked());
|
||||
|
@ -126,15 +94,10 @@ void DisplayGL::stopDrawing() {
|
|||
if (m_drawThread) {
|
||||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "stop", Qt::BlockingQueuedConnection);
|
||||
m_drawThread->exit();
|
||||
m_drawThread->wait();
|
||||
m_drawThread = nullptr;
|
||||
|
||||
m_gl->makeCurrent(windowHandle());
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
#endif
|
||||
setUpdatesEnabled(true);
|
||||
}
|
||||
m_context.reset();
|
||||
|
@ -144,7 +107,7 @@ void DisplayGL::pauseDrawing() {
|
|||
if (m_drawThread) {
|
||||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection);
|
||||
#ifndef Q_OS_MAC
|
||||
setUpdatesEnabled(true);
|
||||
#endif
|
||||
|
@ -155,7 +118,7 @@ void DisplayGL::unpauseDrawing() {
|
|||
if (m_drawThread) {
|
||||
m_isDrawing = true;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
|
||||
#ifndef Q_OS_MAC
|
||||
setUpdatesEnabled(false);
|
||||
#endif
|
||||
|
@ -164,69 +127,69 @@ void DisplayGL::unpauseDrawing() {
|
|||
|
||||
void DisplayGL::forceDraw() {
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "forceDraw");
|
||||
QMetaObject::invokeMethod(m_painter.get(), "forceDraw");
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::lockAspectRatio(bool lock) {
|
||||
Display::lockAspectRatio(lock);
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Q_ARG(bool, lock));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "lockAspectRatio", Q_ARG(bool, lock));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::lockIntegerScaling(bool lock) {
|
||||
Display::lockIntegerScaling(lock);
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "lockIntegerScaling", Q_ARG(bool, lock));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "lockIntegerScaling", Q_ARG(bool, lock));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::interframeBlending(bool enable) {
|
||||
Display::interframeBlending(enable);
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "interframeBlending", Q_ARG(bool, enable));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "interframeBlending", Q_ARG(bool, enable));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::showOSDMessages(bool enable) {
|
||||
Display::showOSDMessages(enable);
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "showOSD", Q_ARG(bool, enable));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "showOSD", Q_ARG(bool, enable));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::filter(bool filter) {
|
||||
Display::filter(filter);
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "filter", Q_ARG(bool, filter));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "filter", Q_ARG(bool, filter));
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::framePosted() {
|
||||
if (m_drawThread) {
|
||||
m_painter->enqueue(m_context->drawContext());
|
||||
QMetaObject::invokeMethod(m_painter, "draw");
|
||||
QMetaObject::invokeMethod(m_painter.get(), "draw");
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::setShaders(struct VDir* shaders) {
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "setShaders", Qt::BlockingQueuedConnection, Q_ARG(struct VDir*, shaders));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "setShaders", Qt::BlockingQueuedConnection, Q_ARG(struct VDir*, shaders));
|
||||
} else {
|
||||
m_painter->setShaders(shaders);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayGL::clearShaders() {
|
||||
QMetaObject::invokeMethod(m_painter, "clearShaders");
|
||||
QMetaObject::invokeMethod(m_painter.get(), "clearShaders");
|
||||
}
|
||||
|
||||
void DisplayGL::resizeContext() {
|
||||
if (m_drawThread) {
|
||||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
QMetaObject::invokeMethod(m_painter, "resizeContext", Qt::BlockingQueuedConnection);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "resizeContext", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,7 +198,7 @@ void DisplayGL::setVideoScale(int scale) {
|
|||
m_isDrawing = false;
|
||||
CoreController::Interrupter interrupter(m_context);
|
||||
mCoreConfigSetIntValue(&m_context->thread()->core->config, "videoScale", scale);
|
||||
QMetaObject::invokeMethod(m_painter, "resizeContext", Qt::BlockingQueuedConnection);
|
||||
QMetaObject::invokeMethod(m_painter.get(), "resizeContext", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,7 +209,7 @@ void DisplayGL::resizeEvent(QResizeEvent* event) {
|
|||
|
||||
void DisplayGL::resizePainter() {
|
||||
if (m_drawThread) {
|
||||
QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));
|
||||
QMetaObject::invokeMethod(m_painter.get(), "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,10 +225,47 @@ int DisplayGL::framebufferHandle() {
|
|||
return m_painter->glTex();
|
||||
}
|
||||
|
||||
PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
||||
: m_gl(parent)
|
||||
, m_surface(surface)
|
||||
PainterGL::PainterGL(QWindow* surface, const QSurfaceFormat& format)
|
||||
: m_surface(surface)
|
||||
, m_format(format)
|
||||
{
|
||||
for (auto& buf : m_buffers) {
|
||||
m_free.append(&buf.front());
|
||||
}
|
||||
}
|
||||
|
||||
PainterGL::~PainterGL() {
|
||||
if (m_gl) {
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void PainterGL::create() {
|
||||
m_gl = std::make_unique<QOpenGLContext>();
|
||||
m_gl->setFormat(m_format);
|
||||
m_gl->create();
|
||||
|
||||
m_gl->makeCurrent(m_surface);
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
#endif
|
||||
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->setFormat(newFormat);
|
||||
m_gl->create();
|
||||
}
|
||||
m_gl->makeCurrent(m_surface);
|
||||
|
||||
#ifdef BUILD_GL
|
||||
mGLContext* glBackend;
|
||||
#endif
|
||||
|
@ -273,24 +273,14 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
mGLES2Context* gl2Backend;
|
||||
#endif
|
||||
|
||||
m_gl->makeCurrent(m_surface);
|
||||
m_window = new QOpenGLPaintDevice;
|
||||
{
|
||||
// XXX: Qt creates TLS for OpenGL objects in the local thread
|
||||
// We need to prevent that thread from being the painter thread
|
||||
// Qt also caches the engine object on the device if a different
|
||||
// engine is active, so let's make a temporary one
|
||||
QOpenGLPaintDevice* fakeDevice = new QOpenGLPaintDevice;
|
||||
QPainter fakePainter(fakeDevice);
|
||||
m_window->paintEngine();
|
||||
}
|
||||
m_window = std::make_unique<QOpenGLPaintDevice>();
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
#endif
|
||||
|
||||
#ifdef BUILD_GLES2
|
||||
auto version = m_gl->format().version();
|
||||
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
|
||||
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)) {
|
||||
gl2Backend = static_cast<mGLES2Context*>(malloc(sizeof(mGLES2Context)));
|
||||
mGLES2ContextCreate(gl2Backend);
|
||||
|
@ -330,13 +320,12 @@ PainterGL::PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion)
|
|||
m_backend->filter = false;
|
||||
m_backend->lockAspectRatio = false;
|
||||
m_backend->interframeBlending = false;
|
||||
|
||||
for (auto& buf : m_buffers) {
|
||||
m_free.append(&buf.front());
|
||||
}
|
||||
}
|
||||
|
||||
PainterGL::~PainterGL() {
|
||||
void PainterGL::destroy() {
|
||||
if (!m_gl) {
|
||||
return;
|
||||
}
|
||||
m_gl->makeCurrent(m_surface);
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
|
@ -357,14 +346,15 @@ PainterGL::~PainterGL() {
|
|||
#endif
|
||||
m_backend->deinit(m_backend);
|
||||
m_gl->doneCurrent();
|
||||
m_window.reset();
|
||||
m_gl.reset();
|
||||
|
||||
free(m_backend);
|
||||
m_backend = nullptr;
|
||||
delete m_window;
|
||||
}
|
||||
|
||||
void PainterGL::setContext(std::shared_ptr<CoreController> context) {
|
||||
m_context = context;
|
||||
resizeContext();
|
||||
}
|
||||
|
||||
void PainterGL::resizeContext() {
|
||||
|
@ -419,6 +409,9 @@ void PainterGL::filter(bool filter) {
|
|||
}
|
||||
|
||||
void PainterGL::start() {
|
||||
if (!m_gl) {
|
||||
create();
|
||||
}
|
||||
m_gl->makeCurrent(m_surface);
|
||||
#if defined(_WIN32) && defined(USE_EPOXY)
|
||||
epoxy_handle_external_wglMakeCurrent();
|
||||
|
@ -429,6 +422,7 @@ void PainterGL::start() {
|
|||
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
|
||||
}
|
||||
#endif
|
||||
resizeContext();
|
||||
|
||||
m_buffer = nullptr;
|
||||
m_active = true;
|
||||
|
@ -456,7 +450,7 @@ void PainterGL::draw() {
|
|||
}
|
||||
|
||||
void PainterGL::forceDraw() {
|
||||
m_painter.begin(m_window);
|
||||
m_painter.begin(m_window.get());
|
||||
performDraw();
|
||||
m_painter.end();
|
||||
if (!m_context->thread()->impl->sync.audioWait && !m_context->thread()->impl->sync.videoFrameWait) {
|
||||
|
@ -477,12 +471,10 @@ void PainterGL::stop() {
|
|||
if (m_videoProxy) {
|
||||
m_videoProxy->reset();
|
||||
}
|
||||
m_gl->doneCurrent();
|
||||
m_gl->moveToThread(m_surface->thread());
|
||||
m_context.reset();
|
||||
moveToThread(m_gl->thread());
|
||||
destroy();
|
||||
moveToThread(m_surface->thread());
|
||||
if (m_videoProxy) {
|
||||
m_videoProxy->moveToThread(m_gl->thread());
|
||||
m_videoProxy->moveToThread(m_surface->thread());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -74,8 +74,7 @@ private:
|
|||
void resizePainter();
|
||||
|
||||
bool m_isDrawing = false;
|
||||
QOpenGLContext* m_gl;
|
||||
PainterGL* m_painter;
|
||||
std::unique_ptr<PainterGL> m_painter;
|
||||
QThread* m_drawThread = nullptr;
|
||||
std::shared_ptr<CoreController> m_context;
|
||||
};
|
||||
|
@ -84,7 +83,7 @@ class PainterGL : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PainterGL(QWindow* surface, QOpenGLContext* parent, int forceVersion = 0);
|
||||
PainterGL(QWindow* surface, const QSurfaceFormat& format);
|
||||
~PainterGL();
|
||||
|
||||
void setContext(std::shared_ptr<CoreController>);
|
||||
|
@ -120,6 +119,8 @@ private:
|
|||
void performDraw();
|
||||
void dequeue();
|
||||
void dequeueAll();
|
||||
void create();
|
||||
void destroy();
|
||||
|
||||
std::array<std::array<uint32_t, 0x100000>, 3> m_buffers;
|
||||
QList<uint32_t*> m_free;
|
||||
|
@ -128,8 +129,9 @@ private:
|
|||
QPainter m_painter;
|
||||
QMutex m_mutex;
|
||||
QWindow* m_surface;
|
||||
QOpenGLPaintDevice* m_window;
|
||||
QOpenGLContext* m_gl;
|
||||
QSurfaceFormat m_format;
|
||||
std::unique_ptr<QOpenGLPaintDevice> m_window;
|
||||
std::unique_ptr<QOpenGLContext> m_gl;
|
||||
bool m_active = false;
|
||||
bool m_started = false;
|
||||
std::shared_ptr<CoreController> m_context = nullptr;
|
||||
|
|
Loading…
Reference in New Issue