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}