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
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
31DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent)
32 : Display(parent)
33 , m_painter(new Painter(format, this))
34 , m_started(false)
35 , m_lockAspectRatio(false)
36 , m_filter(false)
37 , m_context(nullptr)
38{
39}
40
41void DisplayGL::startDrawing(GBAThread* thread) {
42 if (m_started) {
43 return;
44 }
45 m_painter->setContext(thread);
46 m_context = thread;
47 m_painter->start();
48 m_painter->resize(size());
49 m_painter->move(0, 0);
50 m_started = true;
51
52 lockAspectRatio(m_lockAspectRatio);
53 filter(m_filter);
54}
55
56void DisplayGL::stopDrawing() {
57 if (m_started) {
58 if (GBAThreadIsActive(m_context)) {
59 GBAThreadInterrupt(m_context);
60 GBASyncSuspendDrawing(&m_context->sync);
61 }
62 m_painter->stop();
63 m_started = false;
64 if (GBAThreadIsActive(m_context)) {
65 GBASyncResumeDrawing(&m_context->sync);
66 GBAThreadContinue(m_context);
67 }
68 }
69}
70
71void DisplayGL::pauseDrawing() {
72 if (m_started) {
73 if (GBAThreadIsActive(m_context)) {
74 GBAThreadInterrupt(m_context);
75 GBASyncSuspendDrawing(&m_context->sync);
76 }
77 m_painter->pause();
78 if (GBAThreadIsActive(m_context)) {
79 GBASyncResumeDrawing(&m_context->sync);
80 GBAThreadContinue(m_context);
81 }
82 }
83}
84
85void DisplayGL::unpauseDrawing() {
86 if (m_started) {
87 if (GBAThreadIsActive(m_context)) {
88 GBAThreadInterrupt(m_context);
89 GBASyncSuspendDrawing(&m_context->sync);
90 }
91 m_painter->unpause();
92 if (GBAThreadIsActive(m_context)) {
93 GBASyncResumeDrawing(&m_context->sync);
94 GBAThreadContinue(m_context);
95 }
96 }
97}
98
99void DisplayGL::forceDraw() {
100 if (m_started) {
101 m_painter->forceDraw();
102 }
103}
104
105void DisplayGL::lockAspectRatio(bool lock) {
106 m_lockAspectRatio = lock;
107 if (m_started) {
108 m_painter->lockAspectRatio(lock);
109 }
110}
111
112void DisplayGL::filter(bool filter) {
113 m_filter = filter;
114 if (m_started) {
115 m_painter->filter(filter);
116 }
117}
118
119void DisplayGL::framePosted(const uint32_t* buffer) {
120 if (m_started && buffer) {
121 m_painter->setBacking(buffer);
122 }
123}
124
125void DisplayGL::resizeEvent(QResizeEvent* event) {
126 m_painter->resize(event->size());
127}
128
129Painter::Painter(const QGLFormat& format, QWidget* parent)
130 : QGLWidget(format, parent)
131 , m_drawTimer(nullptr)
132 , m_lockAspectRatio(false)
133 , m_filter(false)
134 , m_context(nullptr)
135{
136 setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
137 m_size = parent->size();
138 setAutoBufferSwap(false);
139}
140
141void Painter::setContext(GBAThread* context) {
142 m_context = context;
143}
144
145void Painter::setBacking(const uint32_t* backing) {
146 makeCurrent();
147#ifdef COLOR_16_BIT
148#ifdef COLOR_5_6_5
149 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, backing);
150#else
151 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, backing);
152#endif
153#else
154 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, backing);
155#endif
156 doneCurrent();
157}
158
159void Painter::resize(const QSize& size) {
160 m_size = size;
161 QWidget::resize(size);
162 if (m_drawTimer) {
163 forceDraw();
164 }
165}
166
167void Painter::lockAspectRatio(bool lock) {
168 m_lockAspectRatio = lock;
169 if (m_drawTimer) {
170 forceDraw();
171 }
172}
173
174void Painter::filter(bool filter) {
175 m_filter = filter;
176 makeCurrent();
177 if (m_filter) {
178 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
179 } else {
180 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
181 }
182 doneCurrent();
183 if (m_drawTimer) {
184 forceDraw();
185 }
186}
187
188void Painter::start() {
189 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 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 makeCurrent();
220 GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip);
221 performDraw();
222 GBASyncWaitFrameEnd(&m_context->sync);
223 swapBuffers();
224 doneCurrent();
225}
226
227void Painter::forceDraw() {
228 makeCurrent();
229 glViewport(0, 0, m_size.width() * devicePixelRatio(), m_size.height() * devicePixelRatio());
230 glClear(GL_COLOR_BUFFER_BIT);
231 performDraw();
232 swapBuffers();
233 doneCurrent();
234}
235
236void Painter::stop() {
237 m_drawTimer->stop();
238 delete m_drawTimer;
239 m_drawTimer = nullptr;
240 makeCurrent();
241 glDeleteTextures(1, &m_tex);
242 glClear(GL_COLOR_BUFFER_BIT);
243 swapBuffers();
244 doneCurrent();
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::initializeGL() {
259 glClearColor(0, 0, 0, 0);
260 glClear(GL_COLOR_BUFFER_BIT);
261}
262
263void Painter::performDraw() {
264 int w = m_size.width() * devicePixelRatio();
265 int h = m_size.height() * devicePixelRatio();
266#ifndef Q_OS_MAC
267 // TODO: This seems to cause framerates to drag down to 120 FPS on OS X,
268 // even if the emulator can go faster. Look into why.
269 glViewport(0, 0, m_size.width() * devicePixelRatio(), m_size.height() * devicePixelRatio());
270 glClear(GL_COLOR_BUFFER_BIT);
271#endif
272 int drawW = w;
273 int drawH = h;
274 if (m_lockAspectRatio) {
275 if (w * 2 > h * 3) {
276 drawW = h * 3 / 2;
277 } else if (w * 2 < h * 3) {
278 drawH = w * 2 / 3;
279 }
280 }
281 glViewport((w - drawW) / 2, (h - drawH) / 2, drawW, drawH);
282 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
283 if (m_context->sync.videoFrameWait) {
284 glFlush();
285 }
286}