src/platform/qt/Display.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 "Display.h"
7
8#include <QApplication>
9#include <QResizeEvent>
10
11extern "C" {
12#include "gba/supervisor/thread.h"
13}
14
15using namespace QGBA;
16
17static const GLint _glVertices[] = {
18 0, 0,
19 256, 0,
20 256, 256,
21 0, 256
22};
23
24static const GLint _glTexCoords[] = {
25 0, 0,
26 1, 0,
27 1, 1,
28 0, 1
29};
30
31Display::Display(QGLFormat format, QWidget* parent)
32 : QGLWidget(format, parent)
33 , m_painter(nullptr)
34 , m_started(false)
35 , m_lockAspectRatio(false)
36 , m_filter(false)
37 , m_context(nullptr)
38{
39 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
40 setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
41 setAutoBufferSwap(false);
42 setCursor(Qt::BlankCursor);
43}
44
45void Display::startDrawing(const uint32_t* buffer, GBAThread* thread) {
46 if (m_started) {
47 return;
48 }
49 m_painter = new Painter(this);
50 m_painter->setContext(thread);
51 m_painter->setBacking(buffer);
52 m_context = thread;
53 doneCurrent();
54 m_painter->start();
55 m_started = true;
56
57 lockAspectRatio(m_lockAspectRatio);
58 filter(m_filter);
59}
60
61void Display::stopDrawing() {
62 if (m_started) {
63 if (GBAThreadIsActive(m_context)) {
64 GBAThreadInterrupt(m_context);
65 GBASyncSuspendDrawing(&m_context->sync);
66 }
67 m_painter->stop();
68 m_started = false;
69 if (GBAThreadIsActive(m_context)) {
70 GBASyncResumeDrawing(&m_context->sync);
71 GBAThreadContinue(m_context);
72 }
73 }
74}
75
76void Display::pauseDrawing() {
77 if (m_started) {
78 if (GBAThreadIsActive(m_context)) {
79 GBAThreadInterrupt(m_context);
80 GBASyncSuspendDrawing(&m_context->sync);
81 }
82 m_painter->pause();
83 if (GBAThreadIsActive(m_context)) {
84 GBASyncResumeDrawing(&m_context->sync);
85 GBAThreadContinue(m_context);
86 }
87 }
88}
89
90void Display::unpauseDrawing() {
91 if (m_started) {
92 if (GBAThreadIsActive(m_context)) {
93 GBAThreadInterrupt(m_context);
94 GBASyncSuspendDrawing(&m_context->sync);
95 }
96 m_painter->unpause();
97 if (GBAThreadIsActive(m_context)) {
98 GBASyncResumeDrawing(&m_context->sync);
99 GBAThreadContinue(m_context);
100 }
101 }
102}
103
104void Display::forceDraw() {
105 if (m_started) {
106 m_painter->forceDraw();
107 }
108}
109
110void Display::lockAspectRatio(bool lock) {
111 m_lockAspectRatio = lock;
112 if (m_started) {
113 m_painter->lockAspectRatio(lock);
114 }
115}
116
117void Display::filter(bool filter) {
118 m_filter = filter;
119 if (m_started) {
120 m_painter->filter(filter);
121 }
122}
123
124#ifdef USE_PNG
125void Display::screenshot() {
126 GBAThreadInterrupt(m_context);
127 GBAThreadTakeScreenshot(m_context);
128 GBAThreadContinue(m_context);
129}
130#endif
131
132void Display::initializeGL() {
133 glClearColor(0, 0, 0, 0);
134 glClear(GL_COLOR_BUFFER_BIT);
135}
136
137void Display::resizeEvent(QResizeEvent* event) {
138 if (m_started) {
139 GBAThreadInterrupt(m_context);
140 GBASyncSuspendDrawing(&m_context->sync);
141 m_painter->resize(event->size());
142 GBASyncResumeDrawing(&m_context->sync);
143 GBAThreadContinue(m_context);
144 }
145}
146
147Painter::Painter(Display* parent)
148 : m_gl(parent)
149 , m_lockAspectRatio(false)
150 , m_filter(false)
151 , m_context(nullptr)
152{
153 m_size = parent->size();
154}
155
156void Painter::setContext(GBAThread* context) {
157 m_context = context;
158}
159
160void Painter::setBacking(const uint32_t* backing) {
161 m_backing = backing;
162}
163
164void Painter::resize(const QSize& size) {
165 m_size = size;
166 forceDraw();
167 forceDraw();
168}
169
170void Painter::lockAspectRatio(bool lock) {
171 m_lockAspectRatio = lock;
172 forceDraw();
173 forceDraw();
174}
175
176void Painter::filter(bool filter) {
177 m_filter = filter;
178 m_gl->makeCurrent();
179 if (m_filter) {
180 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
181 } else {
182 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
183 }
184 m_gl->doneCurrent();
185 forceDraw();
186}
187
188void Painter::start() {
189 m_gl->makeCurrent();
190 glEnable(GL_TEXTURE_2D);
191 glGenTextures(1, &m_tex);
192 glBindTexture(GL_TEXTURE_2D, m_tex);
193 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
194 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
195 if (m_filter) {
196 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
197 } else {
198 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
199 }
200 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
201 glEnableClientState(GL_VERTEX_ARRAY);
202 glVertexPointer(2, GL_INT, 0, _glVertices);
203 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
204 glMatrixMode(GL_PROJECTION);
205 glLoadIdentity();
206 glOrtho(0, 240, 160, 0, 0, 1);
207 glMatrixMode(GL_MODELVIEW);
208 glLoadIdentity();
209 m_gl->doneCurrent();
210
211 m_drawTimer = new QTimer;
212 m_drawTimer->moveToThread(QThread::currentThread());
213 m_drawTimer->setInterval(0);
214 connect(m_drawTimer, SIGNAL(timeout()), this, SLOT(draw()));
215 m_drawTimer->start();
216}
217
218void Painter::draw() {
219 m_gl->makeCurrent();
220 GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip);
221 performDraw();
222 GBASyncWaitFrameEnd(&m_context->sync);
223 m_gl->swapBuffers();
224 m_gl->doneCurrent();
225}
226
227void Painter::forceDraw() {
228 m_gl->makeCurrent();
229 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
230 glClear(GL_COLOR_BUFFER_BIT);
231 performDraw();
232 m_gl->swapBuffers();
233 m_gl->doneCurrent();
234}
235
236void Painter::stop() {
237 m_drawTimer->stop();
238 delete m_drawTimer;
239 m_gl->makeCurrent();
240 glDeleteTextures(1, &m_tex);
241 glClear(GL_COLOR_BUFFER_BIT);
242 m_gl->swapBuffers();
243 m_gl->doneCurrent();
244 m_gl->context()->moveToThread(QApplication::instance()->thread());
245}
246
247void Painter::pause() {
248 m_drawTimer->stop();
249 // Make sure both buffers are filled
250 forceDraw();
251 forceDraw();
252}
253
254void Painter::unpause() {
255 m_drawTimer->start();
256}
257
258void Painter::performDraw() {
259 int w = m_size.width() * m_gl->devicePixelRatio();
260 int h = m_size.height() * m_gl->devicePixelRatio();
261#ifndef Q_OS_MAC
262 // TODO: This seems to cause framerates to drag down to 120 FPS on OS X,
263 // even if the emulator can go faster. Look into why.
264 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
265 glClear(GL_COLOR_BUFFER_BIT);
266#endif
267 int drawW = w;
268 int drawH = h;
269 if (m_lockAspectRatio) {
270 if (w * 2 > h * 3) {
271 drawW = h * 3 / 2;
272 } else if (w * 2 < h * 3) {
273 drawH = w * 2 / 3;
274 }
275 }
276 glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
277#ifdef COLOR_16_BIT
278#ifdef COLOR_5_6_5
279 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, m_backing);
280#else
281 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, m_backing);
282#endif
283#else
284 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
285#endif
286 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
287 if (m_context->sync.videoFrameWait) {
288 glFlush();
289 }
290}