all repos — mgba @ 42813bb197dee028b64ba70b0a9b63aea2cf264c

mGBA Game Boy Advance Emulator

Qt: Add VideoProxy
Vicki Pfau vi@endrift.com
Fri, 10 May 2019 11:12:24 -0700
commit

42813bb197dee028b64ba70b0a9b63aea2cf264c

parent

bb7f41e8cca003d57f7470293a9951d9e027a8a5

M include/mgba/core/core.hinclude/mgba/core/core.h

@@ -47,6 +47,7 @@ void* board;

struct mTiming* timing; struct mDebugger* debugger; struct mDebuggerSymbols* symbolTable; + struct mVideoLogger* videoLogger; #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 struct mDirectorySet dirs;
M src/gba/core.csrc/gba/core.c

@@ -151,6 +151,7 @@ core->board = gba;

core->timing = &gba->timing; core->debugger = NULL; core->symbolTable = NULL; + core->videoLogger = NULL; gbacore->overrides = NULL; gbacore->debuggerPlatform = NULL; gbacore->cheatDevice = NULL;

@@ -392,11 +393,16 @@ struct GBAVideoRenderer* renderer = &gbacore->renderer.d;

#ifndef DISABLE_THREADING int fakeBool; if (mCoreConfigGetIntValue(&core->config, "threadedVideo", &fakeBool) && fakeBool) { - gbacore->proxyRenderer.logger = &gbacore->threadProxy.d; + if (!core->videoLogger) { + core->videoLogger = &gbacore->threadProxy.d; + } + } +#endif + if (core->videoLogger) { + gbacore->proxyRenderer.logger = core->videoLogger; GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer); renderer = &gbacore->proxyRenderer.d; } -#endif GBAVideoAssociateRenderer(&gba->video, renderer); }
M src/platform/qt/CMakeLists.txtsrc/platform/qt/CMakeLists.txt

@@ -121,6 +121,7 @@ TileView.cpp

utils.cpp Window.cpp VFileDevice.cpp + VideoProxy.cpp VideoView.cpp) set(UI_FILES
M src/platform/qt/Display.hsrc/platform/qt/Display.h

@@ -19,6 +19,7 @@

namespace QGBA { class CoreController; +class VideoProxy; class Display : public QWidget { Q_OBJECT

@@ -47,6 +48,7 @@ virtual void startDrawing(std::shared_ptr<CoreController>) = 0;

virtual bool isDrawing() const = 0; virtual bool supportsShaders() const = 0; virtual VideoShader* shaders() = 0; + virtual VideoProxy* videoProxy() { return nullptr; } signals: void showCursor();
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -34,10 +34,12 @@ {

// 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 EmptyGLWidget(format, this); - m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl); + m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), &m_videoProxy, m_gl); m_gl->setMouseTracking(true); m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work? setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions + + connect(&m_videoProxy, &VideoProxy::dataAvailable, &m_videoProxy, &VideoProxy::processData); } DisplayGL::~DisplayGL() {

@@ -74,6 +76,7 @@ m_drawThread->setObjectName("Painter Thread");

m_gl->context()->doneCurrent(); m_gl->context()->moveToThread(m_drawThread); m_painter->moveToThread(m_drawThread); + m_videoProxy.moveToThread(m_drawThread); connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start); m_drawThread->start();

@@ -184,8 +187,16 @@ QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));

} } -PainterGL::PainterGL(int majorVersion, QGLWidget* parent) +VideoProxy* DisplayGL::videoProxy() { + if (supportsShaders()) { + return &m_videoProxy; + } + return nullptr; +} + +PainterGL::PainterGL(int majorVersion, VideoProxy* proxy, QGLWidget* parent) : m_gl(parent) + , m_videoProxy(proxy) { #ifdef BUILD_GL mGLContext* glBackend;

@@ -347,7 +358,7 @@ m_painter.end();

m_backend->swap(m_backend); if (!m_delayTimer.isValid()) { m_delayTimer.start(); - } else { + } else if (m_gl->format().swapInterval()) { while (m_delayTimer.elapsed() < 15) { QThread::usleep(100); }

@@ -382,6 +393,7 @@ m_gl->doneCurrent();

m_gl->context()->moveToThread(m_gl->thread()); m_context.reset(); moveToThread(m_gl->thread()); + m_videoProxy->moveToThread(m_gl->thread()); } void PainterGL::pause() {
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -23,6 +23,8 @@ #include <QMouseEvent>

#include <QQueue> #include <QThread> +#include "VideoProxy.h" + #include "platform/video-backend.h" namespace QGBA {

@@ -49,6 +51,7 @@ void startDrawing(std::shared_ptr<CoreController>) override;

bool isDrawing() const override { return m_isDrawing; } bool supportsShaders() const override; VideoShader* shaders() override; + VideoProxy* videoProxy() override; public slots: void stopDrawing() override;

@@ -75,13 +78,14 @@ QGLWidget* m_gl;

PainterGL* m_painter; QThread* m_drawThread = nullptr; std::shared_ptr<CoreController> m_context; + VideoProxy m_videoProxy; }; class PainterGL : public QObject { Q_OBJECT public: - PainterGL(int majorVersion, QGLWidget* parent); + PainterGL(int majorVersion, VideoProxy* proxy, QGLWidget* parent); ~PainterGL(); void setContext(std::shared_ptr<CoreController>);

@@ -126,6 +130,7 @@ VideoBackend* m_backend = nullptr;

QSize m_size; MessagePainter* m_messagePainter = nullptr; QElapsedTimer m_delayTimer; + VideoProxy* m_videoProxy; }; }
A src/platform/qt/VideoProxy.cpp

