all repos — mgba @ 870c8ebb8c529daeb5326c836322a72114a76c5d

mGBA Game Boy Advance Emulator

Qt: Fix sync, hopefully
Jeffrey Pfau jeffrey@endrift.com
Thu, 06 Aug 2015 19:04:03 -0700
commit

870c8ebb8c529daeb5326c836322a72114a76c5d

parent

581d5fab3b0b724d782ca01cf7d920043c8d0f45

M CHANGESCHANGES

@@ -30,6 +30,7 @@ - Default controller profiles for several common controllers

- Libretro now supports BIOS, rumble and solar sensor - Implement BIOS call Stop, for sleep mode - Automatically load patches, if found + - Improved video synchronization Bugfixes: - ARM7: Fix SWI and IRQ timings - GBA Audio: Force audio FIFOs to 32-bit
M src/platform/qt/DisplayGL.cppsrc/platform/qt/DisplayGL.cpp

@@ -118,7 +118,8 @@ }

void DisplayGL::framePosted(const uint32_t* buffer) { if (m_drawThread && buffer) { - QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer)); + m_painter->enqueue(buffer); + QMetaObject::invokeMethod(m_painter, "draw"); } }

@@ -152,6 +153,19 @@ };

m_backend.d.user = this; m_backend.d.filter = false; m_backend.d.lockAspectRatio = false; + + for (int i = 0; i < 2; ++i) { + m_free.append(new uint32_t[256 * 256]); + } +} + +PainterGL::~PainterGL() { + while (!m_queue.isEmpty()) { + delete[] m_queue.dequeue(); + } + for (auto item : m_free) { + delete[] item; + } } void PainterGL::setContext(GBAThread* context) {

@@ -162,15 +176,6 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {

m_messagePainter = messagePainter; } -void PainterGL::setBacking(const uint32_t* backing) { - m_gl->makeCurrent(); - m_backend.d.postFrame(&m_backend.d, backing); - if (m_active) { - draw(); - } - m_gl->doneCurrent(); -} - void PainterGL::resize(const QSize& size) { m_size = size; if (m_active) {

@@ -200,7 +205,11 @@ m_active = true;

} void PainterGL::draw() { - if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) { + if (m_queue.isEmpty()) { + return; + } + if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip) || !m_queue.isEmpty()) { + dequeue(); m_painter.begin(m_gl->context()->device()); performDraw(); m_painter.end();

@@ -209,6 +218,9 @@ m_backend.d.swap(&m_backend.d);

} else { GBASyncWaitFrameEnd(&m_context->sync); } + if (!m_queue.isEmpty()) { + QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); + } } void PainterGL::forceDraw() {

@@ -221,6 +233,7 @@

void PainterGL::stop() { m_active = false; m_gl->makeCurrent(); + dequeueAll(); m_backend.d.clear(&m_backend.d); m_backend.d.swap(&m_backend.d); m_backend.d.deinit(&m_backend.d);

@@ -232,8 +245,11 @@

void PainterGL::pause() { m_active = false; // Make sure both buffers are filled + m_gl->makeCurrent(); + dequeueAll(); forceDraw(); forceDraw(); + m_gl->doneCurrent(); } void PainterGL::unpause() {

@@ -250,3 +266,39 @@ if (m_messagePainter) {

m_messagePainter->paint(&m_painter); } } + +void PainterGL::enqueue(const uint32_t* backing) { + m_mutex.lock(); + uint32_t* buffer; + if (m_free.isEmpty()) { + buffer = m_queue.dequeue(); + } else { + buffer = m_free.takeLast(); + } + memcpy(buffer, backing, 256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL); + m_queue.enqueue(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeue() { + m_mutex.lock(); + if (m_queue.isEmpty()) { + m_mutex.unlock(); + return; + } + uint32_t* buffer = m_queue.dequeue(); + m_backend.d.postFrame(&m_backend.d, buffer); + m_free.append(buffer); + m_mutex.unlock(); +} + +void PainterGL::dequeueAll() { + uint32_t* buffer; + m_mutex.lock(); + while (!m_queue.isEmpty()) { + buffer = m_queue.dequeue(); + m_free.append(buffer); + } + m_backend.d.postFrame(&m_backend.d, buffer); + m_mutex.unlock(); +}
M src/platform/qt/DisplayGL.hsrc/platform/qt/DisplayGL.h

@@ -9,7 +9,9 @@

#include "Display.h" #include <QGLWidget> +#include <QList> #include <QMouseEvent> +#include <QQueue> #include <QThread> #include <QTimer>

@@ -74,12 +76,13 @@ Q_OBJECT

public: PainterGL(QGLWidget* parent); + ~PainterGL(); void setContext(GBAThread*); void setMessagePainter(MessagePainter*); + void enqueue(const uint32_t* backing); public slots: - void setBacking(const uint32_t*); void forceDraw(); void draw(); void start();

@@ -92,8 +95,13 @@ void filter(bool filter);

private: void performDraw(); + void dequeue(); + void dequeueAll(); + QList<uint32_t*> m_free; + QQueue<uint32_t*> m_queue; QPainter m_painter; + QMutex m_mutex; QGLWidget* m_gl; bool m_active; GBAThread* m_context;
M src/platform/qt/GameController.cppsrc/platform/qt/GameController.cpp

@@ -31,7 +31,8 @@ using namespace std;

GameController::GameController(QObject* parent) : QObject(parent) - , m_drawContext(new uint32_t[256 * 256]) + , m_drawContext(new uint32_t[256 * VIDEO_HORIZONTAL_PIXELS]) + , m_frontBuffer(new uint32_t[256 * 256]) , m_threadContext() , m_activeKeys(0) , m_inactiveKeys(0)

@@ -122,7 +123,8 @@

m_threadContext.frameCallback = [](GBAThread* context) { GameController* controller = static_cast<GameController*>(context->userData); if (GBASyncDrawingFrame(&controller->m_threadContext.sync)) { - controller->frameAvailable(controller->m_drawContext); + memcpy(controller->m_frontBuffer, controller->m_drawContext, 256 * VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL); + controller->frameAvailable(controller->m_frontBuffer); } else { controller->frameAvailable(nullptr); }

@@ -216,6 +218,7 @@ closeGame();

GBACheatDeviceDestroy(&m_cheatDevice); delete m_renderer; delete[] m_drawContext; + delete[] m_frontBuffer; delete m_backupLoadState; }

@@ -339,7 +342,7 @@ m_threadContext.patch = VFileDevice::open(m_patch, O_RDONLY);

} m_inputController->recalibrateAxes(); - memset(m_drawContext, 0xF8, 1024 * 256); + memset(m_drawContext, 0xF8, 1024 * VIDEO_HORIZONTAL_PIXELS); if (!GBAThreadStart(&m_threadContext)) { m_gameOpen = false;
M src/platform/qt/GameController.hsrc/platform/qt/GameController.h

@@ -167,6 +167,7 @@ void redoSamples(int samples);

void enableTurbo(); uint32_t* m_drawContext; + uint32_t* m_frontBuffer; GBAThread m_threadContext; GBAVideoSoftwareRenderer* m_renderer; GBACheatDevice m_cheatDevice;