src/platform/qt/Display.cpp (view raw)
1#include "Display.h"
2
3#include <QApplication>
4#include <QResizeEvent>
5
6extern "C" {
7#include "gba-thread.h"
8}
9
10using namespace QGBA;
11
12static const GLint _glVertices[] = {
13 0, 0,
14 256, 0,
15 256, 256,
16 0, 256
17};
18
19static const GLint _glTexCoords[] = {
20 0, 0,
21 1, 0,
22 1, 1,
23 0, 1
24};
25
26Display::Display(QGLFormat format, QWidget* parent)
27 : QGLWidget(format, parent)
28 , m_painter(nullptr)
29 , m_drawThread(nullptr)
30{
31 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
32 setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS);
33 setAutoBufferSwap(false);
34}
35
36void Display::startDrawing(const uint32_t* buffer, GBAThread* thread) {
37 if (m_drawThread) {
38 return;
39 }
40 m_drawThread = new QThread(this);
41 m_painter = new Painter(this);
42 m_painter->setContext(thread);
43 m_painter->setBacking(buffer);
44 m_painter->moveToThread(m_drawThread);
45 m_context = thread;
46 doneCurrent();
47 context()->moveToThread(m_drawThread);
48 connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
49 m_drawThread->start(QThread::TimeCriticalPriority);
50}
51
52void Display::stopDrawing() {
53 if (m_drawThread) {
54 GBAThreadInterrupt(m_context);
55 GBASyncSuspendDrawing(&m_context->sync);
56 QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
57 m_drawThread->exit();
58 m_drawThread = nullptr;
59 GBASyncResumeDrawing(&m_context->sync);
60 GBAThreadContinue(m_context);
61 }
62}
63
64void Display::forceDraw() {
65 if (m_drawThread) {
66 QMetaObject::invokeMethod(m_painter, "forceDraw", Qt::QueuedConnection);
67 }
68}
69
70void Display::initializeGL() {
71 glClearColor(0, 0, 0, 0);
72 glClear(GL_COLOR_BUFFER_BIT);
73 swapBuffers();
74}
75
76void Display::resizeEvent(QResizeEvent* event) {
77 if (m_drawThread) {
78 GBAThreadInterrupt(m_context);
79 GBASyncSuspendDrawing(&m_context->sync);
80 QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, event->size()));
81 GBASyncResumeDrawing(&m_context->sync);
82 GBAThreadContinue(m_context);
83 }
84}
85
86Painter::Painter(Display* parent)
87 : m_gl(parent)
88{
89 m_size = parent->size();
90}
91
92void Painter::setContext(GBAThread* context) {
93 m_context = context;
94}
95
96void Painter::setBacking(const uint32_t* backing) {
97 m_backing = backing;
98}
99
100void Painter::resize(const QSize& size) {
101 m_size = size;
102 m_gl->makeCurrent();
103 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
104 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
105 m_gl->swapBuffers();
106 m_gl->doneCurrent();
107}
108
109void Painter::start() {
110 m_gl->makeCurrent();
111 glEnable(GL_TEXTURE_2D);
112 glGenTextures(1, &m_tex);
113 glBindTexture(GL_TEXTURE_2D, m_tex);
114 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
115 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
116 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
117 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
118 glEnableClientState(GL_VERTEX_ARRAY);
119 glVertexPointer(2, GL_INT, 0, _glVertices);
120 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
121 glMatrixMode(GL_PROJECTION);
122 glLoadIdentity();
123 glOrtho(0, 240, 160, 0, 0, 1);
124 glMatrixMode(GL_MODELVIEW);
125 glLoadIdentity();
126 m_gl->doneCurrent();
127
128 m_drawTimer = new QTimer;
129 m_drawTimer->moveToThread(QThread::currentThread());
130 m_drawTimer->setInterval(0);
131 connect(m_drawTimer, SIGNAL(timeout()), this, SLOT(draw()));
132 m_drawTimer->start();
133}
134
135void Painter::draw() {
136 m_gl->makeCurrent();
137 if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
138 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
139 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
140 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
141 if (m_context->sync.videoFrameWait) {
142 glFlush();
143 }
144 }
145 GBASyncWaitFrameEnd(&m_context->sync);
146 m_gl->swapBuffers();
147 m_gl->doneCurrent();
148}
149
150void Painter::forceDraw() {
151 m_gl->makeCurrent();
152 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
153 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
154 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
155 if (m_context->sync.videoFrameWait) {
156 glFlush();
157 }
158 m_gl->swapBuffers();
159 m_gl->doneCurrent();
160}
161
162void Painter::stop() {
163 m_drawTimer->stop();
164 delete m_drawTimer;
165 m_gl->makeCurrent();
166 glDeleteTextures(1, &m_tex);
167 glClear(GL_COLOR_BUFFER_BIT);
168 m_gl->swapBuffers();
169 m_gl->doneCurrent();
170 m_gl->context()->moveToThread(QApplication::instance()->thread());
171}