Qt: Add VideoProxy

This commit is contained in:
Vicki Pfau 2019-05-10 11:12:24 -07:00
parent bb7f41e8cc
commit 42813bb197
9 changed files with 207 additions and 9 deletions

View File

@ -47,6 +47,7 @@ struct mCore {
struct mTiming* timing;
struct mDebugger* debugger;
struct mDebuggerSymbols* symbolTable;
struct mVideoLogger* videoLogger;
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
struct mDirectorySet dirs;

View File

@ -151,6 +151,7 @@ static bool _GBACoreInit(struct mCore* core) {
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 @@ static void _GBACoreReset(struct mCore* core) {
#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);
}

View File

@ -121,6 +121,7 @@ set(SOURCE_FILES
utils.cpp
Window.cpp
VFileDevice.cpp
VideoProxy.cpp
VideoView.cpp)
set(UI_FILES

View File

@ -19,6 +19,7 @@ struct VideoShader;
namespace QGBA {
class CoreController;
class VideoProxy;
class Display : public QWidget {
Q_OBJECT
@ -47,6 +48,7 @@ public:
virtual bool isDrawing() const = 0;
virtual bool supportsShaders() const = 0;
virtual VideoShader* shaders() = 0;
virtual VideoProxy* videoProxy() { return nullptr; }
signals:
void showCursor();

View File

@ -34,10 +34,12 @@ DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
// 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 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
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 @@ void DisplayGL::resizePainter() {
}
}
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 @@ void PainterGL::draw() {
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 @@ void PainterGL::stop() {
m_gl->context()->moveToThread(m_gl->thread());
m_context.reset();
moveToThread(m_gl->thread());
m_videoProxy->moveToThread(m_gl->thread());
}
void PainterGL::pause() {

View File

@ -23,6 +23,8 @@
#include <QQueue>
#include <QThread>
#include "VideoProxy.h"
#include "platform/video-backend.h"
namespace QGBA {
@ -49,6 +51,7 @@ public:
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 @@ private:
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 @@ private:
QSize m_size;
MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer;
VideoProxy* m_videoProxy;
};
}

View File

@ -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();
}
}

View File

@ -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;
};
}

View File

@ -52,6 +52,7 @@
#include "ShaderSelector.h"
#include "ShortcutController.h"
#include "TileView.h"
#include "VideoProxy.h"
#include "VideoView.h"
#ifdef USE_DISCORD_RPC
@ -716,9 +717,6 @@ void Window::gameStarted() {
if (m_savedScale > 0) {
resizeFrame(size * m_savedScale);
}
if (!m_display) {
reloadDisplayDriver();
}
attachWidget(m_display.get());
m_display->setMinimumSize(size);
setFocus();
@ -1713,6 +1711,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
appendMRU(fname);
}
if (!m_display) {
reloadDisplayDriver();
}
if (m_display->videoProxy()) {
m_display->videoProxy()->attach(controller);
}
m_controller = std::shared_ptr<CoreController>(controller);
m_inputController.recalibrateAxes();
m_controller->setInputController(&m_inputController);