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
30#include <QtTest/QtTest>
31
32#include <qcoreapplication.h>
33#include <qdebug.h>
34#include <qgl.h>
35#include <qglpixelbuffer.h>
36#include <qglframebufferobject.h>
37#include <qglcolormap.h>
38#include <qpaintengine.h>
39#include <qpainterpath.h>
40#include <qopenglfunctions.h>
41#include <qopenglframebufferobject.h>
42#include <qopenglpaintdevice.h>
43
44#include <QGraphicsView>
45#include <QGraphicsProxyWidget>
46#include <QVBoxLayout>
47#include <QOperatingSystemVersion>
48
49#ifdef QT_BUILD_INTERNAL
50#include <qpa/qplatformpixmap.h>
51#include <QtOpenGL/private/qgl_p.h>
52#include <QtGui/private/qimage_p.h>
53#include <QtGui/private/qimagepixmapcleanuphooks_p.h>
54#include <QtGui/private/qopenglextensions_p.h>
55#endif
56
57class tst_QGL : public QObject
58{
59Q_OBJECT
60
61public:
62 tst_QGL();
63 virtual ~tst_QGL();
64
65 static void initMain();
66
67private slots:
68 void initTestCase();
69 void getSetCheck();
70#ifdef QT_BUILD_INTERNAL
71 void qglContextDefaultBindTexture();
72 void openGLVersionCheck();
73 void shareRegister();
74 void textureCleanup();
75#endif
76 void partialGLWidgetUpdates_data();
77 void partialGLWidgetUpdates();
78 void glWidgetWithAlpha();
79 void glWidgetRendering();
80 void glFBOSimpleRendering();
81 void glFBORendering();
82 void currentFboSync();
83 void multipleFBOInterleavedRendering();
84 void glFBOUseInGLWidget();
85 void glPBufferRendering();
86 void glWidgetReparent();
87 void glWidgetRenderPixmap();
88 void colormap();
89 void fboFormat();
90 void testDontCrashOnDanglingResources();
91 void replaceClipping();
92 void clipTest();
93 void destroyFBOAfterContext();
94 void threadImages();
95 void nullRectCrash();
96 void graphicsViewClipping();
97 void extensions();
98 void closeAndThenShow();
99};
100
101tst_QGL::tst_QGL()
102{
103}
104
105tst_QGL::~tst_QGL()
106{
107}
108
109void tst_QGL::initMain()
110{
111 QCoreApplication::setAttribute(attribute: Qt::AA_DisableHighDpiScaling);
112}
113
114void tst_QGL::initTestCase()
115{
116 QGLWidget glWidget;
117 if (!glWidget.isValid())
118 QSKIP("QGL is not supported on the test system");
119}
120
121class MyGLContext : public QGLContext
122{
123public:
124 MyGLContext(const QGLFormat& format) : QGLContext(format) {}
125 bool windowCreated() const { return QGLContext::windowCreated(); }
126 void setWindowCreated(bool on) { QGLContext::setWindowCreated(on); }
127 bool initialized() const { return QGLContext::initialized(); }
128 void setInitialized(bool on) { QGLContext::setInitialized(on); }
129};
130
131class MyGLWidget : public QGLWidget
132{
133public:
134 MyGLWidget() : QGLWidget() {}
135 bool autoBufferSwap() const { return QGLWidget::autoBufferSwap(); }
136 void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); }
137};
138
139static int appDefaultDepth()
140{
141 static int depth = 0;
142 if (depth == 0) {
143 QPixmap pm(1, 1);
144 depth = pm.depth();
145 }
146 return depth;
147}
148
149// Using INT_MIN and INT_MAX will cause failures on systems
150// where "int" is 64-bit, so use the explicit values instead.
151#define TEST_INT_MIN (-2147483647 - 1)
152#define TEST_INT_MAX 2147483647
153
154// Testing get/set functions
155void tst_QGL::getSetCheck()
156{
157 QGLFormat obj1;
158 // int QGLFormat::depthBufferSize()
159 // void QGLFormat::setDepthBufferSize(int)
160 QCOMPARE(-1, obj1.depthBufferSize());
161 obj1.setDepthBufferSize(0);
162 QCOMPARE(0, obj1.depthBufferSize());
163 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648");
164 obj1.setDepthBufferSize(TEST_INT_MIN);
165 QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size
166 obj1.setDepthBufferSize(3);
167 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1");
168 obj1.setDepthBufferSize(-1);
169 QCOMPARE(3, obj1.depthBufferSize());
170 obj1.setDepthBufferSize(TEST_INT_MAX);
171 QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize());
172
173 // int QGLFormat::accumBufferSize()
174 // void QGLFormat::setAccumBufferSize(int)
175 QCOMPARE(-1, obj1.accumBufferSize());
176 obj1.setAccumBufferSize(0);
177 QCOMPARE(0, obj1.accumBufferSize());
178 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648");
179 obj1.setAccumBufferSize(TEST_INT_MIN);
180 QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size
181 obj1.setAccumBufferSize(3);
182 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1");
183 obj1.setAccumBufferSize(-1);
184 QCOMPARE(3, obj1.accumBufferSize());
185 obj1.setAccumBufferSize(TEST_INT_MAX);
186 QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize());
187
188 // int QGLFormat::redBufferSize()
189 // void QGLFormat::setRedBufferSize(int)
190 QCOMPARE(-1, obj1.redBufferSize());
191 obj1.setRedBufferSize(0);
192 QCOMPARE(0, obj1.redBufferSize());
193 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648");
194 obj1.setRedBufferSize(TEST_INT_MIN);
195 QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size
196 obj1.setRedBufferSize(3);
197 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1");
198 obj1.setRedBufferSize(-1);
199 QCOMPARE(3, obj1.redBufferSize());
200 obj1.setRedBufferSize(TEST_INT_MAX);
201 QCOMPARE(TEST_INT_MAX, obj1.redBufferSize());
202
203 // int QGLFormat::greenBufferSize()
204 // void QGLFormat::setGreenBufferSize(int)
205 QCOMPARE(-1, obj1.greenBufferSize());
206 obj1.setGreenBufferSize(0);
207 QCOMPARE(0, obj1.greenBufferSize());
208 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648");
209 obj1.setGreenBufferSize(TEST_INT_MIN);
210 QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size
211 obj1.setGreenBufferSize(3);
212 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1");
213 obj1.setGreenBufferSize(-1);
214 QCOMPARE(3, obj1.greenBufferSize());
215 obj1.setGreenBufferSize(TEST_INT_MAX);
216 QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize());
217
218 // int QGLFormat::blueBufferSize()
219 // void QGLFormat::setBlueBufferSize(int)
220 QCOMPARE(-1, obj1.blueBufferSize());
221 obj1.setBlueBufferSize(0);
222 QCOMPARE(0, obj1.blueBufferSize());
223 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648");
224 obj1.setBlueBufferSize(TEST_INT_MIN);
225 QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size
226 obj1.setBlueBufferSize(3);
227 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1");
228 obj1.setBlueBufferSize(-1);
229 QCOMPARE(3, obj1.blueBufferSize());
230 obj1.setBlueBufferSize(TEST_INT_MAX);
231 QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize());
232
233 // int QGLFormat::alphaBufferSize()
234 // void QGLFormat::setAlphaBufferSize(int)
235 QCOMPARE(-1, obj1.alphaBufferSize());
236 QCOMPARE(false, obj1.alpha());
237 QVERIFY(!obj1.testOption(QGL::AlphaChannel));
238 QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
239 obj1.setAlphaBufferSize(1);
240 QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha.
241 QCOMPARE(1, obj1.alphaBufferSize());
242 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648");
243 obj1.setAlphaBufferSize(TEST_INT_MIN);
244 QCOMPARE(1, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size
245 obj1.setAlphaBufferSize(3);
246 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1");
247 obj1.setAlphaBufferSize(-1);
248 QCOMPARE(3, obj1.alphaBufferSize());
249 obj1.setAlphaBufferSize(TEST_INT_MAX);
250 QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize());
251
252 // int QGLFormat::stencilBufferSize()
253 // void QGLFormat::setStencilBufferSize(int)
254 QCOMPARE(-1, obj1.stencilBufferSize());
255 obj1.setStencilBufferSize(1);
256 QCOMPARE(1, obj1.stencilBufferSize());
257 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648");
258 obj1.setStencilBufferSize(TEST_INT_MIN);
259 QCOMPARE(1, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size
260 obj1.setStencilBufferSize(3);
261 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1");
262 obj1.setStencilBufferSize(-1);
263 QCOMPARE(3, obj1.stencilBufferSize());
264 obj1.setStencilBufferSize(TEST_INT_MAX);
265 QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize());
266
267 // bool QGLFormat::sampleBuffers()
268 // void QGLFormat::setSampleBuffers(bool)
269 QCOMPARE(false, obj1.sampleBuffers());
270 QVERIFY(!obj1.testOption(QGL::SampleBuffers));
271 QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
272
273 obj1.setSampleBuffers(false);
274 QCOMPARE(false, obj1.sampleBuffers());
275 QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
276 obj1.setSampleBuffers(true);
277 QCOMPARE(true, obj1.sampleBuffers());
278 QVERIFY(obj1.testOption(QGL::SampleBuffers));
279
280 // int QGLFormat::samples()
281 // void QGLFormat::setSamples(int)
282 QCOMPARE(-1, obj1.samples());
283 obj1.setSamples(0);
284 QCOMPARE(0, obj1.samples());
285 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648");
286 obj1.setSamples(TEST_INT_MIN);
287 QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size
288 obj1.setSamples(3);
289 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1");
290 obj1.setSamples(-1);
291 QCOMPARE(3, obj1.samples());
292 obj1.setSamples(TEST_INT_MAX);
293 QCOMPARE(TEST_INT_MAX, obj1.samples());
294
295 // int QGLFormat::swapInterval()
296 // void QGLFormat::setSwapInterval(int)
297 QCOMPARE(-1, obj1.swapInterval());
298 obj1.setSwapInterval(0);
299 QCOMPARE(0, obj1.swapInterval());
300 obj1.setSwapInterval(TEST_INT_MIN);
301 QCOMPARE(TEST_INT_MIN, obj1.swapInterval());
302 obj1.setSwapInterval(-1);
303 QCOMPARE(-1, obj1.swapInterval());
304 obj1.setSwapInterval(TEST_INT_MAX);
305 QCOMPARE(TEST_INT_MAX, obj1.swapInterval());
306
307 // bool QGLFormat::doubleBuffer()
308 // void QGLFormat::setDoubleBuffer(bool)
309 QCOMPARE(true, obj1.doubleBuffer());
310 QVERIFY(obj1.testOption(QGL::DoubleBuffer));
311 QVERIFY(!obj1.testOption(QGL::SingleBuffer));
312 obj1.setDoubleBuffer(false);
313 QCOMPARE(false, obj1.doubleBuffer());
314 QVERIFY(!obj1.testOption(QGL::DoubleBuffer));
315 QVERIFY(obj1.testOption(QGL::SingleBuffer));
316 obj1.setDoubleBuffer(true);
317 QCOMPARE(true, obj1.doubleBuffer());
318 QVERIFY(obj1.testOption(QGL::DoubleBuffer));
319 QVERIFY(!obj1.testOption(QGL::SingleBuffer));
320
321 // bool QGLFormat::depth()
322 // void QGLFormat::setDepth(bool)
323 QCOMPARE(true, obj1.depth());
324 QVERIFY(obj1.testOption(QGL::DepthBuffer));
325 QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
326 obj1.setDepth(false);
327 QCOMPARE(false, obj1.depth());
328 QVERIFY(!obj1.testOption(QGL::DepthBuffer));
329 QVERIFY(obj1.testOption(QGL::NoDepthBuffer));
330 obj1.setDepth(true);
331 QCOMPARE(true, obj1.depth());
332 QVERIFY(obj1.testOption(QGL::DepthBuffer));
333 QVERIFY(!obj1.testOption(QGL::NoDepthBuffer));
334
335 // bool QGLFormat::rgba()
336 // void QGLFormat::setRgba(bool)
337 QCOMPARE(true, obj1.rgba());
338 QVERIFY(obj1.testOption(QGL::Rgba));
339 QVERIFY(!obj1.testOption(QGL::ColorIndex));
340 obj1.setRgba(false);
341 QCOMPARE(false, obj1.rgba());
342 QVERIFY(!obj1.testOption(QGL::Rgba));
343 QVERIFY(obj1.testOption(QGL::ColorIndex));
344 obj1.setRgba(true);
345 QCOMPARE(true, obj1.rgba());
346 QVERIFY(obj1.testOption(QGL::Rgba));
347 QVERIFY(!obj1.testOption(QGL::ColorIndex));
348
349 // bool QGLFormat::alpha()
350 // void QGLFormat::setAlpha(bool)
351 QVERIFY(obj1.testOption(QGL::AlphaChannel));
352 QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
353 obj1.setAlpha(false);
354 QCOMPARE(false, obj1.alpha());
355 QVERIFY(!obj1.testOption(QGL::AlphaChannel));
356 QVERIFY(obj1.testOption(QGL::NoAlphaChannel));
357 obj1.setAlpha(true);
358 QCOMPARE(true, obj1.alpha());
359 QVERIFY(obj1.testOption(QGL::AlphaChannel));
360 QVERIFY(!obj1.testOption(QGL::NoAlphaChannel));
361
362 // bool QGLFormat::accum()
363 // void QGLFormat::setAccum(bool)
364 obj1.setAccumBufferSize(0);
365 QCOMPARE(false, obj1.accum());
366 QVERIFY(!obj1.testOption(QGL::AccumBuffer));
367 QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
368 obj1.setAccum(false);
369 QCOMPARE(false, obj1.accum());
370 QVERIFY(!obj1.testOption(QGL::AccumBuffer));
371 QVERIFY(obj1.testOption(QGL::NoAccumBuffer));
372 obj1.setAccum(true);
373 QCOMPARE(true, obj1.accum());
374 QVERIFY(obj1.testOption(QGL::AccumBuffer));
375 QVERIFY(!obj1.testOption(QGL::NoAccumBuffer));
376
377 // bool QGLFormat::stencil()
378 // void QGLFormat::setStencil(bool)
379 QCOMPARE(true, obj1.stencil());
380 QVERIFY(obj1.testOption(QGL::StencilBuffer));
381 QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
382 obj1.setStencil(false);
383 QCOMPARE(false, obj1.stencil());
384 QVERIFY(!obj1.testOption(QGL::StencilBuffer));
385 QVERIFY(obj1.testOption(QGL::NoStencilBuffer));
386 obj1.setStencil(true);
387 QCOMPARE(true, obj1.stencil());
388 QVERIFY(obj1.testOption(QGL::StencilBuffer));
389 QVERIFY(!obj1.testOption(QGL::NoStencilBuffer));
390
391 // bool QGLFormat::stereo()
392 // void QGLFormat::setStereo(bool)
393 QCOMPARE(false, obj1.stereo());
394 QVERIFY(!obj1.testOption(QGL::StereoBuffers));
395 QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
396 obj1.setStereo(false);
397 QCOMPARE(false, obj1.stereo());
398 QVERIFY(!obj1.testOption(QGL::StereoBuffers));
399 QVERIFY(obj1.testOption(QGL::NoStereoBuffers));
400 obj1.setStereo(true);
401 QCOMPARE(true, obj1.stereo());
402 QVERIFY(obj1.testOption(QGL::StereoBuffers));
403 QVERIFY(!obj1.testOption(QGL::NoStereoBuffers));
404
405 // bool QGLFormat::directRendering()
406 // void QGLFormat::setDirectRendering(bool)
407 QCOMPARE(true, obj1.directRendering());
408 QVERIFY(obj1.testOption(QGL::DirectRendering));
409 QVERIFY(!obj1.testOption(QGL::IndirectRendering));
410 obj1.setDirectRendering(false);
411 QCOMPARE(false, obj1.directRendering());
412 QVERIFY(!obj1.testOption(QGL::DirectRendering));
413 QVERIFY(obj1.testOption(QGL::IndirectRendering));
414 obj1.setDirectRendering(true);
415 QCOMPARE(true, obj1.directRendering());
416 QVERIFY(obj1.testOption(QGL::DirectRendering));
417 QVERIFY(!obj1.testOption(QGL::IndirectRendering));
418
419 // bool QGLFormat::overlay()
420 // void QGLFormat::setOverlay(bool)
421 QCOMPARE(false, obj1.hasOverlay());
422 QVERIFY(!obj1.testOption(QGL::HasOverlay));
423 QVERIFY(obj1.testOption(QGL::NoOverlay));
424 obj1.setOverlay(false);
425 QCOMPARE(false, obj1.hasOverlay());
426 QVERIFY(!obj1.testOption(QGL::HasOverlay));
427 QVERIFY(obj1.testOption(QGL::NoOverlay));
428 obj1.setOverlay(true);
429 QCOMPARE(true, obj1.hasOverlay());
430 QVERIFY(obj1.testOption(QGL::HasOverlay));
431 QVERIFY(!obj1.testOption(QGL::NoOverlay));
432
433 // int QGLFormat::plane()
434 // void QGLFormat::setPlane(int)
435 QCOMPARE(0, obj1.plane());
436 obj1.setPlane(0);
437 QCOMPARE(0, obj1.plane());
438 obj1.setPlane(TEST_INT_MIN);
439 QCOMPARE(TEST_INT_MIN, obj1.plane());
440 obj1.setPlane(TEST_INT_MAX);
441 QCOMPARE(TEST_INT_MAX, obj1.plane());
442
443 // int QGLFormat::major/minorVersion()
444 // void QGLFormat::setVersion(int, int)
445 QCOMPARE(obj1.majorVersion(), 2);
446 QCOMPARE(obj1.minorVersion(), 0);
447 obj1.setVersion(major: 3, minor: 2);
448 QCOMPARE(obj1.majorVersion(), 3);
449 QCOMPARE(obj1.minorVersion(), 2);
450 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setVersion: Cannot set zero or negative version number 0.1");
451 obj1.setVersion(major: 0, minor: 1);
452 QCOMPARE(obj1.majorVersion(), 3);
453 QCOMPARE(obj1.minorVersion(), 2);
454 QTest::ignoreMessage(type: QtWarningMsg, message: "QGLFormat::setVersion: Cannot set zero or negative version number 3.-1");
455 obj1.setVersion(major: 3, minor: -1);
456 QCOMPARE(obj1.majorVersion(), 3);
457 QCOMPARE(obj1.minorVersion(), 2);
458 obj1.setVersion(TEST_INT_MAX, TEST_INT_MAX - 1);
459 QCOMPARE(obj1.majorVersion(), TEST_INT_MAX);
460 QCOMPARE(obj1.minorVersion(), TEST_INT_MAX - 1);
461
462
463 // operator== and operator!= for QGLFormat
464 QGLFormat format1;
465 QGLFormat format2;
466
467 QCOMPARE(format1, format2);
468 QVERIFY(!(format1 != format2));
469 format1.setDoubleBuffer(false);
470 QVERIFY(!(format1 == format2));
471 QVERIFY(format1 != format2);
472 format2.setDoubleBuffer(false);
473 QCOMPARE(format1, format2);
474 QVERIFY(!(format1 != format2));
475
476 format1.setDepthBufferSize(8);
477 QVERIFY(!(format1 == format2));
478 QVERIFY(format1 != format2);
479 format2.setDepthBufferSize(8);
480 QCOMPARE(format1, format2);
481 QVERIFY(!(format1 != format2));
482
483 format1.setAccumBufferSize(8);
484 QVERIFY(!(format1 == format2));
485 QVERIFY(format1 != format2);
486 format2.setAccumBufferSize(8);
487 QCOMPARE(format1, format2);
488 QVERIFY(!(format1 != format2));
489
490 format1.setRedBufferSize(8);
491 QVERIFY(!(format1 == format2));
492 QVERIFY(format1 != format2);
493 format2.setRedBufferSize(8);
494 QCOMPARE(format1, format2);
495 QVERIFY(!(format1 != format2));
496
497 format1.setGreenBufferSize(8);
498 QVERIFY(!(format1 == format2));
499 QVERIFY(format1 != format2);
500 format2.setGreenBufferSize(8);
501 QCOMPARE(format1, format2);
502 QVERIFY(!(format1 != format2));
503
504 format1.setBlueBufferSize(8);
505 QVERIFY(!(format1 == format2));
506 QVERIFY(format1 != format2);
507 format2.setBlueBufferSize(8);
508 QCOMPARE(format1, format2);
509 QVERIFY(!(format1 != format2));
510
511 format1.setAlphaBufferSize(8);
512 QVERIFY(!(format1 == format2));
513 QVERIFY(format1 != format2);
514 format2.setAlphaBufferSize(8);
515 QCOMPARE(format1, format2);
516 QVERIFY(!(format1 != format2));
517
518 format1.setStencilBufferSize(8);
519 QVERIFY(!(format1 == format2));
520 QVERIFY(format1 != format2);
521 format2.setStencilBufferSize(8);
522 QCOMPARE(format1, format2);
523 QVERIFY(!(format1 != format2));
524
525 format1.setSamples(8);
526 QVERIFY(!(format1 == format2));
527 QVERIFY(format1 != format2);
528 format2.setSamples(8);
529 QCOMPARE(format1, format2);
530 QVERIFY(!(format1 != format2));
531
532 format1.setSwapInterval(8);
533 QVERIFY(!(format1 == format2));
534 QVERIFY(format1 != format2);
535 format2.setSwapInterval(8);
536 QCOMPARE(format1, format2);
537 QVERIFY(!(format1 != format2));
538
539 format1.setPlane(8);
540 QVERIFY(!(format1 == format2));
541 QVERIFY(format1 != format2);
542 format2.setPlane(8);
543 QCOMPARE(format1, format2);
544 QVERIFY(!(format1 != format2));
545
546 format1.setVersion(major: 3, minor: 2);
547 QVERIFY(!(format1 == format2));
548 QVERIFY(format1 != format2);
549 format2.setVersion(major: 3, minor: 2);
550 QCOMPARE(format1, format2);
551 QVERIFY(!(format1 != format2));
552
553 format1.setProfile(QGLFormat::CoreProfile);
554 QVERIFY(!(format1 == format2));
555 QVERIFY(format1 != format2);
556 format2.setProfile(QGLFormat::CoreProfile);
557 QCOMPARE(format1, format2);
558 QVERIFY(!(format1 != format2));
559
560 format1.setOption(QGL::NoDeprecatedFunctions);
561 QVERIFY(!(format1 == format2));
562 QVERIFY(format1 != format2);
563 format2.setOption(QGL::NoDeprecatedFunctions);
564 QCOMPARE(format1, format2);
565 QVERIFY(!(format1 != format2));
566
567 // Copy constructor and assignment for QGLFormat.
568 QGLFormat format3(format1);
569 QGLFormat format4;
570 QCOMPARE(format1, format3);
571 QVERIFY(format1 != format4);
572 format4 = format1;
573 QCOMPARE(format1, format4);
574
575 // Check that modifying a copy doesn't affect the original.
576 format3.setRedBufferSize(16);
577 format4.setPlane(16);
578 QCOMPARE(format1.redBufferSize(), 8);
579 QCOMPARE(format1.plane(), 8);
580
581 // Check the QGLFormat constructor that takes an option list.
582 QGLFormat format5
583 (QGL::DepthBuffer | QGL::StereoBuffers | QGL::ColorIndex, 3);
584 QVERIFY(format5.depth());
585 QVERIFY(format5.stereo());
586 QVERIFY(format5.doubleBuffer()); // From defaultFormat()
587 QVERIFY(!format5.hasOverlay()); // From defaultFormat()
588 QVERIFY(!format5.rgba());
589 QCOMPARE(format5.plane(), 3);
590
591 // The default format should be the same as QGLFormat().
592 QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
593
594 // Modify the default format and check that it was changed.
595 QGLFormat::setDefaultFormat(format1);
596 QCOMPARE(QGLFormat::defaultFormat(), format1);
597
598 // Restore the default format.
599 QGLFormat::setDefaultFormat(QGLFormat());
600 QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
601
602 // Check the default overlay format's expected values.
603 QGLFormat overlay(QGLFormat::defaultOverlayFormat());
604 QCOMPARE(overlay.depthBufferSize(), -1);
605 QCOMPARE(overlay.accumBufferSize(), -1);
606 QCOMPARE(overlay.redBufferSize(), -1);
607 QCOMPARE(overlay.greenBufferSize(), -1);
608 QCOMPARE(overlay.blueBufferSize(), -1);
609 QCOMPARE(overlay.alphaBufferSize(), -1);
610 QCOMPARE(overlay.samples(), -1);
611 QCOMPARE(overlay.swapInterval(), -1);
612 QCOMPARE(overlay.plane(), 1);
613 QVERIFY(!overlay.sampleBuffers());
614 QVERIFY(!overlay.doubleBuffer());
615 QVERIFY(!overlay.depth());
616 QVERIFY(!overlay.rgba());
617 QVERIFY(!overlay.alpha());
618 QVERIFY(!overlay.accum());
619 QVERIFY(!overlay.stencil());
620 QVERIFY(!overlay.stereo());
621 QVERIFY(overlay.directRendering()); // Only option that should be on.
622 QVERIFY(!overlay.hasOverlay()); // Overlay doesn't need an overlay!
623
624 // Modify the default overlay format and check that it was changed.
625 QGLFormat::setDefaultOverlayFormat(format1);
626 QCOMPARE(QGLFormat::defaultOverlayFormat(), format1);
627
628 // Restore the default overlay format.
629 QGLFormat::setDefaultOverlayFormat(overlay);
630 QCOMPARE(QGLFormat::defaultOverlayFormat(), overlay);
631
632 MyGLContext obj2(obj1);
633 // bool QGLContext::windowCreated()
634 // void QGLContext::setWindowCreated(bool)
635 obj2.setWindowCreated(false);
636 QCOMPARE(false, obj2.windowCreated());
637 obj2.setWindowCreated(true);
638 QCOMPARE(true, obj2.windowCreated());
639
640 // bool QGLContext::initialized()
641 // void QGLContext::setInitialized(bool)
642 obj2.setInitialized(false);
643 QCOMPARE(false, obj2.initialized());
644 obj2.setInitialized(true);
645 QCOMPARE(true, obj2.initialized());
646
647 MyGLWidget obj3;
648 // bool QGLWidget::autoBufferSwap()
649 // void QGLWidget::setAutoBufferSwap(bool)
650 obj3.setAutoBufferSwap(false);
651 QCOMPARE(false, obj3.autoBufferSwap());
652 obj3.setAutoBufferSwap(true);
653 QCOMPARE(true, obj3.autoBufferSwap());
654}
655
656#ifdef QT_BUILD_INTERNAL
657QT_BEGIN_NAMESPACE
658extern QGLFormat::OpenGLVersionFlags qOpenGLVersionFlagsFromString(const QString &versionString);
659QT_END_NAMESPACE
660#endif
661
662#ifdef QT_BUILD_INTERNAL
663void tst_QGL::openGLVersionCheck()
664{
665 QString versionString;
666 QGLFormat::OpenGLVersionFlags expectedFlag;
667 QGLFormat::OpenGLVersionFlags versionFlag;
668
669 versionString = "1.1 Irix 6.5";
670 expectedFlag = QGLFormat::OpenGL_Version_1_1;
671 versionFlag = qOpenGLVersionFlagsFromString(versionString);
672 QCOMPARE(versionFlag, expectedFlag);
673
674 versionString = "1.2 Microsoft";
675 expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
676 versionFlag = qOpenGLVersionFlagsFromString(versionString);
677 QCOMPARE(versionFlag, expectedFlag);
678
679 versionString = "1.2.1";
680 expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
681 versionFlag = qOpenGLVersionFlagsFromString(versionString);
682 QCOMPARE(versionFlag, expectedFlag);
683
684 versionString = "1.3 NVIDIA";
685 expectedFlag = QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
686 versionFlag = qOpenGLVersionFlagsFromString(versionString);
687 QCOMPARE(versionFlag, expectedFlag);
688
689 versionString = "1.4";
690 expectedFlag = QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
691 versionFlag = qOpenGLVersionFlagsFromString(versionString);
692 QCOMPARE(versionFlag, expectedFlag);
693
694 versionString = "1.5 NVIDIA";
695 expectedFlag = QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
696 versionFlag = qOpenGLVersionFlagsFromString(versionString);
697 QCOMPARE(versionFlag, expectedFlag);
698
699 versionString = "2.0.2 NVIDIA 87.62";
700 expectedFlag = QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
701 versionFlag = qOpenGLVersionFlagsFromString(versionString);
702 QCOMPARE(versionFlag, expectedFlag);
703
704 versionString = "2.1 NVIDIA";
705 expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
706 versionFlag = qOpenGLVersionFlagsFromString(versionString);
707 QCOMPARE(versionFlag, expectedFlag);
708
709 versionString = "2.1";
710 expectedFlag = QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
711 versionFlag = qOpenGLVersionFlagsFromString(versionString);
712 QCOMPARE(versionFlag, expectedFlag);
713
714 versionString = "OpenGL ES-CM 1.0 ATI";
715 expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
716 versionFlag = qOpenGLVersionFlagsFromString(versionString);
717 QCOMPARE(versionFlag, expectedFlag);
718
719 versionString = "OpenGL ES-CL 1.0 ATI";
720 expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
721 versionFlag = qOpenGLVersionFlagsFromString(versionString);
722 QCOMPARE(versionFlag, expectedFlag);
723
724 versionString = "OpenGL ES-CM 1.1 ATI";
725 expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
726 versionFlag = qOpenGLVersionFlagsFromString(versionString);
727 QCOMPARE(versionFlag, expectedFlag);
728
729 versionString = "OpenGL ES-CL 1.1 ATI";
730 expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0;
731 versionFlag = qOpenGLVersionFlagsFromString(versionString);
732 QCOMPARE(versionFlag, expectedFlag);
733
734 versionString = "OpenGL ES 2.0 ATI";
735 expectedFlag = QGLFormat::OpenGL_ES_Version_2_0;
736 versionFlag = qOpenGLVersionFlagsFromString(versionString);
737 QCOMPARE(versionFlag, expectedFlag);
738
739 versionString = "3.0";
740 expectedFlag = QGLFormat::OpenGL_Version_3_0 | QGLFormat::OpenGL_Version_2_1 | QGLFormat::OpenGL_Version_2_0 | QGLFormat::OpenGL_Version_1_5 | QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1;
741 versionFlag = qOpenGLVersionFlagsFromString(versionString);
742 QCOMPARE(versionFlag, expectedFlag);
743
744 QGLWidget glWidget;
745 glWidget.show();
746 glWidget.makeCurrent();
747
748 // This is unfortunately the only test we can make on the actual openGLVersionFlags()
749 // However, the complicated parts are in openGLVersionFlags(const QString &versionString)
750 // tested above
751
752#if defined(QT_OPENGL_ES_2)
753 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
754#else
755 if (QOpenGLContext::currentContext()->isOpenGLES())
756 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
757 else
758 QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1);
759#endif //defined(QT_OPENGL_ES_2)
760}
761#endif //QT_BUILD_INTERNAL
762
763static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1)
764{
765 static int maxFuzz = 1;
766 static bool maxFuzzSet = false;
767
768 // On 16 bpp systems, we need to allow for more fuzz:
769 if (!maxFuzzSet) {
770 maxFuzzSet = true;
771 if (appDefaultDepth() < 24)
772 maxFuzz = 32;
773 }
774
775 int redFuzz = qAbs(t: qRed(rgb: testPixel) - qRed(rgb: refPixel));
776 int greenFuzz = qAbs(t: qGreen(rgb: testPixel) - qGreen(rgb: refPixel));
777 int blueFuzz = qAbs(t: qBlue(rgb: testPixel) - qBlue(rgb: refPixel));
778 int alphaFuzz = qAbs(t: qAlpha(rgb: testPixel) - qAlpha(rgb: refPixel));
779
780 if (refPixel != 0 && testPixel == 0) {
781 QString msg;
782 if (x >= 0) {
783 msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)")
784 .arg(a: x).arg(a: y)
785 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
786 } else {
787 msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)")
788 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
789 }
790
791 QTest::qFail(statementStr: msg.toLatin1(), file, line);
792 return false;
793 }
794
795 if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) {
796 QString msg;
797
798 if (x >= 0)
799 msg = QString("Pixel [%1,%2]: ").arg(a: x).arg(a: y);
800 else
801 msg = QString("Pixel ");
802
803 msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)")
804 .arg(a: maxFuzz)
805 .arg(a: qRed(rgb: testPixel)).arg(a: qGreen(rgb: testPixel)).arg(a: qBlue(rgb: testPixel)).arg(a: qAlpha(rgb: testPixel))
806 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
807 QTest::qFail(statementStr: msg.toLatin1(), file, line);
808 return false;
809 }
810 return true;
811}
812
813static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line)
814{
815 QCOMPARE(testImage.width(), referenceImage.width());
816 QCOMPARE(testImage.height(), referenceImage.height());
817
818 for (int y = 0; y < testImage.height(); y++) {
819 for (int x = 0; x < testImage.width(); x++) {
820 if (!fuzzyComparePixels(testPixel: testImage.pixel(x, y), refPixel: referenceImage.pixel(x, y), file, line, x, y)) {
821 // Might as well save the images for easier debugging:
822 referenceImage.save(fileName: "referenceImage.png");
823 testImage.save(fileName: "testImage.png");
824 return;
825 }
826 }
827 }
828}
829
830#define QFUZZY_COMPARE_IMAGES(A,B) \
831 fuzzyCompareImages(A, B, __FILE__, __LINE__)
832
833#define QFUZZY_COMPARE_PIXELS(A,B) \
834 fuzzyComparePixels(A, B, __FILE__, __LINE__)
835
836class UnclippedWidget : public QWidget
837{
838public:
839 bool painted;
840
841 UnclippedWidget()
842 : painted(false)
843 {
844 }
845
846 void paintEvent(QPaintEvent *)
847 {
848 QPainter p(this);
849 p.fillRect(r: rect().adjusted(xp1: -1000, yp1: -1000, xp2: 1000, yp2: 1000), c: Qt::black);
850
851 painted = true;
852 }
853};
854
855void tst_QGL::graphicsViewClipping()
856{
857 const int size = 64;
858 UnclippedWidget *widget = new UnclippedWidget;
859 widget->setFixedSize(w: size, h: size);
860
861 QGraphicsScene scene;
862
863 scene.addWidget(widget)->setPos(ax: 0, ay: 0);
864
865 QGraphicsView view(&scene);
866 // Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
867 view.setWindowFlags(view.windowFlags() | Qt::Tool);
868 view.setBackgroundBrush(Qt::white);
869 view.resize(w: 2*size, h: 2*size);
870
871 QGLWidget *viewport = new QGLWidget;
872 view.setViewport(viewport);
873 view.show();
874 qApp->setActiveWindow(&view);
875
876 if (!viewport->isValid())
877 return;
878
879 scene.setSceneRect(view.viewport()->rect());
880
881 QVERIFY(QTest::qWaitForWindowExposed(view.viewport()->windowHandle()));
882 #ifdef Q_OS_MAC
883 // The black rectangle jumps from the center to the upper left for some reason.
884 QTest::qWait(100);
885 #endif
886
887 QTRY_VERIFY(widget->painted);
888
889 QImage image = viewport->grabFrameBuffer();
890 QImage expected = image;
891
892 QPainter p(&expected);
893 p.fillRect(r: expected.rect(), c: Qt::white);
894 p.fillRect(r: QRect(0, 0, size, size), c: Qt::black);
895 p.end();
896
897 QFUZZY_COMPARE_IMAGES(image, expected);
898}
899
900void tst_QGL::partialGLWidgetUpdates_data()
901{
902 QTest::addColumn<bool>(name: "doubleBufferedContext");
903 QTest::addColumn<bool>(name: "autoFillBackground");
904 QTest::addColumn<bool>(name: "supportsPartialUpdates");
905
906 QTest::newRow(dataTag: "Double buffered context") << true << true << false;
907 QTest::newRow(dataTag: "Double buffered context without auto-fill background") << true << false << false;
908 QTest::newRow(dataTag: "Single buffered context") << false << true << false;
909 QTest::newRow(dataTag: "Single buffered context without auto-fill background") << false << false << true;
910}
911
912void tst_QGL::partialGLWidgetUpdates()
913{
914 QFETCH(bool, doubleBufferedContext);
915 QFETCH(bool, autoFillBackground);
916 QFETCH(bool, supportsPartialUpdates);
917
918 class MyGLWidget : public QGLWidget
919 {
920 public:
921 QRegion paintEventRegion;
922 void paintEvent(QPaintEvent *e)
923 {
924 paintEventRegion = e->region();
925 }
926 };
927
928 QGLFormat format = QGLFormat::defaultFormat();
929 format.setDoubleBuffer(doubleBufferedContext);
930 QGLFormat::setDefaultFormat(format);
931
932 MyGLWidget widget;
933 widget.setFixedSize(w: 150, h: 150);
934 widget.setAutoFillBackground(autoFillBackground);
935 widget.show();
936 QVERIFY(QTest::qWaitForWindowExposed(&widget));
937 QCoreApplication::processEvents(); // Process all queued paint events
938
939 if (widget.format().doubleBuffer() != doubleBufferedContext)
940 QSKIP("Platform does not support requested format");
941
942 widget.paintEventRegion = QRegion();
943 widget.repaint(x: 50, y: 50, w: 50, h: 50);
944
945 if (supportsPartialUpdates)
946 QCOMPARE(widget.paintEventRegion, QRegion(50, 50, 50, 50));
947 else
948 QCOMPARE(widget.paintEventRegion, QRegion(widget.rect()));
949}
950
951
952// This tests that rendering to a QGLPBuffer using QPainter works.
953void tst_QGL::glPBufferRendering()
954{
955 if (!QGLPixelBuffer::hasOpenGLPbuffers())
956 QSKIP("QGLPixelBuffer not supported on this platform");
957
958 QGLPixelBuffer* pbuf = new QGLPixelBuffer(128, 128);
959
960 QPainter p;
961 bool begun = p.begin(pbuf);
962 QVERIFY(begun);
963
964 QPaintEngine::Type engineType = p.paintEngine()->type();
965 QVERIFY(engineType == QPaintEngine::OpenGL || engineType == QPaintEngine::OpenGL2);
966
967 p.fillRect(x: 0, y: 0, w: 128, h: 128, c: Qt::red);
968 p.fillRect(x: 32, y: 32, w: 64, h: 64, c: Qt::blue);
969 p.end();
970
971 QImage fb = pbuf->toImage();
972 delete pbuf;
973
974 QImage reference(128, 128, fb.format());
975 p.begin(&reference);
976 p.fillRect(x: 0, y: 0, w: 128, h: 128, c: Qt::red);
977 p.fillRect(x: 32, y: 32, w: 64, h: 64, c: Qt::blue);
978 p.end();
979
980 QFUZZY_COMPARE_IMAGES(fb, reference);
981}
982
983void tst_QGL::glWidgetWithAlpha()
984{
985 QGLWidget* w = new QGLWidget(QGLFormat(QGL::AlphaChannel));
986 w->show();
987 QVERIFY(QTest::qWaitForWindowExposed(w));
988
989 delete w;
990}
991
992
993void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height)
994{
995 QPainterPath intersectingPath;
996 intersectingPath.moveTo(x: 0, y: 0);
997 intersectingPath.lineTo(x: 100, y: 0);
998 intersectingPath.lineTo(x: 0, y: 100);
999 intersectingPath.lineTo(x: 100, y: 100);
1000 intersectingPath.closeSubpath();
1001
1002 QPainterPath trianglePath;
1003 trianglePath.moveTo(x: 50, y: 0);
1004 trianglePath.lineTo(x: 100, y: 100);
1005 trianglePath.lineTo(x: 0, y: 100);
1006 trianglePath.closeSubpath();
1007
1008 painter->setTransform(transform: QTransform()); // reset xform
1009 painter->fillRect(x: -1, y: -1, w: width+2, h: height+2, c: Qt::red); // Background
1010 painter->translate(dx: 14, dy: 14);
1011 painter->fillPath(path: intersectingPath, brush: Qt::blue); // Test stencil buffer works
1012 painter->translate(dx: 128, dy: 0);
1013 painter->setClipPath(path: trianglePath); // Test depth buffer works
1014 painter->setTransform(transform: QTransform()); // reset xform ready for fill
1015 painter->fillRect(x: -1, y: -1, w: width+2, h: height+2, c: Qt::green);
1016}
1017
1018void qt_opengl_check_test_pattern(const QImage& img)
1019{
1020 // As we're doing more than trivial painting, we can't just compare to
1021 // an image rendered with raster. Instead, we sample at well-defined
1022 // test-points:
1023 QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb());
1024 QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb());
1025 QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb());
1026 QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb());
1027
1028 QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb());
1029 QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb());
1030 QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb());
1031}
1032
1033class GLWidget : public QGLWidget
1034{
1035public:
1036 GLWidget(QWidget* p = 0)
1037 : QGLWidget(p), beginOk(false), engineType(QPaintEngine::MaxUser) {}
1038 bool beginOk;
1039 QPaintEngine::Type engineType;
1040 void paintGL()
1041 {
1042 QPainter p;
1043 beginOk = p.begin(this);
1044 QPaintEngine* pe = p.paintEngine();
1045 engineType = pe->type();
1046
1047 qt_opengl_draw_test_pattern(painter: &p, width: width(), height: height());
1048
1049 // No p.end() or swap buffers, should be done automatically
1050 }
1051
1052};
1053
1054void tst_QGL::glWidgetRendering()
1055{
1056 GLWidget w;
1057 w.resize(w: 256, h: 128);
1058 w.show();
1059
1060 QVERIFY(QTest::qWaitForWindowExposed(&w));
1061
1062 QVERIFY(w.beginOk);
1063 QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2);
1064
1065#if defined(Q_OS_QNX)
1066 // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
1067 // after a buffer swap. This is why we have to swap the buffer explicitly before calling
1068 // grabFrameBuffer to retrieve the content of the front buffer.
1069 w.swapBuffers();
1070#endif
1071 QImage fb = w.grabFrameBuffer(withAlpha: false);
1072 qt_opengl_check_test_pattern(img: fb);
1073}
1074
1075void tst_QGL::glFBOSimpleRendering()
1076{
1077 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1078 QSKIP("QGLFramebufferObject not supported on this platform");
1079
1080 QGLWidget glw;
1081 glw.makeCurrent();
1082
1083 // No multisample with combined depth/stencil attachment:
1084 QGLFramebufferObjectFormat fboFormat;
1085 fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
1086
1087 QGLFramebufferObject *fbo = new QGLFramebufferObject(200, 100, fboFormat);
1088
1089 fbo->bind();
1090
1091 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1092 funcs->glClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0);
1093 funcs->glClear(GL_COLOR_BUFFER_BIT);
1094 funcs->glFinish();
1095
1096 QImage fb = fbo->toImage().convertToFormat(f: QImage::Format_RGB32);
1097 QImage reference(fb.size(), QImage::Format_RGB32);
1098 reference.fill(pixel: 0xffff0000);
1099
1100 QFUZZY_COMPARE_IMAGES(fb, reference);
1101
1102 delete fbo;
1103}
1104
1105// NOTE: This tests that CombinedDepthStencil attachment works by assuming the
1106// GL2 engine is being used and is implemented the same way as it was when
1107// this autotest was written. If this is not the case, there may be some
1108// false-positives: I.e. The test passes when either the depth or stencil
1109// buffer is actually missing. But that's probably ok anyway.
1110void tst_QGL::glFBORendering()
1111{
1112#if defined(Q_OS_QNX)
1113 QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
1114#endif
1115 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1116 QSKIP("QGLFramebufferObject not supported on this platform");
1117
1118 QGLWidget glw;
1119 glw.makeCurrent();
1120
1121 // No multisample with combined depth/stencil attachment:
1122 QGLFramebufferObjectFormat fboFormat;
1123 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1124
1125 // Don't complicate things by using NPOT:
1126 QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
1127
1128 if (fbo->attachment() != QGLFramebufferObject::CombinedDepthStencil) {
1129 delete fbo;
1130 QSKIP("FBOs missing combined depth~stencil support");
1131 }
1132
1133 QPainter fboPainter;
1134 bool painterBegun = fboPainter.begin(fbo);
1135 QVERIFY(painterBegun);
1136
1137 qt_opengl_draw_test_pattern(painter: &fboPainter, width: fbo->width(), height: fbo->height());
1138
1139 fboPainter.end();
1140
1141 QImage fb = fbo->toImage().convertToFormat(f: QImage::Format_RGB32);
1142 delete fbo;
1143
1144 qt_opengl_check_test_pattern(img: fb);
1145}
1146
1147class QOpenGLFramebufferObjectPaintDevice : public QOpenGLPaintDevice
1148{
1149public:
1150 QOpenGLFramebufferObjectPaintDevice(int width, int height)
1151 : QOpenGLPaintDevice(width, height)
1152 , m_fbo(width, height, QOpenGLFramebufferObject::CombinedDepthStencil)
1153 {
1154 }
1155
1156 void ensureActiveTarget()
1157 {
1158 m_fbo.bind();
1159 }
1160
1161 QImage toImage() const
1162 {
1163 return m_fbo.toImage();
1164 }
1165
1166private:
1167 QOpenGLFramebufferObject m_fbo;
1168};
1169
1170void tst_QGL::currentFboSync()
1171{
1172 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1173 QSKIP("QGLFramebufferObject not supported on this platform");
1174
1175#if defined(Q_OS_QNX)
1176 QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
1177#endif
1178
1179 QGLWidget glw;
1180 glw.makeCurrent();
1181
1182 // For some reason we offer inter-operatibility between QGL and QOpenGL
1183 // paint engines. (?!) Let's check if the two engines can be used to perform
1184 // drawing in turns on different targets within the same context.
1185
1186 {
1187 QGLFramebufferObject fbo1(256, 256, QGLFramebufferObject::CombinedDepthStencil);
1188
1189 QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
1190
1191 QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
1192 QPainter sourcePainter(&sourceImage);
1193 qt_opengl_draw_test_pattern(painter: &sourcePainter, width: 256, height: 256);
1194
1195 QPainter fbo1Painter(&fbo1);
1196
1197 QPainter fbo2Painter(&fbo2);
1198 fbo2Painter.drawImage(x: 0, y: 0, image: sourceImage);
1199 fbo2Painter.end();
1200
1201 QImage fbo2Image = fbo2.toImage();
1202
1203 fbo1Painter.drawImage(x: 0, y: 0, image: sourceImage);
1204 fbo1Painter.end();
1205
1206 QGLFramebufferObject::bindDefault();
1207
1208 // Convert the QGLFBO's result since QOpenGLFBO uses a wider
1209 // variety of possible return formats.
1210 QCOMPARE(fbo1.toImage().convertToFormat(fbo2Image.format()), fbo2Image);
1211 }
1212
1213 {
1214 QGLFramebufferObject fbo1(512, 512, QGLFramebufferObject::CombinedDepthStencil);
1215
1216 QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
1217
1218 QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
1219 QPainter sourcePainter(&sourceImage);
1220 qt_opengl_draw_test_pattern(painter: &sourcePainter, width: 256, height: 256);
1221
1222 QPainter fbo2Painter(&fbo2);
1223 fbo2Painter.drawImage(x: 0, y: 0, image: sourceImage);
1224 QImage fbo2Image1 = fbo2.toImage();
1225 fbo2Painter.fillRect(x: 0, y: 0, w: 256, h: 256, c: Qt::white);
1226
1227 QPainter fbo1Painter(&fbo1);
1228 fbo1Painter.drawImage(x: 0, y: 0, image: sourceImage);
1229 fbo1Painter.end();
1230
1231 // check that the OpenGL paint engine now knows it needs to sync
1232 fbo2Painter.drawImage(x: 0, y: 0, image: sourceImage);
1233 QImage fbo2Image2 = fbo2.toImage();
1234
1235 fbo2Painter.end();
1236
1237 QCOMPARE(fbo2Image1, fbo2Image2);
1238 }
1239}
1240
1241// Tests multiple QPainters active on different FBOs at the same time, with
1242// interleaving painting. Performance-wise, this is sub-optimal, but it still
1243// has to work flawlessly
1244void tst_QGL::multipleFBOInterleavedRendering()
1245{
1246 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1247 QSKIP("QGLFramebufferObject not supported on this platform");
1248
1249 QGLWidget glw;
1250 glw.makeCurrent();
1251
1252 // No multisample with combined depth/stencil attachment:
1253 QGLFramebufferObjectFormat fboFormat;
1254 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1255
1256 QGLFramebufferObject *fbo1 = new QGLFramebufferObject(256, 128, fboFormat);
1257 QGLFramebufferObject *fbo2 = new QGLFramebufferObject(256, 128, fboFormat);
1258 QGLFramebufferObject *fbo3 = new QGLFramebufferObject(256, 128, fboFormat);
1259
1260 if ( (fbo1->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
1261 (fbo2->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
1262 (fbo3->attachment() != QGLFramebufferObject::CombinedDepthStencil) )
1263 {
1264 delete fbo1;
1265 delete fbo2;
1266 delete fbo3;
1267 QSKIP("FBOs missing combined depth~stencil support");
1268 }
1269
1270 QPainter fbo1Painter;
1271 QPainter fbo2Painter;
1272 QPainter fbo3Painter;
1273
1274 QVERIFY(fbo1Painter.begin(fbo1));
1275 QVERIFY(fbo2Painter.begin(fbo2));
1276 QVERIFY(fbo3Painter.begin(fbo3));
1277
1278 // Confirm we're using the GL2 engine, as interleaved rendering isn't supported
1279 // on the GL1 engine:
1280 if (fbo1Painter.paintEngine()->type() != QPaintEngine::OpenGL2)
1281 QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher");
1282
1283 QPainterPath intersectingPath;
1284 intersectingPath.moveTo(x: 0, y: 0);
1285 intersectingPath.lineTo(x: 100, y: 0);
1286 intersectingPath.lineTo(x: 0, y: 100);
1287 intersectingPath.lineTo(x: 100, y: 100);
1288 intersectingPath.closeSubpath();
1289
1290 QPainterPath trianglePath;
1291 trianglePath.moveTo(x: 50, y: 0);
1292 trianglePath.lineTo(x: 100, y: 100);
1293 trianglePath.lineTo(x: 0, y: 100);
1294 trianglePath.closeSubpath();
1295
1296 fbo1Painter.fillRect(x: 0, y: 0, w: fbo1->width(), h: fbo1->height(), c: Qt::red); // Background
1297 fbo2Painter.fillRect(x: 0, y: 0, w: fbo2->width(), h: fbo2->height(), c: Qt::green); // Background
1298 fbo3Painter.fillRect(x: 0, y: 0, w: fbo3->width(), h: fbo3->height(), c: Qt::blue); // Background
1299
1300 fbo1Painter.translate(dx: 14, dy: 14);
1301 fbo2Painter.translate(dx: 14, dy: 14);
1302 fbo3Painter.translate(dx: 14, dy: 14);
1303
1304 fbo1Painter.fillPath(path: intersectingPath, brush: Qt::blue); // Test stencil buffer works
1305 fbo2Painter.fillPath(path: intersectingPath, brush: Qt::red); // Test stencil buffer works
1306 fbo3Painter.fillPath(path: intersectingPath, brush: Qt::green); // Test stencil buffer works
1307
1308 fbo1Painter.translate(dx: 128, dy: 0);
1309 fbo2Painter.translate(dx: 128, dy: 0);
1310 fbo3Painter.translate(dx: 128, dy: 0);
1311
1312 fbo1Painter.setClipPath(path: trianglePath);
1313 fbo2Painter.setClipPath(path: trianglePath);
1314 fbo3Painter.setClipPath(path: trianglePath);
1315
1316 fbo1Painter.setTransform(transform: QTransform()); // reset xform
1317 fbo2Painter.setTransform(transform: QTransform()); // reset xform
1318 fbo3Painter.setTransform(transform: QTransform()); // reset xform
1319
1320 fbo1Painter.fillRect(x: 0, y: 0, w: fbo1->width(), h: fbo1->height(), c: Qt::green);
1321 fbo2Painter.fillRect(x: 0, y: 0, w: fbo2->width(), h: fbo2->height(), c: Qt::blue);
1322 fbo3Painter.fillRect(x: 0, y: 0, w: fbo3->width(), h: fbo3->height(), c: Qt::red);
1323
1324 fbo1Painter.end();
1325 fbo2Painter.end();
1326 fbo3Painter.end();
1327
1328 QImage fb1 = fbo1->toImage().convertToFormat(f: QImage::Format_RGB32);
1329 QImage fb2 = fbo2->toImage().convertToFormat(f: QImage::Format_RGB32);
1330 QImage fb3 = fbo3->toImage().convertToFormat(f: QImage::Format_RGB32);
1331 delete fbo1;
1332 delete fbo2;
1333 delete fbo3;
1334
1335 // As we're doing more than trivial painting, we can't just compare to
1336 // an image rendered with raster. Instead, we sample at well-defined
1337 // test-points:
1338 QFUZZY_COMPARE_PIXELS(fb1.pixel(39, 64), QColor(Qt::red).rgb());
1339 QFUZZY_COMPARE_PIXELS(fb1.pixel(89, 64), QColor(Qt::red).rgb());
1340 QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 39), QColor(Qt::blue).rgb());
1341 QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 89), QColor(Qt::blue).rgb());
1342 QFUZZY_COMPARE_PIXELS(fb1.pixel(167, 39), QColor(Qt::red).rgb());
1343 QFUZZY_COMPARE_PIXELS(fb1.pixel(217, 39), QColor(Qt::red).rgb());
1344 QFUZZY_COMPARE_PIXELS(fb1.pixel(192, 64), QColor(Qt::green).rgb());
1345
1346 QFUZZY_COMPARE_PIXELS(fb2.pixel(39, 64), QColor(Qt::green).rgb());
1347 QFUZZY_COMPARE_PIXELS(fb2.pixel(89, 64), QColor(Qt::green).rgb());
1348 QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 39), QColor(Qt::red).rgb());
1349 QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 89), QColor(Qt::red).rgb());
1350 QFUZZY_COMPARE_PIXELS(fb2.pixel(167, 39), QColor(Qt::green).rgb());
1351 QFUZZY_COMPARE_PIXELS(fb2.pixel(217, 39), QColor(Qt::green).rgb());
1352 QFUZZY_COMPARE_PIXELS(fb2.pixel(192, 64), QColor(Qt::blue).rgb());
1353
1354 QFUZZY_COMPARE_PIXELS(fb3.pixel(39, 64), QColor(Qt::blue).rgb());
1355 QFUZZY_COMPARE_PIXELS(fb3.pixel(89, 64), QColor(Qt::blue).rgb());
1356 QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 39), QColor(Qt::green).rgb());
1357 QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 89), QColor(Qt::green).rgb());
1358 QFUZZY_COMPARE_PIXELS(fb3.pixel(167, 39), QColor(Qt::blue).rgb());
1359 QFUZZY_COMPARE_PIXELS(fb3.pixel(217, 39), QColor(Qt::blue).rgb());
1360 QFUZZY_COMPARE_PIXELS(fb3.pixel(192, 64), QColor(Qt::red).rgb());
1361}
1362
1363class FBOUseInGLWidget : public QGLWidget
1364{
1365public:
1366 bool widgetPainterBeginOk;
1367 bool fboPainterBeginOk;
1368 QImage fboImage;
1369protected:
1370 void paintEvent(QPaintEvent*)
1371 {
1372 QPainter widgetPainter;
1373 widgetPainterBeginOk = widgetPainter.begin(this);
1374 QGLFramebufferObjectFormat fboFormat;
1375 fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
1376 QGLFramebufferObject *fbo = new QGLFramebufferObject(100, 100, fboFormat);
1377
1378 QPainter fboPainter;
1379 fboPainterBeginOk = fboPainter.begin(fbo);
1380 fboPainter.fillRect(x: -1, y: -1, w: 130, h: 130, c: Qt::red);
1381 fboPainter.end();
1382 fboImage = fbo->toImage();
1383
1384 widgetPainter.fillRect(x: -1, y: -1, w: width()+2, h: height()+2, c: Qt::blue);
1385
1386 delete fbo;
1387 }
1388
1389};
1390
1391void tst_QGL::glFBOUseInGLWidget()
1392{
1393 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1394 QSKIP("QGLFramebufferObject not supported on this platform");
1395
1396 FBOUseInGLWidget w;
1397 w.resize(w: 100, h: 100);
1398 w.showNormal();
1399
1400 QVERIFY(QTest::qWaitForWindowExposed(&w));
1401
1402 QVERIFY(w.widgetPainterBeginOk);
1403 QVERIFY(w.fboPainterBeginOk);
1404
1405#if defined(Q_OS_QNX)
1406 // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
1407 // after a buffer swap. This is why we have to swap the buffer explicitly before calling
1408 // grabFrameBuffer to retrieve the content of the front buffer
1409 w.swapBuffers();
1410#endif
1411
1412 QImage widgetFB = w.grabFrameBuffer(withAlpha: false);
1413 QImage widgetReference(widgetFB.size(), widgetFB.format());
1414 widgetReference.fill(pixel: 0xff0000ff);
1415 QFUZZY_COMPARE_IMAGES(widgetFB, widgetReference);
1416
1417 QImage fboReference(w.fboImage.size(), w.fboImage.format());
1418 fboReference.fill(pixel: 0xffff0000);
1419 QFUZZY_COMPARE_IMAGES(w.fboImage, fboReference);
1420}
1421
1422void tst_QGL::glWidgetReparent()
1423{
1424 // Try it as a top-level first:
1425 GLWidget *widget = new GLWidget;
1426 widget->setObjectName(QStringLiteral("glWidget1"));
1427 widget->setGeometry(ax: 0, ay: 0, aw: 200, ah: 30);
1428 widget->show();
1429
1430 QWidget grandParentWidget;
1431 grandParentWidget.setObjectName(QStringLiteral("grandParentWidget"));
1432 grandParentWidget.setPalette(Qt::blue);
1433 QVBoxLayout grandParentLayout(&grandParentWidget);
1434
1435 QWidget parentWidget(&grandParentWidget);
1436 parentWidget.setObjectName(QStringLiteral("parentWidget"));
1437 grandParentLayout.addWidget(&parentWidget);
1438 parentWidget.setPalette(Qt::green);
1439 parentWidget.setAutoFillBackground(true);
1440 QVBoxLayout parentLayout(&parentWidget);
1441
1442 grandParentWidget.setGeometry(ax: 0, ay: 100, aw: 200, ah: 200);
1443 grandParentWidget.show();
1444
1445 QVERIFY(QTest::qWaitForWindowExposed(widget));
1446 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1447
1448 QVERIFY(parentWidget.children().count() == 1); // The layout
1449
1450 // Now both widgets should be created & shown, time to re-parent:
1451 parentLayout.addWidget(widget);
1452
1453 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1454
1455 QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
1456 QVERIFY(parentWidget.children().contains(widget));
1457 QTRY_VERIFY(widget->height() > 30);
1458
1459 delete widget;
1460
1461 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1462
1463 QVERIFY(parentWidget.children().count() == 1); // The layout
1464
1465 // Now do pretty much the same thing, but don't show the
1466 // widget first:
1467 widget = new GLWidget;
1468 widget->setObjectName(QStringLiteral("glWidget2"));
1469 parentLayout.addWidget(widget);
1470
1471 QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
1472
1473 QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
1474 QVERIFY(parentWidget.children().contains(widget));
1475 QVERIFY(widget->height() > 30);
1476
1477 delete widget;
1478}
1479
1480class RenderPixmapWidget : public QGLWidget
1481{
1482protected:
1483 void initializeGL() {
1484 // Set some gl state:
1485 QOpenGLContext::currentContext()->functions()->glClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0);
1486 }
1487
1488 void paintGL() {
1489 QOpenGLContext::currentContext()->functions()->glClear(GL_COLOR_BUFFER_BIT);
1490 }
1491};
1492
1493void tst_QGL::glWidgetRenderPixmap()
1494{
1495 RenderPixmapWidget *w = new RenderPixmapWidget;
1496
1497 QSize pmSize = QSize(100, 100);
1498 QPixmap pm = w->renderPixmap(w: pmSize.width(), h: pmSize.height(), useContext: false);
1499
1500 delete w;
1501
1502 QImage fb = pm.toImage().convertToFormat(f: QImage::Format_RGB32);
1503 QImage reference(pmSize, QImage::Format_RGB32);
1504 reference.fill(pixel: 0xffff0000);
1505
1506 QFUZZY_COMPARE_IMAGES(fb, reference);
1507}
1508
1509class ColormapExtended : public QGLColormap
1510{
1511public:
1512 ColormapExtended() {}
1513
1514 Qt::HANDLE handle() { return QGLColormap::handle(); }
1515 void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); }
1516};
1517
1518void tst_QGL::colormap()
1519{
1520 // Check the properties of the default empty colormap.
1521 QGLColormap cmap1;
1522 QVERIFY(cmap1.isEmpty());
1523 QCOMPARE(cmap1.size(), 0);
1524 QCOMPARE(cmap1.entryRgb(0), QRgb(0));
1525 QCOMPARE(cmap1.entryRgb(-1), QRgb(0));
1526 QCOMPARE(cmap1.entryRgb(100), QRgb(0));
1527 QVERIFY(!cmap1.entryColor(0).isValid());
1528 QVERIFY(!cmap1.entryColor(-1).isValid());
1529 QVERIFY(!cmap1.entryColor(100).isValid());
1530 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1);
1531 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1);
1532
1533 // Set an entry and re-test.
1534 cmap1.setEntry(idx: 56, color: qRgb(r: 255, g: 0, b: 0));
1535 // The colormap is still considered "empty" even though it
1536 // has entries in it now. The isEmpty() method is used to
1537 // detect when the colormap is in use by a GL widget,
1538 // not to detect when it is empty!
1539 QVERIFY(cmap1.isEmpty());
1540 QCOMPARE(cmap1.size(), 256);
1541 QCOMPARE(cmap1.entryRgb(0), QRgb(0));
1542 QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255));
1543 QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0));
1544 QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255));
1545 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
1546 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
1547
1548 // Set some more entries.
1549 static QRgb const colors[] = {
1550 qRgb(r: 255, g: 0, b: 0),
1551 qRgb(r: 0, g: 255, b: 0),
1552 qRgb(r: 255, g: 255, b: 255),
1553 qRgb(r: 0, g: 0, b: 255),
1554 qRgb(r: 0, g: 0, b: 0)
1555 };
1556 cmap1.setEntry(idx: 57, color: QColor(0, 255, 0));
1557 cmap1.setEntries(count: 3, colors: colors + 2, base: 58);
1558 cmap1.setEntries(count: 5, colors, base: 251);
1559 int idx;
1560 for (idx = 0; idx < 5; ++idx) {
1561 QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]);
1562 QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx]));
1563 QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]);
1564 QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx]));
1565 }
1566 QCOMPARE(cmap1.size(), 256);
1567
1568 // Perform color lookups.
1569 QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56);
1570 QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255.
1571 QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0);
1572 QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57);
1573 QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58);
1574 QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59);
1575 QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1);
1576 QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1);
1577 QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1);
1578 QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1);
1579 QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1);
1580 QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1);
1581 QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56);
1582 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60);
1583 QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0);
1584 QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57);
1585 QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58);
1586 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59);
1587 QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56);
1588 QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57);
1589 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59);
1590 QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0);
1591 QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0);
1592 QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0);
1593
1594 // Make some copies of the colormap and check that they are the same.
1595 QGLColormap cmap2(cmap1);
1596 QGLColormap cmap3;
1597 cmap3 = cmap1;
1598 QVERIFY(cmap2.isEmpty());
1599 QVERIFY(cmap3.isEmpty());
1600 QCOMPARE(cmap2.size(), 256);
1601 QCOMPARE(cmap3.size(), 256);
1602 for (idx = 0; idx < 256; ++idx) {
1603 QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
1604 QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
1605 }
1606
1607 // Modify an entry in one of the copies and recheck the original.
1608 cmap2.setEntry(idx: 45, color: qRgb(r: 255, g: 0, b: 0));
1609 for (idx = 0; idx < 256; ++idx) {
1610 if (idx != 45)
1611 QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx));
1612 else
1613 QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0));
1614 QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx));
1615 }
1616
1617 // Check that setting the handle will cause isEmpty() to work right.
1618 ColormapExtended cmap4;
1619 cmap4.setEntry(idx: 56, color: qRgb(r: 255, g: 0, b: 0));
1620 QVERIFY(cmap4.isEmpty());
1621 QCOMPARE(cmap4.size(), 256);
1622 cmap4.setHandle(Qt::HANDLE(42));
1623 QCOMPARE(cmap4.handle(), Qt::HANDLE(42));
1624 QVERIFY(!cmap4.isEmpty());
1625 QCOMPARE(cmap4.size(), 256);
1626}
1627
1628#ifndef GL_TEXTURE_3D
1629#define GL_TEXTURE_3D 0x806F
1630#endif
1631
1632#ifndef GL_RGB16
1633#define GL_RGB16 0x8054
1634#endif
1635
1636void tst_QGL::fboFormat()
1637{
1638 // Check the initial conditions.
1639 QGLFramebufferObjectFormat format1;
1640 QCOMPARE(format1.samples(), 0);
1641 QCOMPARE(format1.attachment(), QGLFramebufferObject::NoAttachment);
1642 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D));
1643 int expectedFormat =
1644#ifdef QT_OPENGL_ES_2
1645 GL_RGBA;
1646#else
1647 QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8;
1648#endif
1649 QCOMPARE(int(format1.internalTextureFormat()), expectedFormat);
1650
1651 // Modify the values and re-check.
1652 format1.setSamples(8);
1653 format1.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1654 format1.setTextureTarget(GL_TEXTURE_3D);
1655 format1.setInternalTextureFormat(GL_RGB16);
1656 QCOMPARE(format1.samples(), 8);
1657 QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
1658 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
1659 QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
1660
1661 // Make copies and check that they are the same.
1662 QGLFramebufferObjectFormat format2(format1);
1663 QGLFramebufferObjectFormat format3;
1664 QCOMPARE(format2.samples(), 8);
1665 QCOMPARE(format2.attachment(), QGLFramebufferObject::CombinedDepthStencil);
1666 QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D));
1667 QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16));
1668 format3 = format1;
1669 QCOMPARE(format3.samples(), 8);
1670 QCOMPARE(format3.attachment(), QGLFramebufferObject::CombinedDepthStencil);
1671 QCOMPARE(int(format3.textureTarget()), int(GL_TEXTURE_3D));
1672 QCOMPARE(int(format3.internalTextureFormat()), int(GL_RGB16));
1673
1674 // Modify the copies and check that the original is unchanged.
1675 format2.setSamples(9);
1676 format3.setTextureTarget(GL_TEXTURE_2D);
1677 QCOMPARE(format1.samples(), 8);
1678 QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
1679 QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
1680 QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
1681
1682 // operator== and operator!= for QGLFramebufferObjectFormat.
1683 QGLFramebufferObjectFormat format1c;
1684 QGLFramebufferObjectFormat format2c;
1685
1686 QCOMPARE(format1c, format2c);
1687 QVERIFY(!(format1c != format2c));
1688 format1c.setSamples(8);
1689 QVERIFY(!(format1c == format2c));
1690 QVERIFY(format1c != format2c);
1691 format2c.setSamples(8);
1692 QCOMPARE(format1c, format2c);
1693 QVERIFY(!(format1c != format2c));
1694
1695 format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1696 QVERIFY(!(format1c == format2c));
1697 QVERIFY(format1c != format2c);
1698 format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1699 QCOMPARE(format1c, format2c);
1700 QVERIFY(!(format1c != format2c));
1701
1702 format1c.setTextureTarget(GL_TEXTURE_3D);
1703 QVERIFY(!(format1c == format2c));
1704 QVERIFY(format1c != format2c);
1705 format2c.setTextureTarget(GL_TEXTURE_3D);
1706 QCOMPARE(format1c, format2c);
1707 QVERIFY(!(format1c != format2c));
1708
1709 format1c.setInternalTextureFormat(GL_RGB16);
1710 QVERIFY(!(format1c == format2c));
1711 QVERIFY(format1c != format2c);
1712 format2c.setInternalTextureFormat(GL_RGB16);
1713 QCOMPARE(format1c, format2c);
1714 QVERIFY(!(format1c != format2c));
1715
1716 QGLFramebufferObjectFormat format3c(format1c);
1717 QGLFramebufferObjectFormat format4c;
1718 QCOMPARE(format1c, format3c);
1719 QVERIFY(!(format1c != format3c));
1720 format3c.setInternalTextureFormat(
1721#ifdef QT_OPENGL_ES_2
1722 GL_RGBA
1723#else
1724 QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
1725#endif
1726 );
1727 QVERIFY(!(format1c == format3c));
1728 QVERIFY(format1c != format3c);
1729
1730 format4c = format1c;
1731 QCOMPARE(format1c, format4c);
1732 QVERIFY(!(format1c != format4c));
1733 format4c.setInternalTextureFormat(
1734#ifdef QT_OPENGL_ES_2
1735 GL_RGBA
1736#else
1737 QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
1738#endif
1739 );
1740 QVERIFY(!(format1c == format4c));
1741 QVERIFY(format1c != format4c);
1742}
1743
1744void tst_QGL::testDontCrashOnDanglingResources()
1745{
1746 // We have a number of Q_GLOBAL_STATICS inside the Qt OpenGL
1747 // module. This test is verify that we don't crash as a result of
1748 // them calling into libgl on application shutdown.
1749 QWidget *widget = new UnclippedWidget();
1750 widget->show();
1751 qApp->processEvents();
1752 widget->hide();
1753}
1754
1755class ReplaceClippingGLWidget : public QGLWidget
1756{
1757public:
1758 void paint(QPainter *painter)
1759 {
1760 painter->fillRect(r: rect(), c: Qt::white);
1761
1762 QPainterPath path;
1763 path.addRect(x: 0, y: 0, w: 100, h: 100);
1764 path.addRect(x: 50, y: 50, w: 100, h: 100);
1765
1766 painter->setClipRect(x: 0, y: 0, w: 150, h: 150);
1767 painter->fillPath(path, brush: Qt::red);
1768
1769 painter->translate(dx: 150, dy: 150);
1770 painter->setClipRect(x: 0, y: 0, w: 150, h: 150);
1771 painter->fillPath(path, brush: Qt::red);
1772 }
1773
1774protected:
1775 void paintEvent(QPaintEvent*)
1776 {
1777 // clear the stencil with junk
1778 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1779 funcs->glStencilMask(mask: 0xFFFF);
1780 funcs->glClearStencil(s: 0xFFFF);
1781 funcs->glDisable(GL_STENCIL_TEST);
1782 funcs->glDisable(GL_SCISSOR_TEST);
1783 funcs->glClear(GL_STENCIL_BUFFER_BIT);
1784
1785 QPainter painter(this);
1786 paint(painter: &painter);
1787 }
1788};
1789
1790void tst_QGL::replaceClipping()
1791{
1792 ReplaceClippingGLWidget glw;
1793 glw.resize(w: 300, h: 300);
1794 glw.show();
1795
1796 QVERIFY(QTest::qWaitForWindowExposed(&glw));
1797
1798 QImage reference(300, 300, QImage::Format_RGB32);
1799 QPainter referencePainter(&reference);
1800 glw.paint(painter: &referencePainter);
1801 referencePainter.end();
1802
1803#if defined(Q_OS_QNX)
1804 // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
1805 // after a buffer swap. This is why we have to swap the buffer explicitly before calling
1806 // grabFrameBuffer to retrieve the content of the front buffer
1807 glw.swapBuffers();
1808#endif
1809 const QImage widgetFB = glw.grabFrameBuffer(withAlpha: false).convertToFormat(f: QImage::Format_RGB32);
1810
1811 // Sample pixels in a grid pattern which avoids false failures due to
1812 // off-by-one pixel errors on some buggy GL implementations
1813 for (int x = 25; x < reference.width(); x += 50) {
1814 for (int y = 25; y < reference.width(); y += 50) {
1815 QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
1816 }
1817 }
1818}
1819
1820class ClipTestGLWidget : public QGLWidget
1821{
1822public:
1823 void paint(QPainter *painter)
1824 {
1825 painter->fillRect(x: -1, y: -1, w: width()+2, h: height()+2, c: Qt::white);
1826 painter->setClipRect(x: 10, y: 10, w: width()-20, h: height()-20);
1827 painter->fillRect(r: rect(), c: Qt::cyan);
1828
1829 painter->save();
1830 painter->setClipRect(x: 10, y: 10, w: 100, h: 100, op: Qt::IntersectClip);
1831
1832 painter->fillRect(r: rect(), c: Qt::blue);
1833
1834 painter->save();
1835 painter->setClipRect(x: 10, y: 10, w: 50, h: 50, op: Qt::IntersectClip);
1836 painter->fillRect(r: rect(), c: Qt::red);
1837 painter->restore();
1838 painter->fillRect(x: 0, y: 0, w: 40, h: 40, c: Qt::white);
1839 painter->save();
1840
1841 painter->setClipRect(x: 0, y: 0, w: 35, h: 35, op: Qt::IntersectClip);
1842 painter->fillRect(r: rect(), c: Qt::black);
1843 painter->restore();
1844
1845 painter->fillRect(x: 0, y: 0, w: 30, h: 30, c: Qt::magenta);
1846
1847 painter->save();
1848 painter->setClipRect(x: 60, y: 10, w: 50, h: 50, op: Qt::ReplaceClip);
1849 painter->fillRect(r: rect(), c: Qt::green);
1850 painter->restore();
1851
1852 painter->restore();
1853
1854 painter->translate(dx: 100, dy: 100);
1855
1856 {
1857 QPainterPath path;
1858 path.addRect(x: 10, y: 10, w: 100, h: 100);
1859 path.addRect(x: 10, y: 10, w: 10, h: 10);
1860 painter->setClipPath(path, op: Qt::IntersectClip);
1861 }
1862
1863 painter->fillRect(r: rect(), c: Qt::blue);
1864
1865 painter->save();
1866 {
1867 QPainterPath path;
1868 path.addRect(x: 10, y: 10, w: 50, h: 50);
1869 path.addRect(x: 10, y: 10, w: 10, h: 10);
1870 painter->setClipPath(path, op: Qt::IntersectClip);
1871 }
1872 painter->fillRect(r: rect(), c: Qt::red);
1873 painter->restore();
1874 painter->fillRect(x: 0, y: 0, w: 40, h: 40, c: Qt::white);
1875 painter->save();
1876
1877 {
1878 QPainterPath path;
1879 path.addRect(x: 0, y: 0, w: 35, h: 35);
1880 path.addRect(x: 10, y: 10, w: 10, h: 10);
1881 painter->setClipPath(path, op: Qt::IntersectClip);
1882 }
1883 painter->fillRect(r: rect(), c: Qt::black);
1884 painter->restore();
1885
1886 painter->fillRect(x: 0, y: 0, w: 30, h: 30, c: Qt::magenta);
1887
1888 painter->save();
1889 {
1890 QPainterPath path;
1891 path.addRect(x: 60, y: 10, w: 50, h: 50);
1892 path.addRect(x: 10, y: 10, w: 10, h: 10);
1893 painter->setClipPath(path, op: Qt::ReplaceClip);
1894 }
1895 painter->fillRect(r: rect(), c: Qt::green);
1896 painter->restore();
1897 }
1898
1899protected:
1900 void paintEvent(QPaintEvent*)
1901 {
1902 QPainter painter(this);
1903 paint(painter: &painter);
1904 }
1905};
1906
1907void tst_QGL::clipTest()
1908{
1909 ClipTestGLWidget glw;
1910 glw.resize(w: 220, h: 220);
1911 glw.showNormal();
1912
1913 QVERIFY(QTest::qWaitForWindowExposed(&glw));
1914
1915 QImage reference(glw.size(), QImage::Format_RGB32);
1916 QPainter referencePainter(&reference);
1917 glw.paint(painter: &referencePainter);
1918 referencePainter.end();
1919
1920#if defined(Q_OS_QNX)
1921 // glReadPixels reads from the back buffer. On QNX the buffer is not preserved
1922 // after a buffer swap. This is why we have to swap the buffer explicitly before calling
1923 // grabFrameBuffer to retrieve the content of the front buffer
1924 glw.swapBuffers();
1925#endif
1926 const QImage widgetFB = glw.grabFrameBuffer(withAlpha: false).convertToFormat(f: QImage::Format_RGB32);
1927
1928 // Sample pixels in a grid pattern which avoids false failures due to
1929 // off-by-one pixel errors on some buggy GL implementations
1930 for (int x = 2; x < reference.width(); x += 5) {
1931 for (int y = 2; y < reference.height(); y += 5) {
1932 QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
1933 }
1934 }
1935}
1936
1937void tst_QGL::destroyFBOAfterContext()
1938{
1939 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
1940 QSKIP("QGLFramebufferObject not supported on this platform");
1941
1942 QGLWidget *glw = new QGLWidget();
1943 glw->makeCurrent();
1944
1945 // No multisample with combined depth/stencil attachment:
1946 QGLFramebufferObjectFormat fboFormat;
1947 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
1948
1949 // Don't complicate things by using NPOT:
1950 QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat);
1951
1952 // The handle should be valid until the context is destroyed.
1953 QVERIFY(fbo->handle() != 0);
1954 QVERIFY(fbo->isValid());
1955
1956 delete glw;
1957
1958 // The handle should now be zero.
1959 QVERIFY(!fbo->handle());
1960 QVERIFY(!fbo->isValid());
1961
1962 delete fbo;
1963}
1964
1965#ifdef QT_BUILD_INTERNAL
1966
1967class tst_QGLResource
1968{
1969public:
1970 tst_QGLResource(const QGLContext * = 0) {}
1971 ~tst_QGLResource() { ++deletions; }
1972
1973 static int deletions;
1974};
1975
1976int tst_QGLResource::deletions = 0;
1977
1978#ifdef TODO
1979Q_GLOBAL_STATIC(QOpenGLContextGroupResource<tst_QGLResource>, qt_shared_test)
1980#endif //TODO
1981#endif // QT_BUILD_INTERNAL
1982
1983#ifdef QT_BUILD_INTERNAL
1984void tst_QGL::shareRegister()
1985{
1986#ifdef TODO
1987 // Create a context.
1988 QGLWidget *glw1 = new QGLWidget();
1989 glw1->makeCurrent();
1990
1991 // Nothing should be sharing with glw1's context yet.
1992 QVERIFY(!glw1->isSharing());
1993
1994 // Create a guard for the first context.
1995 QOpenGLSharedResourceGuard guard(glw1->context()->contextHandle());
1996 QCOMPARE(guard.id(), 0);
1997 guard.setId(3);
1998 QCOMPARE(guard.id(), 3);
1999
2000 // Request a tst_QGLResource object for the first context.
2001 tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()->contextHandle());
2002 QVERIFY(res1);
2003 QCOMPARE(qt_shared_test()->value(glw1->context()->contextHandle()), res1);
2004
2005 // Create another context that shares with the first.
2006 QVERIFY(!glw1->isSharing());
2007 QGLWidget *glw2 = new QGLWidget(0, glw1);
2008 if (!glw2->isSharing()) {
2009 delete glw2;
2010 delete glw1;
2011 QSKIP("Context sharing is not supported");
2012 }
2013 QVERIFY(glw1->isSharing());
2014 QVERIFY(glw1->context() != glw2->context());
2015
2016 // Check that the first context's resource is also on the second.
2017 QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
2018 QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
2019
2020 // Guard should still be the same.
2021 QCOMPARE(guard.context(), glw1->context());
2022 QCOMPARE(guard.id(), 3);
2023
2024 // Check the sharing relationships.
2025 QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
2026 QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
2027 QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
2028 QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
2029 QVERIFY(!QGLContext::areSharing(0, glw2->context()));
2030 QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
2031 QVERIFY(!QGLContext::areSharing(0, 0));
2032
2033 // Create a third context, not sharing with the others.
2034 QGLWidget *glw3 = new QGLWidget();
2035 QVERIFY(!glw3->isSharing());
2036
2037 // Create a guard on the standalone context.
2038 QGLSharedResourceGuard guard3(glw3->context());
2039 guard3.setId(5);
2040
2041 // Request a resource to the third context.
2042 tst_QGLResource *res3 = qt_shared_test()->value(glw3->context());
2043 QVERIFY(res3);
2044 QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
2045 QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
2046 QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
2047
2048 // Check the sharing relationships again.
2049 QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context()));
2050 QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context()));
2051 QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context()));
2052 QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context()));
2053 QVERIFY(!QGLContext::areSharing(glw1->context(), glw3->context()));
2054 QVERIFY(!QGLContext::areSharing(glw2->context(), glw3->context()));
2055 QVERIFY(!QGLContext::areSharing(glw3->context(), glw1->context()));
2056 QVERIFY(!QGLContext::areSharing(glw3->context(), glw2->context()));
2057 QVERIFY(QGLContext::areSharing(glw3->context(), glw3->context()));
2058 QVERIFY(!QGLContext::areSharing(0, glw2->context()));
2059 QVERIFY(!QGLContext::areSharing(glw1->context(), 0));
2060 QVERIFY(!QGLContext::areSharing(0, glw3->context()));
2061 QVERIFY(!QGLContext::areSharing(glw3->context(), 0));
2062 QVERIFY(!QGLContext::areSharing(0, 0));
2063
2064 // Shared guard should still be the same.
2065 QCOMPARE(guard.context(), glw1->context());
2066 QCOMPARE(guard.id(), 3);
2067
2068 // Delete the first context.
2069 delete glw1;
2070
2071 // The second context should no longer register as sharing.
2072 QVERIFY(!glw2->isSharing());
2073
2074 // The first context's resource should transfer to the second context.
2075 QCOMPARE(tst_QGLResource::deletions, 0);
2076 QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
2077 QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
2078
2079 // Shared guard should now be the second context, with the id the same.
2080 QCOMPARE(guard.context(), glw2->context());
2081 QCOMPARE(guard.id(), 3);
2082 QCOMPARE(guard3.context(), glw3->context());
2083 QCOMPARE(guard3.id(), 5);
2084
2085 // Clean up and check that the resources are properly deleted.
2086 delete glw2;
2087 QCOMPARE(tst_QGLResource::deletions, 1);
2088 delete glw3;
2089 QCOMPARE(tst_QGLResource::deletions, 2);
2090
2091 // Guards should now be null and the id zero.
2092 QVERIFY(guard.context() == 0);
2093 QVERIFY(guard.id() == 0);
2094 QVERIFY(guard3.context() == 0);
2095 QVERIFY(guard3.id() == 0);
2096#endif //TODO
2097}
2098#endif
2099
2100// Tests QGLContext::bindTexture with default options
2101#ifdef QT_BUILD_INTERNAL
2102void tst_QGL::qglContextDefaultBindTexture()
2103{
2104 QGLWidget w;
2105 w.makeCurrent();
2106 QGLContext *ctx = const_cast<QGLContext*>(w.context());
2107
2108 QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
2109 boundImage->fill(pixel: 0xFFFFFFFF);
2110 QPixmap *boundPixmap = new QPixmap(256, 256);
2111 boundPixmap->fill(fillColor: Qt::red);
2112
2113 int startCacheItemCount = QGLTextureCache::instance()->size();
2114
2115 GLuint boundImageTextureId = ctx->bindTexture(image: *boundImage);
2116 GLuint boundPixmapTextureId = ctx->bindTexture(pixmap: *boundPixmap);
2117
2118 // Make sure the image & pixmap have been added to the cache:
2119 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2120
2121 // Make sure the image & pixmap have the is_cached flag set:
2122 QVERIFY(QImagePixmapCleanupHooks::isImageCached(*boundImage));
2123 QVERIFY(QImagePixmapCleanupHooks::isPixmapCached(*boundPixmap));
2124
2125 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
2126 // Make sure the texture IDs returned are valid:
2127 QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
2128 QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
2129
2130 // Make sure the textures are still valid after we delete the image/pixmap:
2131 // Also check that although the textures are left intact, the cache entries are removed:
2132 delete boundImage;
2133 boundImage = 0;
2134 QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
2135 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2136 delete boundPixmap;
2137 boundPixmap = 0;
2138 QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
2139 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2140
2141 // Finally, make sure QGLContext::deleteTexture deletes the texture IDs:
2142 ctx->deleteTexture(tx_id: boundImageTextureId);
2143 ctx->deleteTexture(tx_id: boundPixmapTextureId);
2144 QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_FALSE));
2145 QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_FALSE));
2146}
2147#endif
2148
2149#ifdef QT_BUILD_INTERNAL
2150void tst_QGL::textureCleanup()
2151{
2152 QGLWidget w;
2153 w.resize(w: 200,h: 200);
2154 w.show();
2155 QVERIFY(QTest::qWaitForWindowExposed(&w));
2156 w.makeCurrent();
2157
2158 // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
2159 // when the pixmap cache is cleared
2160 {
2161 int startCacheItemCount = QGLTextureCache::instance()->size();
2162 QPainter p(&w);
2163
2164 QPixmap boundPixmap(":designer.png");
2165
2166 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2167
2168 p.drawPixmap(x: 0, y: 0, pm: boundPixmap);
2169 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2170
2171 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2172 p.end();
2173
2174 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2175
2176 // Check that the texture doesn't get removed from the cache when the pixmap is cleared
2177 // as it should still be in the cache:
2178 boundPixmap = QPixmap();
2179 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2180
2181 QPixmapCache::clear();
2182 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2183 }
2184
2185 // Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
2186 // when they are explicitly removed from the pixmap cache
2187 {
2188 int startCacheItemCount = QGLTextureCache::instance()->size();
2189 QPainter p(&w);
2190
2191 QPixmap boundPixmap(128, 128);
2192 QString cacheKey = QString::fromLatin1(str: "myPixmap");
2193 QPixmapCache::insert(key: cacheKey, pixmap: boundPixmap);
2194
2195 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2196
2197 p.drawPixmap(x: 0, y: 0, pm: boundPixmap);
2198 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2199
2200 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2201 p.end();
2202
2203 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2204
2205 // Check that the texture doesn't get removed from the cache when the pixmap is cleared
2206 // as it should still be in the cache:
2207 boundPixmap = QPixmap();
2208 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2209
2210 // Finally, we check that the texture cache entry is removed when we remove the
2211 // pixmap cache entry, which should hold the last reference:
2212 QPixmapCache::remove(key: cacheKey);
2213 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2214 }
2215
2216 // Check images & pixmaps are removed from the cache when they are deleted
2217 {
2218 int startCacheItemCount = QGLTextureCache::instance()->size();
2219 QPainter p(&w);
2220
2221 QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
2222 boundImage->fill(pixel: 0xFFFFFFFF);
2223 QPixmap *boundPixmap = new QPixmap(256, 256);
2224 boundPixmap->fill(fillColor: Qt::red);
2225
2226 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2227
2228 p.drawImage(x: 0, y: 0, image: *boundImage);
2229 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2230
2231 p.drawPixmap(x: 0, y: 0, pm: *boundPixmap);
2232 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2233
2234 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2235 p.end();
2236
2237 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2238
2239 delete boundImage;
2240 boundImage = 0;
2241 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2242
2243 delete boundPixmap;
2244 boundPixmap = 0;
2245 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2246 }
2247
2248 // Check images & pixmaps are removed from the cache when they are assigned to
2249 {
2250 int startCacheItemCount = QGLTextureCache::instance()->size();
2251 QPainter p(&w);
2252
2253 QImage boundImage(256, 256, QImage::Format_RGB32);
2254 boundImage.fill(pixel: 0xFFFFFFFF);
2255 QPixmap boundPixmap(256, 256);
2256 boundPixmap.fill(fillColor: Qt::red);
2257
2258 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2259
2260 p.drawImage(x: 0, y: 0, image: boundImage);
2261 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2262
2263 p.drawPixmap(x: 0, y: 0, pm: boundPixmap);
2264 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2265
2266 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2267 p.end();
2268
2269 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2270
2271 boundImage = QImage(64, 64, QImage::Format_RGB32);
2272 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2273
2274 boundPixmap = QPixmap(64, 64);
2275 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2276 }
2277
2278 // Check images & pixmaps are removed from the cache when they are modified (detached)
2279 {
2280 int startCacheItemCount = QGLTextureCache::instance()->size();
2281 QPainter p(&w);
2282
2283 QImage boundImage(256, 256, QImage::Format_RGB32);
2284 boundImage.fill(pixel: 0xFFFFFFFF);
2285 QPixmap boundPixmap(256, 256);
2286 boundPixmap.fill(fillColor: Qt::red);
2287
2288 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2289
2290 p.drawImage(x: 0, y: 0, image: boundImage);
2291 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2292
2293 p.drawPixmap(x: 0, y: 0, pm: boundPixmap);
2294 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2295
2296 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2297 p.end();
2298
2299 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2300
2301 boundImage.fill(pixel: 0x00000000);
2302 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2303
2304 boundPixmap.fill(fillColor: Qt::blue);
2305 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2306 }
2307
2308 // Check that images/pixmaps aren't removed from the cache if a shallow copy has been made
2309 QImage copyOfImage;
2310 QPixmap copyOfPixmap;
2311 int startCacheItemCount = QGLTextureCache::instance()->size();
2312 {
2313 QPainter p(&w);
2314
2315 QImage boundImage(256, 256, QImage::Format_RGB32);
2316 boundImage.fill(pixel: 0xFFFFFFFF);
2317 QPixmap boundPixmap(256, 256);
2318 boundPixmap.fill(fillColor: Qt::red);
2319
2320 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2321
2322 p.drawImage(x: 0, y: 0, image: boundImage);
2323 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2324
2325 p.drawPixmap(x: 0, y: 0, pm: boundPixmap);
2326 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2327
2328 // Need to call end for the GL2 paint engine to release references to pixmap if using tfp
2329 p.end();
2330
2331 copyOfImage = boundImage;
2332 copyOfPixmap = boundPixmap;
2333 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2334 } // boundImage & boundPixmap would have been deleted when they went out of scope
2335 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
2336
2337 copyOfImage = QImage();
2338 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
2339
2340 copyOfPixmap = QPixmap();
2341 QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
2342}
2343#endif
2344
2345namespace ThreadImages {
2346
2347class Producer : public QObject
2348{
2349 Q_OBJECT
2350public:
2351 Producer()
2352 {
2353 startTimer(interval: 20);
2354
2355 QThread *thread = new QThread;
2356 thread->start();
2357
2358 connect(sender: this, SIGNAL(destroyed()), receiver: thread, SLOT(quit()));
2359
2360 moveToThread(thread);
2361 connect(sender: thread, SIGNAL(finished()), receiver: thread, SLOT(deleteLater()));
2362 }
2363
2364signals:
2365 void imageReady(const QImage &image);
2366
2367protected:
2368 void timerEvent(QTimerEvent *)
2369 {
2370 QImage image(256, 256, QImage::Format_RGB32);
2371 QLinearGradient g(0, 0, 0, 256);
2372 g.setColorAt(pos: 0, color: QColor(255, 180, 180));
2373 g.setColorAt(pos: 1, color: Qt::white);
2374 g.setSpread(QGradient::ReflectSpread);
2375
2376 QBrush brush(g);
2377 brush.setTransform(QTransform::fromTranslate(dx: 0, dy: delta));
2378 delta += 10;
2379
2380 QPainter p(&image);
2381 p.fillRect(image.rect(), brush);
2382
2383 if (images.size() > 10)
2384 images.removeFirst();
2385
2386 images.append(t: image);
2387
2388 emit imageReady(image);
2389 }
2390
2391private:
2392 QList<QImage> images;
2393 int delta;
2394};
2395
2396
2397class DisplayWidget : public QGLWidget
2398{
2399 Q_OBJECT
2400public:
2401 DisplayWidget(QWidget *parent) : QGLWidget(parent) {}
2402 void paintEvent(QPaintEvent *)
2403 {
2404 QPainter p(this);
2405 p.drawImage(r: rect(), image: m_image);
2406 }
2407
2408public slots:
2409 void setImage(const QImage &image)
2410 {
2411 m_image = image;
2412 update();
2413 }
2414
2415private:
2416 QImage m_image;
2417};
2418
2419class Widget : public QWidget
2420{
2421 Q_OBJECT
2422public:
2423 Widget()
2424 : iterations(0)
2425 , display(0)
2426 , producer(new Producer)
2427 {
2428 startTimer(interval: 400);
2429 connect(sender: this, SIGNAL(destroyed()), receiver: producer, SLOT(deleteLater()));
2430 }
2431
2432 int iterations;
2433
2434protected:
2435 void timerEvent(QTimerEvent *)
2436 {
2437 ++iterations;
2438
2439 delete display;
2440 display = new DisplayWidget(this);
2441 connect(sender: producer, SIGNAL(imageReady(QImage)), receiver: display, SLOT(setImage(QImage)));
2442
2443 display->setGeometry(rect());
2444 display->show();
2445 }
2446
2447private:
2448 DisplayWidget *display;
2449 Producer *producer;
2450};
2451
2452}
2453
2454void tst_QGL::threadImages()
2455{
2456 ThreadImages::Widget *widget = new ThreadImages::Widget;
2457 widget->show();
2458
2459 while (widget->iterations <= 5) {
2460 qApp->processEvents();
2461 }
2462
2463 delete widget;
2464}
2465
2466void tst_QGL::nullRectCrash()
2467{
2468 if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
2469 QSKIP("QGLFramebufferObject not supported on this platform");
2470
2471 QGLWidget glw;
2472 glw.makeCurrent();
2473
2474 QGLFramebufferObjectFormat fboFormat;
2475 fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
2476
2477 QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat);
2478
2479 QPainter fboPainter(fbo);
2480
2481 fboPainter.setPen(QPen(QColor(255, 127, 127, 127), 2));
2482 fboPainter.setBrush(QColor(127, 255, 127, 127));
2483 fboPainter.drawRect(rect: QRectF());
2484
2485 fboPainter.end();
2486}
2487
2488void tst_QGL::extensions()
2489{
2490 QGLWidget glw;
2491 glw.makeCurrent();
2492
2493 QOpenGLContext *ctx = QOpenGLContext::currentContext();
2494 QVERIFY(ctx);
2495 QOpenGLFunctions *funcs = ctx->functions();
2496 QVERIFY(funcs);
2497 QSurfaceFormat format = ctx->format();
2498
2499#ifdef QT_BUILD_INTERNAL
2500 QOpenGLExtensions *exts = static_cast<QOpenGLExtensions *>(funcs);
2501 QOpenGLExtensions::OpenGLExtensions allExts = exts->openGLExtensions();
2502 // Mipmapping is always available in GL2/GLES2+. Verify this.
2503 if (format.majorVersion() >= 2)
2504 QVERIFY(allExts.testFlag(QOpenGLExtensions::GenerateMipmap));
2505#endif
2506
2507 // Now look for some features should always be available in a given version.
2508 QOpenGLFunctions::OpenGLFeatures allFeatures = funcs->openGLFeatures();
2509 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multitexture));
2510 if (format.majorVersion() >= 2) {
2511 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Shaders));
2512 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Buffers));
2513 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multisample));
2514 QVERIFY(!ctx->isOpenGLES() || allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
2515 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::NPOTTextures)
2516 && allFeatures.testFlag(QOpenGLFunctions::NPOTTextureRepeat));
2517 if (ctx->isOpenGLES()) {
2518 QVERIFY(!allFeatures.testFlag(QOpenGLFunctions::FixedFunctionPipeline));
2519 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
2520 }
2521 }
2522 if (format.majorVersion() >= 3)
2523 QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
2524}
2525
2526class TestPaintWidget : public QGLWidget
2527{
2528public:
2529 TestPaintWidget(QWidget *parent = nullptr) : QGLWidget(parent) { }
2530 void paintEvent(QPaintEvent *) override
2531 {
2532 QPainter painter(this);
2533 painter.setPen(Qt::red);
2534 painter.setBrush(Qt::blue);
2535 painter.drawRect(x: 0, y: 0, w: width(), h: height());
2536 }
2537};
2538
2539void tst_QGL::closeAndThenShow()
2540{
2541#ifdef Q_OS_MACOS
2542 if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSBigSur)
2543 QSKIP("Test is crashing flaky on macos-11.0 - QTBUG-96271");
2544#endif
2545 QWidget *w = new QWidget;
2546 w->resize(w: 640, h: 480);
2547 QVBoxLayout *layout = new QVBoxLayout(w);
2548 QGLWidget *glw = new TestPaintWidget;
2549 layout->addWidget(glw);
2550
2551 w->show();
2552 QVERIFY(QTest::qWaitForWindowExposed(w));
2553 QCoreApplication::processEvents();
2554
2555 // QWidget::close() does not exhibit the problems from
2556 // QTBUG-86582, since that's not the same as closing a QWindow.
2557 // Rather, close the QWindow (i.e. simulate what pressing the
2558 // close button interactively would do).
2559 w->windowHandle()->close();
2560
2561 w->show();
2562 QVERIFY(QTest::qWaitForWindowExposed(w));
2563 QCoreApplication::processEvents();
2564
2565 delete w;
2566}
2567
2568QTEST_MAIN(tst_QGL)
2569#include "tst_qgl.moc"
2570

source code of qtbase/tests/auto/opengl/qgl/tst_qgl.cpp