all repos — mgba @ 742296b8b9e3bec29ff16c0c1553d7e5c337ca2d

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_isDrawing(false)
 20	, m_gl(new EmptyGLWidget(format, this))
 21	, m_painter(new PainterGL(m_gl))
 22	, m_drawThread(nullptr)
 23	, m_context(nullptr)
 24{
 25	m_gl->setMouseTracking(true);
 26	m_gl->setAttribute(Qt::WA_TransparentForMouseEvents); // This doesn't seem to work?
 27}
 28
 29DisplayGL::~DisplayGL() {
 30	delete m_painter;
 31}
 32
 33void DisplayGL::startDrawing(GBAThread* thread) {
 34	if (m_drawThread) {
 35		return;
 36	}
 37	m_isDrawing = true;
 38	m_painter->setContext(thread);
 39	m_painter->setMessagePainter(messagePainter());
 40	m_context = thread;
 41	m_painter->resize(size());
 42	m_gl->move(0, 0);
 43	m_drawThread = new QThread(this);
 44	m_drawThread->setObjectName("Painter Thread");
 45	m_gl->context()->doneCurrent();
 46	m_gl->context()->moveToThread(m_drawThread);
 47	m_painter->moveToThread(m_drawThread);
 48	connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
 49	m_drawThread->start();
 50	GBASyncSetVideoSync(&m_context->sync, false);
 51
 52	lockAspectRatio(isAspectRatioLocked());
 53	filter(isFiltered());
 54	messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio());
 55	resizePainter();
 56}
 57
 58void DisplayGL::stopDrawing() {
 59	if (m_drawThread) {
 60		m_isDrawing = false;
 61		if (GBAThreadIsActive(m_context)) {
 62			GBAThreadInterrupt(m_context);
 63		}
 64		QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
 65		m_drawThread->exit();
 66		m_drawThread = nullptr;
 67		if (GBAThreadIsActive(m_context)) {
 68			GBAThreadContinue(m_context);
 69		}
 70	}
 71}
 72
 73void DisplayGL::pauseDrawing() {
 74	if (m_drawThread) {
 75		m_isDrawing = false;
 76		if (GBAThreadIsActive(m_context)) {
 77			GBAThreadInterrupt(m_context);
 78		}
 79		QMetaObject::invokeMethod(m_painter, "pause", Qt::BlockingQueuedConnection);
 80		if (GBAThreadIsActive(m_context)) {
 81			GBAThreadContinue(m_context);
 82		}
 83	}
 84}
 85
 86void DisplayGL::unpauseDrawing() {
 87	if (m_drawThread) {
 88		m_isDrawing = true;
 89		if (GBAThreadIsActive(m_context)) {
 90			GBAThreadInterrupt(m_context);
 91		}
 92		QMetaObject::invokeMethod(m_painter, "unpause", Qt::BlockingQueuedConnection);
 93		if (GBAThreadIsActive(m_context)) {
 94			GBAThreadContinue(m_context);
 95		}
 96	}
 97}
 98
 99void DisplayGL::forceDraw() {
100	if (m_drawThread) {
101		QMetaObject::invokeMethod(m_painter, "forceDraw");
102	}
103}
104
105void DisplayGL::lockAspectRatio(bool lock) {
106	Display::lockAspectRatio(lock);
107	if (m_drawThread) {
108		QMetaObject::invokeMethod(m_painter, "lockAspectRatio", Q_ARG(bool, lock));
109	}
110}
111
112void DisplayGL::filter(bool filter) {
113	Display::filter(filter);
114	if (m_drawThread) {
115		QMetaObject::invokeMethod(m_painter, "filter", Q_ARG(bool, filter));
116	}
117}
118
119void DisplayGL::framePosted(const uint32_t* buffer) {
120	if (m_drawThread && buffer) {
121		QMetaObject::invokeMethod(m_painter, "setBacking", Q_ARG(const uint32_t*, buffer));
122	}
123}
124
125void DisplayGL::resizeEvent(QResizeEvent* event) {
126	Display::resizeEvent(event);
127	resizePainter();
128}
129
130void DisplayGL::resizePainter() {
131	m_gl->resize(size());
132	if (m_drawThread) {
133		QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, size()));
134	}
135}
136
137PainterGL::PainterGL(QGLWidget* parent)
138	: m_gl(parent)
139	, m_active(false)
140	, m_context(nullptr)
141	, m_messagePainter(nullptr)
142{
143#ifdef BUILD_GL
144	GBAGLContextCreate(&m_backend);
145#elif defined(BUILD_GLES2)
146	GBAGLES2ContextCreate(&m_backend);
147#endif
148	m_backend.d.swap = [](VideoBackend* v) {
149		PainterGL* painter = static_cast<PainterGL*>(v->user);
150		painter->m_gl->swapBuffers();
151	};
152	m_backend.d.user = this;
153	m_backend.d.filter = false;
154	m_backend.d.lockAspectRatio = false;
155}
156
157void PainterGL::setContext(GBAThread* context) {
158	m_context = context;
159}
160
161void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
162	m_messagePainter = messagePainter;
163}
164
165void PainterGL::setBacking(const uint32_t* backing) {
166	m_gl->makeCurrent();
167	m_backend.d.postFrame(&m_backend.d, backing);
168	if (m_active) {
169		draw();
170	}
171	m_gl->doneCurrent();
172}
173
174void PainterGL::resize(const QSize& size) {
175	m_size = size;
176	if (m_active) {
177		forceDraw();
178	}
179}
180
181void PainterGL::lockAspectRatio(bool lock) {
182	m_backend.d.lockAspectRatio = lock;
183	if (m_active) {
184		forceDraw();
185	}
186}
187
188void PainterGL::filter(bool filter) {
189	m_backend.d.filter = filter;
190	if (m_active) {
191		forceDraw();
192	}
193}
194
195void PainterGL::start() {
196	m_gl->makeCurrent();
197	m_backend.d.init(&m_backend.d, reinterpret_cast<WHandle>(m_gl->winId()));
198	m_gl->doneCurrent();
199	m_active = true;
200}
201
202void PainterGL::draw() {
203	if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
204		m_painter.begin(m_gl->context()->device());
205		performDraw();
206		m_painter.end();
207		GBASyncWaitFrameEnd(&m_context->sync);
208		m_backend.d.swap(&m_backend.d);
209	} else {
210		GBASyncWaitFrameEnd(&m_context->sync);
211	}
212}
213
214void PainterGL::forceDraw() {
215	m_painter.begin(m_gl->context()->device());
216	performDraw();
217	m_painter.end();
218	m_backend.d.swap(&m_backend.d);
219}
220
221void PainterGL::stop() {
222	m_active = false;
223	m_gl->makeCurrent();
224	m_backend.d.clear(&m_backend.d);
225	m_backend.d.swap(&m_backend.d);
226	m_backend.d.deinit(&m_backend.d);
227	m_gl->doneCurrent();
228	m_gl->context()->moveToThread(m_gl->thread());
229	moveToThread(m_gl->thread());
230}
231
232void PainterGL::pause() {
233	m_active = false;
234	// Make sure both buffers are filled
235	forceDraw();
236	forceDraw();
237}
238
239void PainterGL::unpause() {
240	m_active = true;
241}
242
243void PainterGL::performDraw() {
244	m_painter.beginNativePainting();
245	float r = m_gl->devicePixelRatio();
246	m_backend.d.resized(&m_backend.d, m_size.width() * r, m_size.height() * r);
247	m_backend.d.drawFrame(&m_backend.d);
248	m_painter.endNativePainting();
249	if (m_messagePainter) {
250		m_messagePainter->paint(&m_painter);
251	}
252}