@@ -0,0 +1,97 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "VideoProxy.h" + +#include "CoreController.h" + +using namespace QGBA; + +VideoProxy::VideoProxy() { + mVideoLoggerRendererCreate(&m_logger.d, false); + m_logger.d.block = true; + + m_logger.d.init = &cbind<&VideoProxy::init>; + m_logger.d.reset = &cbind<&VideoProxy::reset>; + m_logger.d.deinit = &cbind<&VideoProxy::deinit>; + m_logger.d.lock = &cbind<&VideoProxy::lock>; + m_logger.d.unlock = &cbind<&VideoProxy::unlock>; + m_logger.d.wait = &cbind<&VideoProxy::wait>; + m_logger.d.wake = &callback<void, int>::func<&VideoProxy::wake>; + + m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>; + m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>; +} + +void VideoProxy::attach(CoreController* controller) { + CoreController::Interrupter interrupter(controller); + controller->thread()->core->videoLogger = &m_logger.d; +} + +void VideoProxy::processData() { + mVideoLoggerRendererRun(&m_logger.d, false); + m_fromThreadCond.wakeAll(); +} + +void VideoProxy::init() { + RingFIFOInit(&m_dirtyQueue, 0x80000); +} + +void VideoProxy::reset() { + RingFIFOClear(&m_dirtyQueue); +} + +void VideoProxy::deinit() { + RingFIFODeinit(&m_dirtyQueue); +} + +bool VideoProxy::writeData(const void* data, size_t length) { + while (!RingFIFOWrite(&m_dirtyQueue, data, length)) { + emit dataAvailable(); + m_mutex.lock(); + m_toThreadCond.wakeAll(); + m_fromThreadCond.wait(&m_mutex); + m_mutex.unlock(); + } + emit dataAvailable(); + return true; +} + +bool VideoProxy::readData(void* data, size_t length, bool block) { + bool read = false; + while (true) { + read = RingFIFORead(&m_dirtyQueue, data, length); + if (!block || read) { + break; + } + m_mutex.lock(); + m_fromThreadCond.wakeAll(); + m_toThreadCond.wait(&m_mutex); + m_mutex.unlock(); + } + return read; +} + +void VideoProxy::lock() { + m_mutex.lock(); +} + +void VideoProxy::unlock() { + m_mutex.unlock(); +} + +void VideoProxy::wait() { + while (RingFIFOSize(&m_dirtyQueue)) { + emit dataAvailable(); + m_toThreadCond.wakeAll(); + m_fromThreadCond.wait(&m_mutex, 1); + } +} + +void VideoProxy::wake(int y) { + if ((y & 15) == 15) { + m_toThreadCond.wakeAll(); + } +}
A src/platform/qt/VideoProxy.h

@@ -0,0 +1,68 @@

+/* Copyright (c) 2013-2018 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#pragma once + +#include <QMutex> +#include <QObject> +#include <QWaitCondition> + +#include <mgba/feature/video-logger.h> +#include <mgba-util/ring-fifo.h> + +namespace QGBA { + +class CoreController; + +class VideoProxy : public QObject { +Q_OBJECT + +public: + VideoProxy(); + + void attach(CoreController*); + +signals: + void dataAvailable(); + +public slots: + void processData(); + +private: + void init(); + void reset(); + void deinit(); + + bool writeData(const void* data, size_t length); + bool readData(void* data, size_t length, bool block); + + void lock(); + void unlock(); + void wait(); + void wake(int y); + + template<typename T, typename... A> struct callback { + using type = T (VideoProxy::*)(A...); + + template<type F> static T func(mVideoLogger* logger, A... args) { + VideoProxy* proxy = reinterpret_cast<Logger*>(logger)->p; + return (proxy->*F)(args...); + } + }; + + template<void (VideoProxy::*F)()> static void cbind(mVideoLogger* logger) { callback<void>::func<F>(logger); } + + struct Logger { + mVideoLogger d; + VideoProxy* p; + } m_logger = {{}, this}; + + RingFIFO m_dirtyQueue; + QMutex m_mutex; + QWaitCondition m_toThreadCond; + QWaitCondition m_fromThreadCond; +}; + +}
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -52,6 +52,7 @@ #include "SettingsView.h"

#include "ShaderSelector.h" #include "ShortcutController.h" #include "TileView.h" +#include "VideoProxy.h" #include "VideoView.h" #ifdef USE_DISCORD_RPC

@@ -715,9 +716,6 @@ m_config->updateOption("lockIntegerScaling");

m_config->updateOption("lockAspectRatio"); if (m_savedScale > 0) { resizeFrame(size * m_savedScale); - } - if (!m_display) { - reloadDisplayDriver(); } attachWidget(m_display.get()); m_display->setMinimumSize(size);

@@ -1711,6 +1709,14 @@ }

if (!fname.isEmpty()) { setWindowFilePath(fname); appendMRU(fname); + } + + if (!m_display) { + reloadDisplayDriver(); + } + + if (m_display->videoProxy()) { + m_display->videoProxy()->attach(controller); } m_controller = std::shared_ptr<CoreController>(controller);