1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtGui/QOpenGLWindow>
30#include <QtTest/QtTest>
31#include <QtGui/QOpenGLFunctions>
32#include <QtGui/QOpenGLContext>
33#include <QtGui/QPainter>
34#include <private/qguiapplication_p.h>
35#include <qpa/qplatformintegration.h>
36
37class tst_QOpenGLWindow : public QObject
38{
39 Q_OBJECT
40
41private slots:
42 void initTestCase();
43 void create();
44 void basic();
45 void resize();
46 void painter();
47 void partial_data();
48 void partial();
49 void underOver();
50};
51
52void tst_QOpenGLWindow::initTestCase()
53{
54 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::OpenGL))
55 QSKIP("OpenGL is not supported on this platform.");
56}
57
58void tst_QOpenGLWindow::create()
59{
60 QOpenGLWindow w;
61 QVERIFY(!w.isValid());
62
63 w.resize(w: 640, h: 480);
64 w.show();
65
66 QVERIFY(QTest::qWaitForWindowExposed(&w));
67
68 QVERIFY(w.isValid());
69}
70
71class Window : public QOpenGLWindow
72{
73public:
74 void reset() {
75 initCount = resizeCount = paintCount = 0;
76 }
77
78 void initializeGL() override {
79 ++initCount;
80 }
81
82 void resizeGL(int w, int h) override {
83 ++resizeCount;
84 QCOMPARE(w, size().width());
85 QCOMPARE(h, size().height());
86 }
87
88 void paintGL() override {
89 ++paintCount;
90
91 QOpenGLContext *ctx = QOpenGLContext::currentContext();
92 QVERIFY(ctx);
93 QCOMPARE(ctx, context());
94 QOpenGLFunctions *f = ctx->functions();
95 QVERIFY(f);
96
97 f->glClearColor(red: 1, green: 0, blue: 0, alpha: 1);
98 f->glClear(GL_COLOR_BUFFER_BIT);
99
100 img = grabFramebuffer();
101 }
102
103 int initCount;
104 int resizeCount;
105 int paintCount;
106 QImage img;
107};
108
109void tst_QOpenGLWindow::basic()
110{
111 Window w;
112 w.reset();
113 w.resize(w: 640, h: 480);
114 w.show();
115 QVERIFY(QTest::qWaitForWindowExposed(&w));
116
117 // Check that the virtuals are invoked.
118 QCOMPARE(w.initCount, 1);
119 QVERIFY(w.resizeCount >= 1);
120 QVERIFY(w.paintCount >= 1);
121
122 // Check that something has been drawn;
123 QCOMPARE(w.img.size(), w.size() * w.devicePixelRatio());
124 QVERIFY(w.img.pixel(QPoint(5, 5) * w.devicePixelRatio()) == qRgb(255, 0, 0));
125
126 // Check that the viewport was properly set.
127 w.makeCurrent();
128 GLint v[4] = { 0, 0, 0, 0 };
129 w.context()->functions()->glGetIntegerv(GL_VIEWPORT, params: v);
130 QCOMPARE(v[0], 0);
131 QCOMPARE(v[1], 0);
132 QCOMPARE(v[2], GLint(w.width() * w.devicePixelRatio()));
133 QCOMPARE(v[3], GLint(w.height() * w.devicePixelRatio()));
134 w.doneCurrent();
135}
136
137static bool isPlatformWayland()
138{
139 return QGuiApplication::platformName().startsWith(s: QLatin1String("wayland"), cs: Qt::CaseInsensitive);
140}
141
142void tst_QOpenGLWindow::resize()
143{
144 if (isPlatformWayland())
145 QSKIP("Wayland: Crashes on Intel Mesa due to a driver bug (QTBUG-66848).");
146
147 Window w;
148 w.reset();
149 w.resize(w: 640, h: 480);
150 w.show();
151 QVERIFY(QTest::qWaitForWindowExposed(&w));
152
153 // Check that the virtuals are invoked.
154 int resCount = w.resizeCount;
155 QVERIFY(resCount >= 1);
156
157 // Check that a future resize triggers resizeGL.
158 w.resize(w: 800, h: 600);
159 QTRY_VERIFY(w.resizeCount > resCount);
160}
161
162class PainterWindow : public QOpenGLWindow
163{
164public:
165 void paintGL() override {
166 QOpenGLContext *ctx = QOpenGLContext::currentContext();
167 QVERIFY(ctx);
168 QCOMPARE(ctx, context());
169 QOpenGLFunctions *f = ctx->functions();
170 QVERIFY(f);
171
172 QPainter p(this);
173 p.beginNativePainting();
174 f->glClearColor(red: 1, green: 0, blue: 0, alpha: 1);
175 f->glClear(GL_COLOR_BUFFER_BIT);
176 p.endNativePainting();
177 p.fillRect(r: QRect(0, 0, 100, 100), c: Qt::blue);
178 p.end();
179
180 img = grabFramebuffer();
181 }
182
183 QImage img;
184};
185
186void tst_QOpenGLWindow::painter()
187{
188 PainterWindow w;
189 w.resize(w: 400, h: 400);
190 w.show();
191 QVERIFY(QTest::qWaitForWindowExposed(&w));
192
193 QCOMPARE(w.img.size(), w.size() * w.devicePixelRatio());
194 QVERIFY(w.img.pixel(QPoint(5, 5) * w.devicePixelRatio()) == qRgb(0, 0, 255));
195 QVERIFY(w.img.pixel(QPoint(200, 5) * w.devicePixelRatio()) == qRgb(255, 0, 0));
196}
197
198class PartialPainterWindow : public QOpenGLWindow
199{
200public:
201 PartialPainterWindow(QOpenGLWindow::UpdateBehavior u)
202 : QOpenGLWindow(u), x(0) { }
203
204 void paintGL() override {
205 ++paintCount;
206
207 QPainter p(this);
208 if (!x)
209 p.fillRect(r: QRect(0, 0, width(), height()), c: Qt::green);
210
211 p.fillRect(r: QRect(x, 0, 10, 10), c: Qt::blue);
212 x += 20;
213 }
214
215 int paintCount;
216 int x;
217};
218
219void tst_QOpenGLWindow::partial_data()
220{
221 QTest::addColumn<int>(name: "behavior");
222 QTest::newRow(dataTag: "blit") << int(QOpenGLWindow::PartialUpdateBlit);
223 QTest::newRow(dataTag: "blend") << int(QOpenGLWindow::PartialUpdateBlend);
224}
225
226void tst_QOpenGLWindow::partial()
227{
228 QFETCH(int, behavior);
229 QOpenGLWindow::UpdateBehavior u = QOpenGLWindow::UpdateBehavior(behavior);
230 PartialPainterWindow w(u);
231 w.resize(w: 800, h: 400);
232 w.show();
233 QVERIFY(QTest::qWaitForWindowExposed(&w));
234
235 // Add a couple of small blue rects.
236 for (int i = 0; i < 10; ++i) {
237 w.paintCount = 0;
238 w.update();
239 QTRY_VERIFY(w.paintCount > 0);
240 }
241
242 // Now since the painting went to an extra framebuffer, all the rects should
243 // be present since everything is preserved between the frames.
244 QImage img = w.grabFramebuffer();
245 QCOMPARE(img.size(), w.size() * w.devicePixelRatio());
246 QCOMPARE(img.pixel(QPoint(5, 5) * w.devicePixelRatio()), qRgb(0, 0, 255));
247 QCOMPARE(img.pixel(QPoint(15, 5) * w.devicePixelRatio()), qRgb(0, 255, 0));
248 QCOMPARE(img.pixel(QPoint(25, 5) * w.devicePixelRatio()), qRgb(0, 0, 255));
249}
250
251class PaintUnderOverWindow : public QOpenGLWindow
252{
253public:
254 PaintUnderOverWindow() : QOpenGLWindow(PartialUpdateBlend), m_state(None) { }
255 enum State {
256 None,
257 PaintUnder,
258 Paint,
259 PaintOver,
260 Error
261 } m_state;
262
263 void paintUnderGL() override {
264 if (m_state == None || m_state == PaintOver)
265 m_state = PaintUnder;
266 else
267 m_state = Error;
268
269 GLuint fbo = 0xFFFF;
270 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, params: (GLint *) &fbo);
271 QCOMPARE(fbo, QOpenGLContext::currentContext()->defaultFramebufferObject());
272 }
273
274 void paintGL() override {
275 if (m_state == PaintUnder)
276 m_state = Paint;
277 else
278 m_state = Error;
279
280 // Using PartialUpdateBlend so paintGL() targets a user fbo, not the default.
281 GLuint fbo = 0xFFFF;
282 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, params: (GLint *) &fbo);
283 QVERIFY(fbo != QOpenGLContext::currentContext()->defaultFramebufferObject());
284 QCOMPARE(fbo, defaultFramebufferObject());
285 }
286
287 void paintOverGL() override {
288 if (m_state == Paint)
289 m_state = PaintOver;
290 else
291 m_state = Error;
292
293 GLuint fbo = 0xFFFF;
294 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, params: (GLint *) &fbo);
295 QCOMPARE(fbo, QOpenGLContext::currentContext()->defaultFramebufferObject());
296 }
297};
298
299void tst_QOpenGLWindow::underOver()
300{
301 PaintUnderOverWindow w;
302 w.resize(w: 400, h: 400);
303 w.show();
304 QVERIFY(QTest::qWaitForWindowExposed(&w));
305
306 // under -> paint -> over -> under -> paint -> ... is the only acceptable sequence
307 QCOMPARE(w.m_state, PaintUnderOverWindow::PaintOver);
308}
309
310#include <tst_qopenglwindow.moc>
311
312QTEST_MAIN(tst_QOpenGLWindow)
313

source code of qtbase/tests/auto/gui/kernel/qopenglwindow/tst_qopenglwindow.cpp