all repos — mgba @ 3c18fe162c2c76eca80692d37083f6809bae4c8d

mGBA Game Boy Advance Emulator

src/platform/qt/DisplayGL.cpp (view raw)

  1/* Copyright (c) 2013-2015 Jeffrey Pfau
  2 *
  3 * This Source Code Form is subject to the terms of the Mozilla Public
  4 * License, v. 2.0. If a copy of the MPL was not distributed with this
  5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6#include "DisplayGL.h"
  7
  8#include <QApplication>
  9#include <QResizeEvent>
 10
 11extern "C" {
 12#include "gba/supervisor/thread.h"
 13}
 14
 15using namespace QGBA;
 16
 17DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
 18	: Display(parent)
 19	, m_gl(new EmptyGLWidget(format, this))
 20	, m_painter(new PainterGL(m_gl))
 21	, m_drawThread(nullptr)
 22	, m_lockAspectRatio(false)
 23	, m_filter(false)
 24	, m_context(nullptr)
 25{
 26}
 27
 28DisplayGL::~DisplayGL() {
 29	delete m_painter;
 30}
 31
 32void DisplayGL::startDrawing(GBAThread* thread) {
 33	if (m_drawThread) {
 34		return;
 35	}
 36	m_painter->setContext(thread);
 37	m_context = thread;
 38	m_painter->resize(size());
 39	m_gl->move(0, 0);
 40	m_drawThread = new QThread(this);
 41	m_drawThread->setObjectName("Painter Thread");
 42	m_gl->context()->doneCurrent();
 43	m_gl->context()->moveToThread(m_drawThread);
 44	m_painter->moveToThread(m_drawThread);
 45	connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
 46	m_drawThread->start();
 47	GBASyncSetVideoSync(&m_context->sync, false);
 48
 49	lockAspectRatio(m_lockAspectRatio);
 50	filter(m_filter);
 51	resizePainter();
 52}
 53
 54void DisplayGL::stopDrawing() {
 55	if (m_drawThread) {
 56		if (GBAThreadIsActive(m_context)) {
 57			GBAThreadInterrupt(m_context);
 58		}
 59		QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
 60		m_drawThread->exit();
 61		m_drawThread = nullptr;
 62		if (GBAThreadIsActive(m_context)) {
 63			GBAThreadContinue(m_context);
 64		}
 65	}
 66}
 67
 68void DisplayGL::pauseDrawing() {
 69	if (m_drawThread) {
 70		if (GBAThreadIsActive(m_context)) {
 71			GBAThreadInterrupt(m_context);
 72		}
 73		QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
 74		if (GBAThreadIsActive(m_context)) {
 75			GBAThreadContinue(m_context);
 76		}
 77	}
 78}
 79
 80void DisplayGL::unpauseDrawing() {
 81	if (m_drawThread) {
 82		if (GBAThreadIsActive(m_context)) {
 83			GBAThreadInterrupt(m_context);
 84		}
 85		QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
 86		if (GBAThreadIsActive(m_context)) {
 87			GBAThreadContinue(m_context);
 88		}
 89	}
 90}
 91
 92void DisplayGL::forceDraw() {
 93	if (m_drawThread) {
 94		QMetaObject::invokeMethod(m_painter, "forceDraw");
 95	}
 96}
 97
 98void DisplayGL::lockAspectRatio(bool lock) {
 99	m_lockAspectRatio = lock;
100	if (m_drawThread) {
101		QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Q_ARG(bool, lock));
102	}
103}
104
105void DisplayGL::filter(bool filter) {
106	m_filter = filter;
107	if (m_drawThread) {
108		QMetaObject::invokeMethod(m_painter, "filter", Q_ARG(bool, filter));
109	}
110}
111
112void DisplayGL::framePosted(const uint32_t* buffer) {
113	if (m_drawThread && buffer) {
114		QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer));
115	}
116}
117
118void DisplayGL::showMessage(const QString& message) {
119	if (m_drawThread) {
120		QMetaObject::invokeMethod(m_painter, "showMessage", Q_ARG(const QString&, message));
121	}
122}
123
124void DisplayGL::resizeEvent(QResizeEvent*) {
125	resizePainter();
126}
127
128void DisplayGL::resizePainter() {
129	m_gl->resize(size());
130	if (m_drawThread) {
131		QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));
132	}
133}
134
135PainterGL::PainterGL(QGLWidget* parent)
136	: m_gl(parent)
137	, m_active(false)
138	, m_context(nullptr)
139	, m_messagePainter(nullptr)
140{
141	GBAGLContextCreate(&m_backend);
142	m_backend.d.swap = [](VideoBackend* v) {
143		PainterGL* painter = static_cast<PainterGL*>(v->user);
144		painter->m_gl->swapBuffers();
145	};
146	m_backend.d.user = this;
147	m_backend.d.filter = false;
148	m_backend.d.lockAspectRatio = false;
149}
150
151void PainterGL::setContext(GBAThread* context) {
152	m_context = context;
153}
154
155void PainterGL::setBacking(const uint32_t* backing) {
156	m_gl->makeCurrent();
157	m_backend.d.postFrame(&m_backend.d, backing);
158	if (m_active) {
159		draw();
160	}
161	m_gl->doneCurrent();
162}
163
164void PainterGL::resize(const QSize& size) {
165	m_size = size;
166	if (m_active) {
167		m_messagePainter->resize(size, m_backend.d.lockAspectRatio);
168		forceDraw();
169	}
170}
171
172void PainterGL::lockAspectRatio(bool lock) {
173	m_backend.d.lockAspectRatio = lock;
174	if (m_active) {
175		m_messagePainter->resize(m_size, m_backend.d.lockAspectRatio);
176		forceDraw();
177	}
178}
179
180void PainterGL::filter(bool filter) {
181	m_backend.d.filter = filter;
182	if (m_active) {
183		forceDraw();
184	}
185}
186
187void PainterGL::start() {
188	m_messagePainter = new MessagePainter(this);
189	m_messagePainter->resize(m_size, m_backend.d.lockAspectRatio);
190	m_gl->makeCurrent();
191	m_backend.d.init(&m_backend.d, reinterpret_cast<WHandle>(m_gl->winId()));
192	m_gl->doneCurrent();
193	m_active = true;
194}
195
196void PainterGL::draw() {
197	if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
198		m_painter.begin(m_gl->context()->device());
199		performDraw();
200		m_painter.end();
201		GBASyncWaitFrameEnd(&m_context->sync);
202		m_backend.d.swap(&m_backend.d);
203	} else {
204		GBASyncWaitFrameEnd(&m_context->sync);
205	}
206}
207
208void PainterGL::forceDraw() {
209	m_painter.begin(m_gl->context()->device());
210	performDraw();
211	m_painter.end();
212	m_backend.d.swap(&m_backend.d);
213}
214
215void PainterGL::stop() {
216	m_active = false;
217	m_gl->makeCurrent();
218	m_backend.d.clear(&m_backend.d);
219	m_backend.d.swap(&m_backend.d);
220	m_backend.d.deinit(&m_backend.d);
221	m_gl->doneCurrent();
222	m_gl->context()->moveToThread(m_gl->thread());
223	m_messagePainter->clearMessage();
224	delete m_messagePainter;
225	m_messagePainter = nullptr;
226	moveToThread(m_gl->thread());
227}
228
229void PainterGL::pause() {
230	m_active = false;
231	// Make sure both buffers are filled
232	forceDraw();
233	forceDraw();
234}
235
236void PainterGL::unpause() {
237	m_active = true;
238}
239
240void PainterGL::performDraw() {
241	m_painter.beginNativePainting();
242	float r = m_gl->devicePixelRatio();
243	m_backend.d.resized(&m_backend.d, m_size.width() * r, m_size.height() * r);
244	m_backend.d.drawFrame(&m_backend.d);
245	m_painter.endNativePainting();
246	m_messagePainter->paint(&m_painter);
247}
248
249void PainterGL::showMessage(const QString& message) {
250	m_messagePainter->showMessage(message);
251}