Qt: Fix sync, hopefully
Jeffrey Pfau jeffrey@endrift.com
Thu, 06 Aug 2015 19:04:03 -0700
5 files changed,
80 insertions(+),
15 deletions(-)
M
CHANGES
→
CHANGES
@@ -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.cpp
→
src/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.h
→
src/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.cpp
→
src/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.h
→
src/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;