mirror of https://github.com/mgba-emu/mgba.git
Qt: Add VideoProxy
This commit is contained in:
parent
bb7f41e8cc
commit
42813bb197
|
@ -47,6 +47,7 @@ struct mCore {
|
||||||
struct mTiming* timing;
|
struct mTiming* timing;
|
||||||
struct mDebugger* debugger;
|
struct mDebugger* debugger;
|
||||||
struct mDebuggerSymbols* symbolTable;
|
struct mDebuggerSymbols* symbolTable;
|
||||||
|
struct mVideoLogger* videoLogger;
|
||||||
|
|
||||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||||
struct mDirectorySet dirs;
|
struct mDirectorySet dirs;
|
||||||
|
|
|
@ -151,6 +151,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
||||||
core->timing = &gba->timing;
|
core->timing = &gba->timing;
|
||||||
core->debugger = NULL;
|
core->debugger = NULL;
|
||||||
core->symbolTable = NULL;
|
core->symbolTable = NULL;
|
||||||
|
core->videoLogger = NULL;
|
||||||
gbacore->overrides = NULL;
|
gbacore->overrides = NULL;
|
||||||
gbacore->debuggerPlatform = NULL;
|
gbacore->debuggerPlatform = NULL;
|
||||||
gbacore->cheatDevice = NULL;
|
gbacore->cheatDevice = NULL;
|
||||||
|
@ -392,11 +393,16 @@ static void _GBACoreReset(struct mCore* core) {
|
||||||
#ifndef DISABLE_THREADING
|
#ifndef DISABLE_THREADING
|
||||||
int fakeBool;
|
int fakeBool;
|
||||||
if (mCoreConfigGetIntValue(&core->config, "threadedVideo", &fakeBool) && 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);
|
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
|
||||||
renderer = &gbacore->proxyRenderer.d;
|
renderer = &gbacore->proxyRenderer.d;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
GBAVideoAssociateRenderer(&gba->video, renderer);
|
GBAVideoAssociateRenderer(&gba->video, renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,7 @@ set(SOURCE_FILES
|
||||||
utils.cpp
|
utils.cpp
|
||||||
Window.cpp
|
Window.cpp
|
||||||
VFileDevice.cpp
|
VFileDevice.cpp
|
||||||
|
VideoProxy.cpp
|
||||||
VideoView.cpp)
|
VideoView.cpp)
|
||||||
|
|
||||||
set(UI_FILES
|
set(UI_FILES
|
||||||
|
|
|
@ -19,6 +19,7 @@ struct VideoShader;
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class CoreController;
|
class CoreController;
|
||||||
|
class VideoProxy;
|
||||||
|
|
||||||
class Display : public QWidget {
|
class Display : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -47,6 +48,7 @@ public:
|
||||||
virtual bool isDrawing() const = 0;
|
virtual bool isDrawing() const = 0;
|
||||||
virtual bool supportsShaders() const = 0;
|
virtual bool supportsShaders() const = 0;
|
||||||
virtual VideoShader* shaders() = 0;
|
virtual VideoShader* shaders() = 0;
|
||||||
|
virtual VideoProxy* videoProxy() { return nullptr; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void showCursor();
|
void showCursor();
|
||||||
|
|
|
@ -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
|
// 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
|
// need to make sure it's initialized to nullptr before we assign the new object to it
|
||||||
m_gl = new EmptyGLWidget(format, this);
|
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->setMouseTracking(true);
|
||||||
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
|
||||||
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
|
||||||
|
|
||||||
|
connect(&m_videoProxy, &VideoProxy::dataAvailable, &m_videoProxy, &VideoProxy::processData);
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayGL::~DisplayGL() {
|
DisplayGL::~DisplayGL() {
|
||||||
|
@ -74,6 +76,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
||||||
m_gl->context()->doneCurrent();
|
m_gl->context()->doneCurrent();
|
||||||
m_gl->context()->moveToThread(m_drawThread);
|
m_gl->context()->moveToThread(m_drawThread);
|
||||||
m_painter->moveToThread(m_drawThread);
|
m_painter->moveToThread(m_drawThread);
|
||||||
|
m_videoProxy.moveToThread(m_drawThread);
|
||||||
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start);
|
||||||
m_drawThread->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_gl(parent)
|
||||||
|
, m_videoProxy(proxy)
|
||||||
{
|
{
|
||||||
#ifdef BUILD_GL
|
#ifdef BUILD_GL
|
||||||
mGLContext* glBackend;
|
mGLContext* glBackend;
|
||||||
|
@ -347,7 +358,7 @@ void PainterGL::draw() {
|
||||||
m_backend->swap(m_backend);
|
m_backend->swap(m_backend);
|
||||||
if (!m_delayTimer.isValid()) {
|
if (!m_delayTimer.isValid()) {
|
||||||
m_delayTimer.start();
|
m_delayTimer.start();
|
||||||
} else {
|
} else if (m_gl->format().swapInterval()) {
|
||||||
while (m_delayTimer.elapsed() < 15) {
|
while (m_delayTimer.elapsed() < 15) {
|
||||||
QThread::usleep(100);
|
QThread::usleep(100);
|
||||||
}
|
}
|
||||||
|
@ -382,6 +393,7 @@ void PainterGL::stop() {
|
||||||
m_gl->context()->moveToThread(m_gl->thread());
|
m_gl->context()->moveToThread(m_gl->thread());
|
||||||
m_context.reset();
|
m_context.reset();
|
||||||
moveToThread(m_gl->thread());
|
moveToThread(m_gl->thread());
|
||||||
|
m_videoProxy->moveToThread(m_gl->thread());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PainterGL::pause() {
|
void PainterGL::pause() {
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#include "VideoProxy.h"
|
||||||
|
|
||||||
#include "platform/video-backend.h"
|
#include "platform/video-backend.h"
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
@ -49,6 +51,7 @@ public:
|
||||||
bool isDrawing() const override { return m_isDrawing; }
|
bool isDrawing() const override { return m_isDrawing; }
|
||||||
bool supportsShaders() const override;
|
bool supportsShaders() const override;
|
||||||
VideoShader* shaders() override;
|
VideoShader* shaders() override;
|
||||||
|
VideoProxy* videoProxy() override;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void stopDrawing() override;
|
void stopDrawing() override;
|
||||||
|
@ -75,13 +78,14 @@ private:
|
||||||
PainterGL* m_painter;
|
PainterGL* m_painter;
|
||||||
QThread* m_drawThread = nullptr;
|
QThread* m_drawThread = nullptr;
|
||||||
std::shared_ptr<CoreController> m_context;
|
std::shared_ptr<CoreController> m_context;
|
||||||
|
VideoProxy m_videoProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PainterGL : public QObject {
|
class PainterGL : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PainterGL(int majorVersion, QGLWidget* parent);
|
PainterGL(int majorVersion, VideoProxy* proxy, QGLWidget* parent);
|
||||||
~PainterGL();
|
~PainterGL();
|
||||||
|
|
||||||
void setContext(std::shared_ptr<CoreController>);
|
void setContext(std::shared_ptr<CoreController>);
|
||||||
|
@ -126,6 +130,7 @@ private:
|
||||||
QSize m_size;
|
QSize m_size;
|
||||||
MessagePainter* m_messagePainter = nullptr;
|
MessagePainter* m_messagePainter = nullptr;
|
||||||
QElapsedTimer m_delayTimer;
|
QElapsedTimer m_delayTimer;
|
||||||
|
VideoProxy* m_videoProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -52,6 +52,7 @@
|
||||||
#include "ShaderSelector.h"
|
#include "ShaderSelector.h"
|
||||||
#include "ShortcutController.h"
|
#include "ShortcutController.h"
|
||||||
#include "TileView.h"
|
#include "TileView.h"
|
||||||
|
#include "VideoProxy.h"
|
||||||
#include "VideoView.h"
|
#include "VideoView.h"
|
||||||
|
|
||||||
#ifdef USE_DISCORD_RPC
|
#ifdef USE_DISCORD_RPC
|
||||||
|
@ -716,9 +717,6 @@ void Window::gameStarted() {
|
||||||
if (m_savedScale > 0) {
|
if (m_savedScale > 0) {
|
||||||
resizeFrame(size * m_savedScale);
|
resizeFrame(size * m_savedScale);
|
||||||
}
|
}
|
||||||
if (!m_display) {
|
|
||||||
reloadDisplayDriver();
|
|
||||||
}
|
|
||||||
attachWidget(m_display.get());
|
attachWidget(m_display.get());
|
||||||
m_display->setMinimumSize(size);
|
m_display->setMinimumSize(size);
|
||||||
setFocus();
|
setFocus();
|
||||||
|
@ -1713,6 +1711,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
||||||
appendMRU(fname);
|
appendMRU(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_display) {
|
||||||
|
reloadDisplayDriver();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_display->videoProxy()) {
|
||||||
|
m_display->videoProxy()->attach(controller);
|
||||||
|
}
|
||||||
|
|
||||||
m_controller = std::shared_ptr<CoreController>(controller);
|
m_controller = std::shared_ptr<CoreController>(controller);
|
||||||
m_inputController.recalibrateAxes();
|
m_inputController.recalibrateAxes();
|
||||||
m_controller->setInputController(&m_inputController);
|
m_controller->setInputController(&m_inputController);
|
||||||
|
|
Loading…
Reference in New Issue