mirror of https://github.com/mgba-emu/mgba.git
Qt: Add QOpenGLWidget cross-thread codepath for macOS (fixes #1754)
This commit is contained in:
parent
c4e481c110
commit
b6e2faaba9
1
CHANGES
1
CHANGES
|
@ -35,6 +35,7 @@ Misc:
|
||||||
- Qt: Save positions of multiplayer windows (closes mgba.io/i/2128)
|
- Qt: Save positions of multiplayer windows (closes mgba.io/i/2128)
|
||||||
- Qt: Add optional frame counter to OSD (closes mgba.io/i/1728)
|
- Qt: Add optional frame counter to OSD (closes mgba.io/i/1728)
|
||||||
- Qt: Add optional emulation-related information on reset (closes mgba.io/i/1780)
|
- Qt: Add optional emulation-related information on reset (closes mgba.io/i/1780)
|
||||||
|
- Qt: Add QOpenGLWidget cross-thread codepath for macOS (fixes mgba.io/i/1754)
|
||||||
- Windows: Attach to console if present
|
- Windows: Attach to console if present
|
||||||
|
|
||||||
0.9.3: (2021-12-17)
|
0.9.3: (2021-12-17)
|
||||||
|
|
|
@ -235,6 +235,7 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h)
|
||||||
context->shaders[n].dirty = true;
|
context->shaders[n].dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
context->finalShader.dirty = true;
|
||||||
glBindTexture(GL_TEXTURE_2D, context->finalShader.tex);
|
glBindTexture(GL_TEXTURE_2D, context->finalShader.tex);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, drawW, drawH, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, context->finalShader.fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, context->finalShader.fbo);
|
||||||
|
@ -375,7 +376,6 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
||||||
glGetIntegerv(GL_VIEWPORT, viewport);
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
||||||
|
|
||||||
context->finalShader.filter = v->filter;
|
context->finalShader.filter = v->filter;
|
||||||
context->finalShader.dirty = true;
|
|
||||||
_drawShader(context, &context->initialShader);
|
_drawShader(context, &context->initialShader);
|
||||||
if (v->interframeBlending) {
|
if (v->interframeBlending) {
|
||||||
context->interframeShader.blend = true;
|
context->interframeShader.blend = true;
|
||||||
|
@ -437,15 +437,19 @@ void mGLES2ContextCreate(struct mGLES2Context* context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void mGLES2ContextUseFramebuffer(struct mGLES2Context* context) {
|
void mGLES2ContextUseFramebuffer(struct mGLES2Context* context) {
|
||||||
glGenFramebuffers(1, &context->finalShader.fbo);
|
if (!context->finalShader.fbo) {
|
||||||
glGenTextures(1, &context->finalShader.tex);
|
glGenFramebuffers(1, &context->finalShader.fbo);
|
||||||
|
}
|
||||||
|
if (!context->finalShader.tex) {
|
||||||
|
glGenTextures(1, &context->finalShader.tex);
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, context->finalShader.tex);
|
glBindTexture(GL_TEXTURE_2D, context->finalShader.tex);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
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_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
}
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, context->finalShader.fbo);
|
glBindFramebuffer(GL_FRAMEBUFFER, context->finalShader.fbo);
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context->finalShader.tex, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, context->finalShader.tex, 0);
|
||||||
|
|
|
@ -4,6 +4,7 @@ set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
set(PLATFORM_SRC)
|
set(PLATFORM_SRC)
|
||||||
set(QT_STATIC OFF)
|
set(QT_STATIC OFF)
|
||||||
|
set(QT_DEFINES)
|
||||||
|
|
||||||
if(BUILD_SDL)
|
if(BUILD_SDL)
|
||||||
add_definitions(-DBUILD_SDL)
|
add_definitions(-DBUILD_SDL)
|
||||||
|
@ -35,6 +36,11 @@ if(NOT Qt5Widgets_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK)
|
||||||
|
if(MACOSX_SDK VERSION_GREATER 10.14)
|
||||||
|
list(APPEND QT_DEFINES USE_SHARE_WIDGET)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(Qt5Widgets_VERSION MATCHES "^5.1[0-9]")
|
if(Qt5Widgets_VERSION MATCHES "^5.1[0-9]")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8")
|
||||||
else()
|
else()
|
||||||
|
@ -183,7 +189,6 @@ if(M_CORE_GB)
|
||||||
list(APPEND PLATFORM_SRC ${GB_SRC})
|
list(APPEND PLATFORM_SRC ${GB_SRC})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(QT_DEFINES)
|
|
||||||
if(Qt5Multimedia_FOUND)
|
if(Qt5Multimedia_FOUND)
|
||||||
list(APPEND AUDIO_SRC
|
list(APPEND AUDIO_SRC
|
||||||
AudioProcessorQt.cpp
|
AudioProcessorQt.cpp
|
||||||
|
|
|
@ -38,7 +38,9 @@ Display* Display::create(QWidget* parent) {
|
||||||
format.setVersion(3, 2);
|
format.setVersion(3, 2);
|
||||||
}
|
}
|
||||||
format.setProfile(QSurfaceFormat::CoreProfile);
|
format.setProfile(QSurfaceFormat::CoreProfile);
|
||||||
if (!DisplayGL::supportsFormat(format)) {
|
if (DisplayGL::supportsFormat(format)) {
|
||||||
|
QSurfaceFormat::setDefaultFormat(format);
|
||||||
|
} else {
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
LOG(QT, WARN) << ("Failed to create an OpenGL Core context, trying old-style...");
|
LOG(QT, WARN) << ("Failed to create an OpenGL Core context, trying old-style...");
|
||||||
format.setVersion(1, 4);
|
format.setVersion(1, 4);
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QOffscreenSurface>
|
|
||||||
#include <QOpenGLContext>
|
|
||||||
#include <QOpenGLFunctions>
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLFunctions_3_2_Core>
|
||||||
#include <QOpenGLPaintDevice>
|
#include <QOpenGLPaintDevice>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
@ -43,13 +43,96 @@ uint qHash(const QSurfaceFormat& format, uint seed) {
|
||||||
return qHash(representation, seed);
|
return qHash(representation, seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mGLWidget::initializeGL() {
|
||||||
|
m_vao.create();
|
||||||
|
m_program.create();
|
||||||
|
|
||||||
|
m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, R"(#version 150 core
|
||||||
|
in vec4 position;
|
||||||
|
out vec2 texCoord;
|
||||||
|
void main() {
|
||||||
|
gl_Position = position;
|
||||||
|
texCoord = (position.st + 1.0) * 0.5;
|
||||||
|
})");
|
||||||
|
|
||||||
|
m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, R"(#version 150 core
|
||||||
|
in vec2 texCoord;
|
||||||
|
out vec4 color;
|
||||||
|
uniform sampler2D tex;
|
||||||
|
void main() {
|
||||||
|
color = vec4(texture(tex, texCoord).rgb, 1.0);
|
||||||
|
})");
|
||||||
|
|
||||||
|
m_program.link();
|
||||||
|
m_program.setUniformValue("tex", 0);
|
||||||
|
m_positionLocation = m_program.attributeLocation("position");
|
||||||
|
|
||||||
|
connect(&m_refresh, &QTimer::timeout, this, static_cast<void (QWidget::*)()>(&QWidget::update));
|
||||||
|
}
|
||||||
|
|
||||||
|
void mGLWidget::finalizeVAO() {
|
||||||
|
QOpenGLFunctions_3_2_Core* fn = context()->versionFunctions<QOpenGLFunctions_3_2_Core>();
|
||||||
|
fn->glGetError(); // Clear the error
|
||||||
|
m_vao.bind();
|
||||||
|
fn->glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||||
|
fn->glEnableVertexAttribArray(m_positionLocation);
|
||||||
|
fn->glVertexAttribPointer(m_positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||||
|
m_vao.release();
|
||||||
|
if (fn->glGetError() == GL_NO_ERROR) {
|
||||||
|
m_vaoDone = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mGLWidget::paintGL() {
|
||||||
|
if (!m_vaoDone) {
|
||||||
|
finalizeVAO();
|
||||||
|
}
|
||||||
|
QOpenGLFunctions_3_2_Core* fn = context()->versionFunctions<QOpenGLFunctions_3_2_Core>();
|
||||||
|
m_program.bind();
|
||||||
|
m_vao.bind();
|
||||||
|
fn->glBindTexture(GL_TEXTURE_2D, m_tex);
|
||||||
|
fn->glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||||
|
fn->glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
m_vao.release();
|
||||||
|
m_program.release();
|
||||||
|
|
||||||
|
// TODO: Better timing
|
||||||
|
++m_refreshResidue;
|
||||||
|
if (m_refreshResidue == 3) {
|
||||||
|
m_refresh.start(16);
|
||||||
|
m_refreshResidue = 0;
|
||||||
|
} else {
|
||||||
|
m_refresh.start(17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
|
||||||
: Display(parent)
|
: Display(parent)
|
||||||
{
|
{
|
||||||
setAttribute(Qt::WA_NativeWindow);
|
setAttribute(Qt::WA_NativeWindow);
|
||||||
|
window()->windowHandle()->setFormat(format);
|
||||||
windowHandle()->create();
|
windowHandle()->create();
|
||||||
|
|
||||||
m_painter = std::make_unique<PainterGL>(windowHandle(), format);
|
#ifdef USE_SHARE_WIDGET
|
||||||
|
bool useShareWidget = true;
|
||||||
|
#else
|
||||||
|
// TODO: Does using this on Wayland help?
|
||||||
|
bool useShareWidget = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (useShareWidget) {
|
||||||
|
m_gl = new mGLWidget;
|
||||||
|
m_gl->setAttribute(Qt::WA_NativeWindow);
|
||||||
|
m_gl->setFormat(format);
|
||||||
|
QBoxLayout* layout = new QVBoxLayout;
|
||||||
|
layout->addWidget(m_gl);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
setLayout(layout);
|
||||||
|
} else {
|
||||||
|
m_gl = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_painter = std::make_unique<PainterGL>(windowHandle(), m_gl, format);
|
||||||
m_drawThread.setObjectName("Painter Thread");
|
m_drawThread.setObjectName("Painter Thread");
|
||||||
m_painter->setThread(&m_drawThread);
|
m_painter->setThread(&m_drawThread);
|
||||||
|
|
||||||
|
@ -106,7 +189,9 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
|
|
||||||
CoreController::Interrupter interrupter(controller);
|
CoreController::Interrupter interrupter(controller);
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "start");
|
QMetaObject::invokeMethod(m_painter.get(), "start");
|
||||||
setUpdatesEnabled(false);
|
if (!m_gl) {
|
||||||
|
setUpdatesEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DisplayGL::supportsFormat(const QSurfaceFormat& format) {
|
bool DisplayGL::supportsFormat(const QSurfaceFormat& format) {
|
||||||
|
@ -188,7 +273,9 @@ void DisplayGL::unpauseDrawing() {
|
||||||
m_isDrawing = true;
|
m_isDrawing = true;
|
||||||
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
|
QMetaObject::invokeMethod(m_painter.get(), "unpause", Qt::BlockingQueuedConnection);
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
setUpdatesEnabled(false);
|
if (!m_gl) {
|
||||||
|
setUpdatesEnabled(false);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,10 +365,21 @@ int DisplayGL::framebufferHandle() {
|
||||||
return m_painter->glTex();
|
return m_painter->glTex();
|
||||||
}
|
}
|
||||||
|
|
||||||
PainterGL::PainterGL(QWindow* surface, const QSurfaceFormat& format)
|
PainterGL::PainterGL(QWindow* window, mGLWidget* widget, const QSurfaceFormat& format)
|
||||||
: m_surface(surface)
|
: m_window(window)
|
||||||
, m_format(format)
|
, m_format(format)
|
||||||
|
, m_widget(widget)
|
||||||
{
|
{
|
||||||
|
if (widget) {
|
||||||
|
m_format = widget->format();
|
||||||
|
QOffscreenSurface* surface = new QOffscreenSurface;
|
||||||
|
surface->setScreen(window->screen());
|
||||||
|
surface->setFormat(m_format);
|
||||||
|
surface->create();
|
||||||
|
m_surface = surface;
|
||||||
|
} else {
|
||||||
|
m_surface = m_window;
|
||||||
|
}
|
||||||
m_supportsShaders = m_format.version() >= qMakePair(2, 0);
|
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());
|
||||||
|
@ -311,6 +409,9 @@ void PainterGL::makeCurrent() {
|
||||||
void PainterGL::create() {
|
void PainterGL::create() {
|
||||||
m_gl = std::make_unique<QOpenGLContext>();
|
m_gl = std::make_unique<QOpenGLContext>();
|
||||||
m_gl->setFormat(m_format);
|
m_gl->setFormat(m_format);
|
||||||
|
if (m_widget) {
|
||||||
|
m_gl->setShareContext(m_widget->context());
|
||||||
|
}
|
||||||
m_gl->create();
|
m_gl->create();
|
||||||
makeCurrent();
|
makeCurrent();
|
||||||
|
|
||||||
|
@ -321,7 +422,7 @@ void PainterGL::create() {
|
||||||
mGLES2Context* gl2Backend;
|
mGLES2Context* gl2Backend;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_window = std::make_unique<QOpenGLPaintDevice>();
|
m_paintDev = std::make_unique<QOpenGLPaintDevice>();
|
||||||
|
|
||||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
auto version = m_format.version();
|
auto version = m_format.version();
|
||||||
|
@ -346,11 +447,36 @@ void PainterGL::create() {
|
||||||
}
|
}
|
||||||
painter->m_gl->swapBuffers(painter->m_surface);
|
painter->m_gl->swapBuffers(painter->m_surface);
|
||||||
painter->makeCurrent();
|
painter->makeCurrent();
|
||||||
|
|
||||||
|
if (painter->m_widget && painter->supportsShaders()) {
|
||||||
|
QOpenGLFunctions_3_2_Core* fn = painter->m_gl->versionFunctions<QOpenGLFunctions_3_2_Core>();
|
||||||
|
fn->glFinish();
|
||||||
|
mGLES2Context* gl2Backend = reinterpret_cast<mGLES2Context*>(painter->m_backend);
|
||||||
|
painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]);
|
||||||
|
painter->m_finalTexIdx ^= 1;
|
||||||
|
gl2Backend->finalShader.tex = painter->m_finalTex[painter->m_finalTexIdx];
|
||||||
|
mGLES2ContextUseFramebuffer(gl2Backend);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
m_backend->init(m_backend, 0);
|
m_backend->init(m_backend, 0);
|
||||||
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
|
||||||
if (m_supportsShaders) {
|
if (m_supportsShaders) {
|
||||||
|
if (m_widget) {
|
||||||
|
m_widget->setVBO(gl2Backend->vbo);
|
||||||
|
|
||||||
|
gl2Backend->finalShader.tex = 0;
|
||||||
|
mGLES2ContextUseFramebuffer(gl2Backend);
|
||||||
|
m_finalTex[0] = gl2Backend->finalShader.tex;
|
||||||
|
|
||||||
|
gl2Backend->finalShader.tex = 0;
|
||||||
|
mGLES2ContextUseFramebuffer(gl2Backend);
|
||||||
|
m_finalTex[1] = gl2Backend->finalShader.tex;
|
||||||
|
|
||||||
|
m_finalTexIdx = 0;
|
||||||
|
gl2Backend->finalShader.tex = m_finalTex[m_finalTexIdx];
|
||||||
|
m_widget->setTex(m_finalTex[m_finalTexIdx]);
|
||||||
|
}
|
||||||
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
|
m_shader.preprocessShader = static_cast<void*>(&reinterpret_cast<mGLES2Context*>(m_backend)->initialShader);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -373,7 +499,7 @@ void PainterGL::destroy() {
|
||||||
#endif
|
#endif
|
||||||
m_backend->deinit(m_backend);
|
m_backend->deinit(m_backend);
|
||||||
m_gl->doneCurrent();
|
m_gl->doneCurrent();
|
||||||
m_window.reset();
|
m_paintDev.reset();
|
||||||
m_gl.reset();
|
m_gl.reset();
|
||||||
|
|
||||||
free(m_backend);
|
free(m_backend);
|
||||||
|
@ -408,10 +534,10 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::resize(const QSize& size) {
|
void PainterGL::resize(const QSize& size) {
|
||||||
qreal r = m_surface->devicePixelRatio();
|
qreal r = m_window->devicePixelRatio();
|
||||||
m_size = size;
|
m_size = size;
|
||||||
m_window->setSize(m_size * r);
|
m_paintDev->setSize(m_size * r);
|
||||||
m_window->setDevicePixelRatio(r);
|
m_paintDev->setDevicePixelRatio(r);
|
||||||
if (m_started && !m_active) {
|
if (m_started && !m_active) {
|
||||||
forceDraw();
|
forceDraw();
|
||||||
}
|
}
|
||||||
|
@ -472,7 +598,7 @@ void PainterGL::draw() {
|
||||||
if (!sync->audioWait && !sync->videoFrameWait) {
|
if (!sync->audioWait && !sync->videoFrameWait) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (m_delayTimer.elapsed() >= 1000 / m_surface->screen()->refreshRate()) {
|
if (m_delayTimer.elapsed() >= 1000 / m_window->screen()->refreshRate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!m_drawTimer.isActive()) {
|
if (!m_drawTimer.isActive()) {
|
||||||
|
@ -492,7 +618,7 @@ void PainterGL::draw() {
|
||||||
forceRedraw = sync->videoFrameWait;
|
forceRedraw = sync->videoFrameWait;
|
||||||
}
|
}
|
||||||
if (!forceRedraw) {
|
if (!forceRedraw) {
|
||||||
forceRedraw = m_delayTimer.nsecsElapsed() + 1000000 >= 1000000000 / m_surface->screen()->refreshRate();
|
forceRedraw = m_delayTimer.nsecsElapsed() + 1000000 >= 1000000000 / m_window->screen()->refreshRate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mCoreSyncWaitFrameEnd(sync);
|
mCoreSyncWaitFrameEnd(sync);
|
||||||
|
@ -507,7 +633,7 @@ void PainterGL::draw() {
|
||||||
void PainterGL::forceDraw() {
|
void PainterGL::forceDraw() {
|
||||||
performDraw();
|
performDraw();
|
||||||
if (!m_context->thread()->impl->sync.audioWait && !m_context->thread()->impl->sync.videoFrameWait) {
|
if (!m_context->thread()->impl->sync.audioWait && !m_context->thread()->impl->sync.videoFrameWait) {
|
||||||
if (m_delayTimer.elapsed() < 1000 / m_surface->screen()->refreshRate()) {
|
if (m_delayTimer.elapsed() < 1000 / m_window->screen()->refreshRate()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_delayTimer.restart();
|
m_delayTimer.restart();
|
||||||
|
@ -532,7 +658,7 @@ void PainterGL::stop() {
|
||||||
}
|
}
|
||||||
if (m_videoProxy) {
|
if (m_videoProxy) {
|
||||||
m_videoProxy->reset();
|
m_videoProxy->reset();
|
||||||
m_videoProxy->moveToThread(m_surface->thread());
|
m_videoProxy->moveToThread(m_window->thread());
|
||||||
m_videoProxy.reset();
|
m_videoProxy.reset();
|
||||||
}
|
}
|
||||||
m_backend->clear(m_backend);
|
m_backend->clear(m_backend);
|
||||||
|
@ -550,14 +676,14 @@ void PainterGL::unpause() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::performDraw() {
|
void PainterGL::performDraw() {
|
||||||
float r = m_surface->devicePixelRatio();
|
float r = m_window->devicePixelRatio();
|
||||||
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
|
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
|
||||||
if (m_buffer) {
|
if (m_buffer) {
|
||||||
m_backend->postFrame(m_backend, m_buffer);
|
m_backend->postFrame(m_backend, m_buffer);
|
||||||
}
|
}
|
||||||
m_backend->drawFrame(m_backend);
|
m_backend->drawFrame(m_backend);
|
||||||
if (m_showOSD && m_messagePainter) {
|
if (m_showOSD && m_messagePainter) {
|
||||||
m_painter.begin(m_window.get());
|
m_painter.begin(m_paintDev.get());
|
||||||
m_messagePainter->paint(&m_painter);
|
m_messagePainter->paint(&m_painter);
|
||||||
m_painter.end();
|
m_painter.end();
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,11 @@
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
#include <QOffscreenSurface>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLVertexArrayObject>
|
||||||
|
#include <QOpenGLWidget>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
@ -35,11 +39,37 @@
|
||||||
#include "platform/video-backend.h"
|
#include "platform/video-backend.h"
|
||||||
|
|
||||||
class QOpenGLPaintDevice;
|
class QOpenGLPaintDevice;
|
||||||
|
class QOpenGLWidget;
|
||||||
|
|
||||||
uint qHash(const QSurfaceFormat&, uint seed = 0);
|
uint qHash(const QSurfaceFormat&, uint seed = 0);
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
|
class mGLWidget : public QOpenGLWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setTex(GLuint tex) { m_tex = tex; }
|
||||||
|
void setVBO(GLuint vbo) { m_vbo = vbo; }
|
||||||
|
void finalizeVAO();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void initializeGL() override;
|
||||||
|
void paintGL() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint m_tex;
|
||||||
|
GLuint m_vbo;
|
||||||
|
|
||||||
|
bool m_vaoDone = false;
|
||||||
|
QOpenGLVertexArrayObject m_vao;
|
||||||
|
QOpenGLShaderProgram m_program;
|
||||||
|
GLuint m_positionLocation;
|
||||||
|
|
||||||
|
QTimer m_refresh;
|
||||||
|
int m_refreshResidue = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class PainterGL;
|
class PainterGL;
|
||||||
class DisplayGL : public Display {
|
class DisplayGL : public Display {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -88,13 +118,14 @@ private:
|
||||||
std::unique_ptr<PainterGL> m_painter;
|
std::unique_ptr<PainterGL> m_painter;
|
||||||
QThread m_drawThread;
|
QThread m_drawThread;
|
||||||
std::shared_ptr<CoreController> m_context;
|
std::shared_ptr<CoreController> m_context;
|
||||||
|
mGLWidget* m_gl;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PainterGL : public QObject {
|
class PainterGL : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PainterGL(QWindow* surface, const QSurfaceFormat& format);
|
PainterGL(QWindow* surface, mGLWidget* widget, const QSurfaceFormat& format);
|
||||||
~PainterGL();
|
~PainterGL();
|
||||||
|
|
||||||
void setThread(QThread*);
|
void setThread(QThread*);
|
||||||
|
@ -146,10 +177,14 @@ private:
|
||||||
uint32_t* m_buffer = nullptr;
|
uint32_t* m_buffer = nullptr;
|
||||||
QPainter m_painter;
|
QPainter m_painter;
|
||||||
QMutex m_mutex;
|
QMutex m_mutex;
|
||||||
QWindow* m_surface;
|
QWindow* m_window;
|
||||||
|
QSurface* m_surface;
|
||||||
QSurfaceFormat m_format;
|
QSurfaceFormat m_format;
|
||||||
std::unique_ptr<QOpenGLPaintDevice> m_window;
|
std::unique_ptr<QOpenGLPaintDevice> m_paintDev;
|
||||||
std::unique_ptr<QOpenGLContext> m_gl;
|
std::unique_ptr<QOpenGLContext> m_gl;
|
||||||
|
int m_finalTexIdx = 0;
|
||||||
|
GLuint m_finalTex[2];
|
||||||
|
mGLWidget* m_widget;
|
||||||
bool m_active = false;
|
bool m_active = false;
|
||||||
bool m_started = false;
|
bool m_started = false;
|
||||||
QTimer m_drawTimer;
|
QTimer m_drawTimer;
|
||||||
|
|
|
@ -754,7 +754,9 @@ void Window::focusInEvent(QFocusEvent*) {
|
||||||
updateMultiplayerActive(true);
|
updateMultiplayerActive(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_display->forceDraw();
|
if (m_display) {
|
||||||
|
m_display->forceDraw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::focusOutEvent(QFocusEvent*) {
|
void Window::focusOutEvent(QFocusEvent*) {
|
||||||
|
|
Loading…
Reference in New Issue