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 doneCurrent();
46 context()->moveToThread(m_drawThread);
47 connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start()));
48 m_drawThread->start(QThread::TimeCriticalPriority);
49}
50
51void Display::stopDrawing() {
52 if (m_drawThread) {
53 QMetaObject::invokeMethod(m_painter, "stop", Qt::BlockingQueuedConnection);
54 m_drawThread->exit();
55 m_drawThread = nullptr;
56 }
57}
58
59void Display::forceDraw() {
60 if (m_drawThread) {
61 QMetaObject::invokeMethod(m_painter, "forceDraw", Qt::QueuedConnection);
62 }
63}
64
65void Display::initializeGL() {
66 glClearColor(0, 0, 0, 0);
67 glClear(GL_COLOR_BUFFER_BIT);
68 swapBuffers();
69}
70
71void Display::resizeEvent(QResizeEvent* event) {
72 if (m_drawThread) {
73 QMetaObject::invokeMethod(m_painter, "resize", Qt::BlockingQueuedConnection, Q_ARG(QSize, event->size()));
74 }
75}
76
77Painter::Painter(Display* parent)
78 : m_gl(parent)
79{
80 m_size = parent->size();
81}
82
83void Painter::setContext(GBAThread* context) {
84 m_context = context;
85}
86
87void Painter::setBacking(const uint32_t* backing) {
88 m_backing = backing;
89}
90
91void Painter::resize(const QSize& size) {
92 m_size = size;
93 m_gl->makeCurrent();
94 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
95 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
96 m_gl->swapBuffers();
97 m_gl->doneCurrent();
98}
99
100void Painter::start() {
101 m_gl->makeCurrent();
102 glEnable(GL_TEXTURE_2D);
103 glGenTextures(1, &m_tex);
104 glBindTexture(GL_TEXTURE_2D, m_tex);
105 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
106 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
107 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
108 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
109 glEnableClientState(GL_VERTEX_ARRAY);
110 glVertexPointer(2, GL_INT, 0, _glVertices);
111 glTexCoordPointer(2, GL_INT, 0, _glTexCoords);
112 glMatrixMode(GL_PROJECTION);
113 glLoadIdentity();
114 glOrtho(0, 240, 160, 0, 0, 1);
115 glMatrixMode(GL_MODELVIEW);
116 glLoadIdentity();
117 m_gl->doneCurrent();
118
119 m_drawTimer = new QTimer;
120 m_drawTimer->moveToThread(QThread::currentThread());
121 m_drawTimer->setInterval(0);
122 connect(m_drawTimer, SIGNAL(timeout()), this, SLOT(draw()));
123 m_drawTimer->start();
124}
125
126void Painter::draw() {
127 m_gl->makeCurrent();
128 if (GBASyncWaitFrameStart(&m_context->sync, m_context->frameskip)) {
129 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
130 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
131 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
132 if (m_context->sync.videoFrameWait) {
133 glFlush();
134 }
135 }
136 GBASyncWaitFrameEnd(&m_context->sync);
137 m_gl->swapBuffers();
138 m_gl->doneCurrent();
139}
140
141void Painter::forceDraw() {
142 m_gl->makeCurrent();
143 glViewport(0, 0, m_size.width() * m_gl->devicePixelRatio(), m_size.height() * m_gl->devicePixelRatio());
144 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_backing);
145 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
146 if (m_context->sync.videoFrameWait) {
147 glFlush();
148 }
149 m_gl->swapBuffers();
150 m_gl->doneCurrent();
151}
152
153void Painter::stop() {
154 m_drawTimer->stop();
155 delete m_drawTimer;
156 m_gl->makeCurrent();
157 glDeleteTextures(1, &m_tex);
158 glClear(GL_COLOR_BUFFER_BIT);
159 m_gl->swapBuffers();
160 m_gl->doneCurrent();
161 m_gl->context()->moveToThread(QApplication::instance()->thread());
162}