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 <QtGui/private/qopenglcontext_p.h>
31#include <QtGui/QOpenGLFramebufferObject>
32#include <QtGui/QOpenGLFunctions>
33#include <QtGui/QOpenGLFunctions_4_2_Core>
34#include <QtGui/QOpenGLVertexArrayObject>
35#include <QtGui/QOpenGLBuffer>
36#include <QtGui/QOpenGLPaintDevice>
37#include <QtGui/QOpenGLTexture>
38#include <QtGui/QPainter>
39#include <QtGui/QPainterPath>
40#include <QtGui/QScreen>
41#include <QtGui/QWindow>
42#include <QtGui/QOffscreenSurface>
43#include <QtGui/QGenericMatrix>
44#include <QtGui/QMatrix4x4>
45#include <QtGui/qopengltextureblitter.h>
46#include <QtGui/private/qguiapplication_p.h>
47#include <QtGui/private/qopenglextensions_p.h>
48#include <qpa/qplatformintegration.h>
49#include <qpa/qplatformnativeinterface.h>
50
51#include <QtTest/QtTest>
52
53#include <QSignalSpy>
54
55#ifdef USE_GLX
56// Must be included last due to the X11 types
57#include <QtPlatformHeaders/QGLXNativeContext>
58#endif
59
60#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2)
61#include <QtPlatformHeaders/QWGLNativeContext>
62#endif
63
64Q_DECLARE_METATYPE(QImage::Format)
65
66class tst_QOpenGL : public QObject
67{
68Q_OBJECT
69
70private slots:
71 void initTestCase();
72 void sharedResourceCleanup_data();
73 void sharedResourceCleanup();
74 void multiGroupSharedResourceCleanup_data();
75 void multiGroupSharedResourceCleanup();
76 void multiGroupSharedResourceCleanupCustom_data();
77 void multiGroupSharedResourceCleanupCustom();
78 void fboSimpleRendering_data();
79 void fboSimpleRendering();
80 void fboTextureOwnership_data();
81 void fboTextureOwnership();
82 void fboRendering_data();
83 void fboRendering();
84 void fboRenderingRGB30_data();
85 void fboRenderingRGB30();
86 void fboRenderingRGB64_data();
87 void fboRenderingRGB64();
88 void fboHandleNulledAfterContextDestroyed();
89 void fboMRT();
90 void fboMRT_differentFormats();
91 void openGLPaintDevice_data();
92 void openGLPaintDevice();
93 void openGLPaintDeviceWithChangingContext();
94 void aboutToBeDestroyed();
95 void sizeLessWindow();
96 void QTBUG15621_triangulatingStrokerDivZero();
97 void textureblitterFullSourceRectTransform();
98 void textureblitterPartOriginBottomLeftSourceRectTransform();
99 void textureblitterPartOriginTopLeftSourceRectTransform();
100 void textureblitterFullTargetRectTransform();
101 void textureblitterPartTargetRectTransform();
102 void defaultSurfaceFormat();
103 void imageFormatPainting();
104 void nullTextureInitializtion();
105 void clipRect();
106
107#ifdef USE_GLX
108 void glxContextWrap();
109#endif
110
111#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2)
112 void wglContextWrap();
113#endif
114
115 void vaoCreate();
116 void bufferCreate();
117 void bufferMapRange();
118 void defaultQGLCurrentBuffer();
119};
120
121struct SharedResourceTracker
122{
123 SharedResourceTracker()
124 {
125 reset();
126 }
127
128 void reset()
129 {
130 invalidateResourceCalls = 0;
131 freeResourceCalls = 0;
132 destructorCalls = 0;
133 }
134
135 int invalidateResourceCalls;
136 int freeResourceCalls;
137 int destructorCalls;
138};
139
140struct SharedResource : public QOpenGLSharedResource
141{
142 SharedResource(SharedResourceTracker *t)
143 : QOpenGLSharedResource(QOpenGLContextGroup::currentContextGroup())
144 , resource(1)
145 , tracker(t)
146 {
147 }
148
149 SharedResource(QOpenGLContext *ctx)
150 : QOpenGLSharedResource(ctx->shareGroup())
151 , resource(1)
152 , tracker(0)
153 {
154 }
155
156 ~SharedResource()
157 {
158 if (tracker)
159 tracker->destructorCalls++;
160 }
161
162 void invalidateResource()
163 {
164 resource = 0;
165 if (tracker)
166 tracker->invalidateResourceCalls++;
167 }
168
169 void freeResource(QOpenGLContext *context)
170 {
171 Q_ASSERT(context == QOpenGLContext::currentContext());
172 Q_UNUSED(context)
173 resource = 0;
174 if (tracker)
175 tracker->freeResourceCalls++;
176 }
177
178 int resource;
179 SharedResourceTracker *tracker;
180};
181
182static QSurface *createSurface(int surfaceClass)
183{
184 if (surfaceClass == int(QSurface::Window)) {
185 QWindow *window = new QWindow;
186 window->setSurfaceType(QWindow::OpenGLSurface);
187 window->setGeometry(posx: 0, posy: 0, w: 10, h: 10);
188 window->create();
189 return window;
190 } else if (surfaceClass == int(QSurface::Offscreen)) {
191 // Create a window and get the format from that. For example, if an EGL
192 // implementation provides 565 and 888 configs for PBUFFER_BIT but only
193 // 888 for WINDOW_BIT, we may end up with a pbuffer surface that is
194 // incompatible with the context since it could choose the 565 while the
195 // window and the context uses a config with 888.
196 static QSurfaceFormat format;
197 if (format.redBufferSize() == -1) {
198 QWindow *window = new QWindow;
199 window->setSurfaceType(QWindow::OpenGLSurface);
200 window->setGeometry(posx: 0, posy: 0, w: 10, h: 10);
201 window->create();
202 format = window->format();
203 delete window;
204 }
205 QOffscreenSurface *offscreenSurface = new QOffscreenSurface;
206 offscreenSurface->setFormat(format);
207 offscreenSurface->create();
208 return offscreenSurface;
209 }
210 return 0;
211}
212
213void tst_QOpenGL::initTestCase()
214{
215 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(cap: QPlatformIntegration::OpenGL))
216 QSKIP("OpenGL is not supported on this platform.");
217}
218
219static void common_data()
220{
221 QTest::addColumn<int>(name: "surfaceClass");
222
223 QTest::newRow(dataTag: "Using QWindow") << int(QSurface::Window);
224 QTest::newRow(dataTag: "Using QOffscreenSurface") << int(QSurface::Offscreen);
225}
226
227void tst_QOpenGL::sharedResourceCleanup_data()
228{
229 common_data();
230}
231
232void tst_QOpenGL::sharedResourceCleanup()
233{
234 QFETCH(int, surfaceClass);
235 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
236
237 QOpenGLContext *ctx = new QOpenGLContext;
238 QVERIFY(ctx->create());
239 QVERIFY(ctx->makeCurrent(surface.data()));
240
241 SharedResourceTracker tracker;
242 SharedResource *resource = new SharedResource(&tracker);
243 resource->free();
244
245 QCOMPARE(tracker.invalidateResourceCalls, 0);
246 QCOMPARE(tracker.freeResourceCalls, 1);
247 QCOMPARE(tracker.destructorCalls, 1);
248
249 tracker.reset();
250
251 resource = new SharedResource(&tracker);
252
253 QOpenGLContext *ctx2 = new QOpenGLContext;
254 ctx2->setShareContext(ctx);
255 QVERIFY(ctx2->create());
256
257 delete ctx;
258
259 resource->free();
260
261 // no current context, freeResource() delayed
262 QCOMPARE(tracker.invalidateResourceCalls, 0);
263 QCOMPARE(tracker.freeResourceCalls, 0);
264 QCOMPARE(tracker.destructorCalls, 0);
265
266 ctx2->makeCurrent(surface: surface.data());
267
268 // freeResource() should now have been called
269 QCOMPARE(tracker.invalidateResourceCalls, 0);
270 QCOMPARE(tracker.freeResourceCalls, 1);
271 QCOMPARE(tracker.destructorCalls, 1);
272
273 tracker.reset();
274
275 resource = new SharedResource(&tracker);
276
277 // this should cause invalidateResource() to be called
278 delete ctx2;
279
280 QCOMPARE(tracker.invalidateResourceCalls, 1);
281 QCOMPARE(tracker.freeResourceCalls, 0);
282 QCOMPARE(tracker.destructorCalls, 0);
283
284 // should have no effect other than destroying the resource,
285 // as it has already been invalidated
286 resource->free();
287
288 QCOMPARE(tracker.invalidateResourceCalls, 1);
289 QCOMPARE(tracker.freeResourceCalls, 0);
290 QCOMPARE(tracker.destructorCalls, 1);
291}
292
293void tst_QOpenGL::multiGroupSharedResourceCleanup_data()
294{
295 common_data();
296}
297
298void tst_QOpenGL::multiGroupSharedResourceCleanup()
299{
300 QFETCH(int, surfaceClass);
301 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
302
303 for (int i = 0; i < 10; ++i) {
304 QOpenGLContext *gl = new QOpenGLContext();
305 QVERIFY(gl->create());
306 gl->makeCurrent(surface: surface.data());
307 {
308 // Cause QOpenGLMultiGroupSharedResource instantiation.
309 QOpenGLFunctions func(gl);
310 }
311 delete gl;
312 // Cause context group's deleteLater() to be processed.
313 QCoreApplication::sendPostedEvents(receiver: 0, event_type: QEvent::DeferredDelete);
314 }
315 // Shouldn't crash when application exits.
316}
317
318void tst_QOpenGL::multiGroupSharedResourceCleanupCustom_data()
319{
320 common_data();
321}
322
323void tst_QOpenGL::multiGroupSharedResourceCleanupCustom()
324{
325 QFETCH(int, surfaceClass);
326 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
327
328 QOpenGLContext *ctx = new QOpenGLContext();
329 QVERIFY(ctx->create());
330 QVERIFY(ctx->makeCurrent(surface.data()));
331
332 QOpenGLMultiGroupSharedResource multiGroupSharedResource;
333 SharedResource *resource = multiGroupSharedResource.value<SharedResource>(context: ctx);
334 SharedResourceTracker tracker;
335 resource->tracker = &tracker;
336
337 delete ctx;
338
339 QCOMPARE(tracker.invalidateResourceCalls, 1);
340 QCOMPARE(tracker.freeResourceCalls, 0);
341 QCOMPARE(tracker.destructorCalls, 1);
342}
343
344static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1)
345{
346 static int maxFuzz = 1;
347 static bool maxFuzzSet = false;
348
349 // On 16 bpp systems, we need to allow for more fuzz:
350 if (!maxFuzzSet) {
351 maxFuzzSet = true;
352 if (QGuiApplication::primaryScreen()->depth() < 24)
353 maxFuzz = 32;
354 }
355
356 int redFuzz = qAbs(t: qRed(rgb: testPixel) - qRed(rgb: refPixel));
357 int greenFuzz = qAbs(t: qGreen(rgb: testPixel) - qGreen(rgb: refPixel));
358 int blueFuzz = qAbs(t: qBlue(rgb: testPixel) - qBlue(rgb: refPixel));
359 int alphaFuzz = qAbs(t: qAlpha(rgb: testPixel) - qAlpha(rgb: refPixel));
360
361 if (refPixel != 0 && testPixel == 0) {
362 QString msg;
363 if (x >= 0) {
364 msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)")
365 .arg(a: x).arg(a: y)
366 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
367 } else {
368 msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)")
369 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
370 }
371
372 QTest::qFail(statementStr: msg.toLatin1(), file, line);
373 return false;
374 }
375
376 if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) {
377 QString msg;
378
379 if (x >= 0)
380 msg = QString("Pixel [%1,%2]: ").arg(a: x).arg(a: y);
381 else
382 msg = QString("Pixel ");
383
384 msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)")
385 .arg(a: maxFuzz)
386 .arg(a: qRed(rgb: testPixel)).arg(a: qGreen(rgb: testPixel)).arg(a: qBlue(rgb: testPixel)).arg(a: qAlpha(rgb: testPixel))
387 .arg(a: qRed(rgb: refPixel)).arg(a: qGreen(rgb: refPixel)).arg(a: qBlue(rgb: refPixel)).arg(a: qAlpha(rgb: refPixel));
388 QTest::qFail(statementStr: msg.toLatin1(), file, line);
389 return false;
390 }
391 return true;
392}
393
394static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line)
395{
396 QCOMPARE(testImage.devicePixelRatio(), referenceImage.devicePixelRatio());
397 QCOMPARE(testImage.width(), referenceImage.width());
398 QCOMPARE(testImage.height(), referenceImage.height());
399
400 for (int y = 0; y < testImage.height(); y++) {
401 for (int x = 0; x < testImage.width(); x++) {
402 if (!fuzzyComparePixels(testPixel: testImage.pixel(x, y), refPixel: referenceImage.pixel(x, y), file, line, x, y)) {
403 // Might as well save the images for easier debugging:
404 referenceImage.save(fileName: "referenceImage.png");
405 testImage.save(fileName: "testImage.png");
406 return;
407 }
408 }
409 }
410}
411
412#define QFUZZY_COMPARE_IMAGES(A,B) \
413 fuzzyCompareImages(A, B, __FILE__, __LINE__)
414
415#define QFUZZY_COMPARE_PIXELS(A,B) \
416 fuzzyComparePixels(A, B, __FILE__, __LINE__)
417
418void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height)
419{
420 QPainterPath intersectingPath;
421 intersectingPath.moveTo(x: 0, y: 0);
422 intersectingPath.lineTo(x: 100, y: 0);
423 intersectingPath.lineTo(x: 0, y: 100);
424 intersectingPath.lineTo(x: 100, y: 100);
425 intersectingPath.closeSubpath();
426
427 QPainterPath trianglePath;
428 trianglePath.moveTo(x: 50, y: 0);
429 trianglePath.lineTo(x: 100, y: 100);
430 trianglePath.lineTo(x: 0, y: 100);
431 trianglePath.closeSubpath();
432
433 painter->setTransform(transform: QTransform()); // reset xform
434 painter->fillRect(x: -1, y: -1, w: width+2, h: height+2, c: Qt::red); // Background
435 painter->translate(dx: 14, dy: 14);
436 painter->fillPath(path: intersectingPath, brush: Qt::blue); // Test stencil buffer works
437 painter->translate(dx: 128, dy: 0);
438 painter->setClipPath(path: trianglePath); // Test depth buffer works
439 painter->setTransform(transform: QTransform()); // reset xform ready for fill
440 painter->fillRect(x: -1, y: -1, w: width+2, h: height+2, c: Qt::green);
441}
442
443void qt_opengl_check_test_pattern(const QImage& img)
444{
445 // As we're doing more than trivial painting, we can't just compare to
446 // an image rendered with raster. Instead, we sample at well-defined
447 // test-points:
448 QVERIFY(!img.isNull());
449 QVERIFY2(img.width() > 217, QByteArray::number(img.width()));
450 QVERIFY2(img.height() > 90, QByteArray::number(img.height()));
451
452 QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb());
453 QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb());
454 QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb());
455 QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb());
456
457 QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb());
458 QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb());
459 QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb());
460}
461
462void tst_QOpenGL::fboSimpleRendering_data()
463{
464 common_data();
465}
466
467void tst_QOpenGL::fboSimpleRendering()
468{
469 QFETCH(int, surfaceClass);
470 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
471
472 QOpenGLContext ctx;
473 QVERIFY(ctx.create());
474
475 QVERIFY(ctx.makeCurrent(surface.data()));
476
477 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
478 QSKIP("QOpenGLFramebufferObject not supported on this platform");
479
480 // No multisample with combined depth/stencil attachment:
481 QOpenGLFramebufferObjectFormat fboFormat;
482 fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment);
483
484 const QSize size(200, 100);
485 QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(size, fboFormat));
486
487 QVERIFY(fbo->bind());
488
489 ctx.functions()->glClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0);
490 ctx.functions()->glClear(GL_COLOR_BUFFER_BIT);
491 ctx.functions()->glFinish();
492
493 const QImage fb = fbo->toImage().convertToFormat(f: QImage::Format_RGB32);
494 QCOMPARE(fb.size(), size);
495 QImage reference(size, QImage::Format_RGB32);
496 reference.fill(pixel: 0xffff0000);
497
498 QFUZZY_COMPARE_IMAGES(fb, reference);
499}
500
501void tst_QOpenGL::fboTextureOwnership_data()
502{
503 common_data();
504}
505
506void tst_QOpenGL::fboTextureOwnership()
507{
508 QFETCH(int, surfaceClass);
509 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
510
511 QOpenGLContext ctx;
512 QVERIFY(ctx.create());
513
514 ctx.makeCurrent(surface: surface.data());
515
516 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
517 QSKIP("QOpenGLFramebufferObject not supported on this platform");
518
519 QOpenGLFramebufferObjectFormat fboFormat;
520 fboFormat.setAttachment(QOpenGLFramebufferObject::NoAttachment);
521
522 QOpenGLFramebufferObject *fbo = new QOpenGLFramebufferObject(200, 100, fboFormat);
523 QVERIFY(fbo->texture() != 0);
524 fbo->bind();
525
526 // pull out the texture
527 GLuint texture = fbo->takeTexture();
528 QVERIFY(texture != 0);
529 QCOMPARE(fbo->texture(), GLuint(0));
530
531 // verify that the next bind() creates a new texture
532 fbo->bind();
533 QVERIFY(fbo->texture() != 0 && fbo->texture() != texture);
534
535 ctx.functions()->glClearColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0);
536 ctx.functions()->glClear(GL_COLOR_BUFFER_BIT);
537 ctx.functions()->glFinish();
538
539 QImage fb = fbo->toImage().convertToFormat(f: QImage::Format_RGB32);
540 QImage reference(fb.size(), QImage::Format_RGB32);
541 reference.fill(pixel: 0xffff0000);
542
543 QFUZZY_COMPARE_IMAGES(fb, reference);
544
545 ctx.functions()->glDeleteTextures(n: 1, textures: &texture);
546 delete fbo;
547}
548
549void tst_QOpenGL::fboRendering_data()
550{
551 common_data();
552}
553
554// NOTE: This tests that CombinedDepthStencil attachment works by assuming the
555// GL2 engine is being used and is implemented the same way as it was when
556// this autotest was written. If this is not the case, there may be some
557// false-positives: I.e. The test passes when either the depth or stencil
558// buffer is actually missing. But that's probably ok anyway.
559void tst_QOpenGL::fboRendering()
560{
561#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
562 QSKIP("QTBUG-22617");
563#endif
564
565 QFETCH(int, surfaceClass);
566 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
567
568 QOpenGLContext ctx;
569 QVERIFY(ctx.create());
570
571 QVERIFY(ctx.makeCurrent(surface.data()));
572
573 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
574 QSKIP("QOpenGLFramebufferObject not supported on this platform");
575
576 // No multisample with combined depth/stencil attachment:
577 QOpenGLFramebufferObjectFormat fboFormat;
578 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
579
580 // Uncomplicate things by using POT:
581 const QSize size(256, 128);
582 QOpenGLFramebufferObject fbo(size, fboFormat);
583
584 if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil)
585 QSKIP("FBOs missing combined depth~stencil support");
586
587 QVERIFY(fbo.bind());
588
589 QPainter fboPainter;
590 QOpenGLPaintDevice device(fbo.width(), fbo.height());
591 bool painterBegun = fboPainter.begin(&device);
592 QVERIFY(painterBegun);
593
594 qt_opengl_draw_test_pattern(painter: &fboPainter, width: fbo.width(), height: fbo.height());
595
596 fboPainter.end();
597
598 const QImage fb = fbo.toImage().convertToFormat(f: QImage::Format_RGB32);
599 QCOMPARE(fb.size(), size);
600
601 qt_opengl_check_test_pattern(img: fb);
602}
603
604void tst_QOpenGL::fboRenderingRGB30_data()
605{
606 common_data();
607}
608
609#ifndef GL_RGB5_A1
610#define GL_RGB5_A1 0x8057
611#endif
612
613#ifndef GL_RGBA8
614#define GL_RGBA8 0x8058
615#endif
616
617#ifndef GL_RGB10_A2
618#define GL_RGB10_A2 0x8059
619#endif
620
621#ifndef GL_RGBA16
622#define GL_RGBA16 0x805B
623#endif
624
625#ifndef GL_FRAMEBUFFER_RENDERABLE
626#define GL_FRAMEBUFFER_RENDERABLE 0x8289
627#endif
628
629#ifndef GL_FULL_SUPPORT
630#define GL_FULL_SUPPORT 0x82B7
631#endif
632
633static bool supportsInternalFboFormat(QOpenGLContext *ctx, int glFormat)
634{
635 if (ctx->format().majorVersion() < 3)
636 return false;
637#ifndef QT_OPENGL_ES_2
638 if (!ctx->isOpenGLES() && ctx->format().majorVersion() >= 4) {
639 GLint value = -1;
640 QOpenGLFunctions_4_2_Core* vFuncs = ctx->versionFunctions<QOpenGLFunctions_4_2_Core>();
641 if (vFuncs && vFuncs->initializeOpenGLFunctions()) {
642 vFuncs->glGetInternalformativ(GL_TEXTURE_2D, internalformat: glFormat, GL_FRAMEBUFFER_RENDERABLE, bufSize: 1, params: &value);
643 if (value != GL_FULL_SUPPORT)
644 return false;
645 }
646 }
647#else
648 Q_UNUSED(glFormat);
649#endif
650 return true;
651}
652
653void tst_QOpenGL::fboRenderingRGB30()
654{
655#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
656 QSKIP("QTBUG-22617");
657#endif
658
659 QFETCH(int, surfaceClass);
660 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
661
662 QOpenGLContext ctx;
663 QVERIFY(ctx.create());
664
665 QVERIFY(ctx.makeCurrent(surface.data()));
666
667 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
668 QSKIP("QOpenGLFramebufferObject not supported on this platform");
669
670 if (!supportsInternalFboFormat(ctx: &ctx, GL_RGB10_A2))
671 QSKIP("An internal RGB30_A2 format is not guaranteed on this platform");
672
673 // No multisample with combined depth/stencil attachment:
674 QOpenGLFramebufferObjectFormat fboFormat;
675 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
676 fboFormat.setInternalTextureFormat(GL_RGB10_A2);
677
678 // Uncomplicate things by using POT:
679 const QSize size(256, 128);
680 QOpenGLFramebufferObject fbo(size, fboFormat);
681
682 if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil)
683 QSKIP("FBOs missing combined depth~stencil support");
684
685 QVERIFY(fbo.bind());
686
687 QPainter fboPainter;
688 QOpenGLPaintDevice device(fbo.width(), fbo.height());
689 bool painterBegun = fboPainter.begin(&device);
690 QVERIFY(painterBegun);
691
692 qt_opengl_draw_test_pattern(painter: &fboPainter, width: fbo.width(), height: fbo.height());
693
694 fboPainter.end();
695
696 QImage fb = fbo.toImage();
697 QCOMPARE(fb.format(), QImage::Format_A2BGR30_Premultiplied);
698 QCOMPARE(fb.size(), size);
699
700 qt_opengl_check_test_pattern(img: fb);
701
702 // Check rendering can handle color values below 1/256.
703 fboPainter.begin(&device);
704 fboPainter.fillRect(QRect(0, 0, 256, 128), color: QColor::fromRgbF(r: 1.0/512, g: 1.0/512, b: 1.0/512));
705 fboPainter.end();
706 fb = fbo.toImage();
707 uint pixel = ((uint*)fb.bits())[0];
708 QVERIFY((pixel & 0x3f) > 0);
709 QVERIFY(((pixel >> 10) & 0x3f) > 0);
710 QVERIFY(((pixel >> 20) & 0x3f) > 0);
711
712 pixel = (3U << 30) | (2U << 20) | (2U << 10) | 2U;
713 fb.fill(pixel);
714
715 fboPainter.begin(&device);
716 fboPainter.setCompositionMode(QPainter::CompositionMode_Source);
717 fboPainter.drawImage(x: 0, y: 0, image: fb);
718 fboPainter.end();
719 fb = fbo.toImage();
720 pixel = ((uint*)fb.bits())[0];
721 QVERIFY((pixel & 0x3f) > 0);
722 QVERIFY(((pixel >> 10) & 0x3f) > 0);
723 QVERIFY(((pixel >> 20) & 0x3f) > 0);
724}
725
726void tst_QOpenGL::fboRenderingRGB64_data()
727{
728 common_data();
729}
730
731void tst_QOpenGL::fboRenderingRGB64()
732{
733#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
734 QSKIP("QTBUG-22617");
735#endif
736
737 QFETCH(int, surfaceClass);
738 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
739
740 QOpenGLContext ctx;
741 QVERIFY(ctx.create());
742
743 QVERIFY(ctx.makeCurrent(surface.data()));
744
745 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
746 QSKIP("QOpenGLFramebufferObject not supported on this platform");
747
748 if (!supportsInternalFboFormat(ctx: &ctx, GL_RGBA16))
749 QSKIP("An internal RGBA16 format is not guaranteed on this platform");
750
751 // No multisample with combined depth/stencil attachment:
752 QOpenGLFramebufferObjectFormat fboFormat;
753 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
754 fboFormat.setInternalTextureFormat(GL_RGBA16);
755
756 // Uncomplicate things by using POT:
757 const QSize size(256, 128);
758 QOpenGLFramebufferObject fbo(size, fboFormat);
759
760 if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil)
761 QSKIP("FBOs missing combined depth~stencil support");
762
763 QVERIFY(fbo.bind());
764
765 QPainter fboPainter;
766 QOpenGLPaintDevice device(fbo.width(), fbo.height());
767 bool painterBegun = fboPainter.begin(&device);
768 QVERIFY(painterBegun);
769
770 qt_opengl_draw_test_pattern(painter: &fboPainter, width: fbo.width(), height: fbo.height());
771
772 fboPainter.end();
773
774 QImage fb = fbo.toImage();
775 QCOMPARE(fb.format(), QImage::Format_RGBA64_Premultiplied);
776 QCOMPARE(fb.size(), size);
777
778 qt_opengl_check_test_pattern(img: fb);
779
780 // Check rendering can handle precise 16 bit color values.
781 fboPainter.begin(&device);
782 fboPainter.fillRect(QRect(0, 0, 256, 128), color: QColor::fromRgba64(r: 5, g: 1002, b: 8001, a: 65535));
783 fboPainter.end();
784 fb = fbo.toImage();
785 QRgba64 pixel = ((QRgba64*)fb.bits())[0];
786 QCOMPARE(pixel.red(), 5);
787 QCOMPARE(pixel.green(), 1002);
788 QCOMPARE(pixel.blue(), 8001);
789}
790
791void tst_QOpenGL::fboHandleNulledAfterContextDestroyed()
792{
793 QWindow window;
794 window.setSurfaceType(QWindow::OpenGLSurface);
795 window.setGeometry(posx: 0, posy: 0, w: 10, h: 10);
796 window.create();
797
798 QOpenGLFramebufferObject *fbo = 0;
799
800 {
801 QOpenGLContext ctx;
802 QVERIFY(ctx.create());
803
804 ctx.makeCurrent(surface: &window);
805
806 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
807 QSKIP("QOpenGLFramebufferObject not supported on this platform");
808
809 fbo = new QOpenGLFramebufferObject(128, 128);
810
811 QVERIFY(fbo->handle() != 0);
812 }
813
814 QCOMPARE(fbo->handle(), 0U);
815}
816
817void tst_QOpenGL::fboMRT()
818{
819 QWindow window;
820 window.setSurfaceType(QWindow::OpenGLSurface);
821 window.setGeometry(posx: 0, posy: 0, w: 10, h: 10);
822 window.create();
823
824 QOpenGLContext ctx;
825 QVERIFY(ctx.create());
826 ctx.makeCurrent(surface: &window);
827
828 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
829 QSKIP("QOpenGLFramebufferObject not supported on this platform");
830
831 if (!ctx.functions()->hasOpenGLFeature(feature: QOpenGLFunctions::MultipleRenderTargets))
832 QSKIP("Multiple render targets not supported on this platform");
833
834 QOpenGLExtraFunctions *ef = ctx.extraFunctions();
835
836 {
837 // 3 color attachments, different sizes, same internal format, no depth/stencil.
838 QVector<QSize> sizes;
839 sizes << QSize(128, 128) << QSize(192, 128) << QSize(432, 123);
840 QOpenGLFramebufferObject fbo(sizes[0]);
841 fbo.addColorAttachment(size: sizes[1]);
842 fbo.addColorAttachment(size: sizes[2]);
843 QVERIFY(fbo.bind());
844 QCOMPARE(fbo.attachment(), QOpenGLFramebufferObject::NoAttachment);
845 QCOMPARE(sizes, fbo.sizes());
846 QCOMPARE(sizes[0], fbo.size());
847 // Clear the three buffers to red, green and blue.
848 GLenum drawBuf = GL_COLOR_ATTACHMENT0;
849 ef->glDrawBuffers(n: 1, bufs: &drawBuf);
850 ef->glClearColor(red: 1, green: 0, blue: 0, alpha: 1);
851 ef->glClear(GL_COLOR_BUFFER_BIT);
852 drawBuf = GL_COLOR_ATTACHMENT0 + 1;
853 ef->glDrawBuffers(n: 1, bufs: &drawBuf);
854 ef->glClearColor(red: 0, green: 1, blue: 0, alpha: 1);
855 ef->glClear(GL_COLOR_BUFFER_BIT);
856 drawBuf = GL_COLOR_ATTACHMENT0 + 2;
857 ef->glDrawBuffers(n: 1, bufs: &drawBuf);
858 ef->glClearColor(red: 0, green: 0, blue: 1, alpha: 1);
859 ef->glClear(GL_COLOR_BUFFER_BIT);
860 // Verify, keeping in mind that only a 128x123 area is touched in the buffers.
861 // Some drivers do not get this right, unfortunately, so do not rely on it.
862 const char *vendor = (const char *) ef->glGetString(GL_VENDOR);
863 bool hasCorrectMRT = false;
864 if (vendor && strstr(haystack: vendor, needle: "NVIDIA")) // maybe others too
865 hasCorrectMRT = true;
866 QImage img = fbo.toImage(flipped: false, colorAttachmentIndex: 0);
867 QCOMPARE(img.size(), sizes[0]);
868 QCOMPARE(img.pixel(0, 0), qRgb(255, 0, 0));
869 if (hasCorrectMRT)
870 QCOMPARE(img.pixel(127, 122), qRgb(255, 0, 0));
871 img = fbo.toImage(flipped: false, colorAttachmentIndex: 1);
872 QCOMPARE(img.size(), sizes[1]);
873 QCOMPARE(img.pixel(0, 0), qRgb(0, 255, 0));
874 if (hasCorrectMRT)
875 QCOMPARE(img.pixel(127, 122), qRgb(0, 255, 0));
876 img = fbo.toImage(flipped: false, colorAttachmentIndex: 2);
877 QCOMPARE(img.size(), sizes[2]);
878 QCOMPARE(img.pixel(0, 0), qRgb(0, 0, 255));
879 if (hasCorrectMRT)
880 QCOMPARE(img.pixel(127, 122), qRgb(0, 0, 255));
881 fbo.release();
882 }
883
884 {
885 // 2 color attachments, same size, same internal format, depth/stencil.
886 QVector<QSize> sizes;
887 sizes.fill(t: QSize(128, 128), size: 2);
888 QOpenGLFramebufferObject fbo(sizes[0], QOpenGLFramebufferObject::CombinedDepthStencil);
889 fbo.addColorAttachment(size: sizes[1]);
890 QVERIFY(fbo.bind());
891 QCOMPARE(fbo.attachment(), QOpenGLFramebufferObject::CombinedDepthStencil);
892 QCOMPARE(sizes, fbo.sizes());
893 QCOMPARE(sizes[0], fbo.size());
894 ef->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
895 ef->glFinish();
896 fbo.release();
897 }
898}
899
900void tst_QOpenGL::fboMRT_differentFormats()
901{
902 QWindow window;
903 window.setSurfaceType(QWindow::OpenGLSurface);
904 window.setGeometry(posx: 0, posy: 0, w: 10, h: 10);
905 window.create();
906
907 QOpenGLContext ctx;
908 QVERIFY(ctx.create());
909 ctx.makeCurrent(surface: &window);
910
911 QOpenGLFunctions *f = ctx.functions();
912 const char * vendor = (const char *) f->glGetString(GL_VENDOR);
913 if (vendor && strstr(haystack: vendor, needle: "VMware, Inc."))
914 QSKIP("The tested formats may not be supported on this platform");
915
916 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
917 QSKIP("QOpenGLFramebufferObject not supported on this platform");
918
919 if (!f->hasOpenGLFeature(feature: QOpenGLFunctions::MultipleRenderTargets))
920 QSKIP("Multiple render targets not supported on this platform");
921
922 if (!supportsInternalFboFormat(ctx: &ctx, GL_RGB10_A2))
923 QSKIP("RGB10_A2 not supported on this platform");
924
925 // 3 color attachments, same size, different internal format, depth/stencil.
926 QVector<QSize> sizes;
927 sizes.fill(t: QSize(128, 128), size: 3);
928 QOpenGLFramebufferObjectFormat format;
929 format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
930 QVector<GLenum> internalFormats;
931 internalFormats << GL_RGBA8 << GL_RGB10_A2 << GL_RGB5_A1;
932 format.setInternalTextureFormat(internalFormats[0]);
933 QOpenGLFramebufferObject fbo(sizes[0], format);
934 fbo.addColorAttachment(size: sizes[1], internalFormat: internalFormats[1]);
935 fbo.addColorAttachment(size: sizes[2], internalFormat: internalFormats[2]);
936
937 QVERIFY(fbo.bind());
938 QCOMPARE(fbo.attachment(), QOpenGLFramebufferObject::CombinedDepthStencil);
939 QCOMPARE(sizes, fbo.sizes());
940 QCOMPARE(sizes[0], fbo.size());
941
942 QOpenGLExtraFunctions *ef = ctx.extraFunctions();
943 QVERIFY(ef->glGetError() == 0);
944 ef->glClearColor(red: 1, green: 0, blue: 0, alpha: 1);
945 GLenum drawBuf[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT0 + 1, GL_COLOR_ATTACHMENT0 + 2 };
946 ef->glDrawBuffers(n: 3, bufs: drawBuf);
947 ef->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
948 QVERIFY(ef->glGetError() == 0);
949
950 QImage img = fbo.toImage(flipped: true, colorAttachmentIndex: 0);
951 QCOMPARE(img.size(), sizes[0]);
952 QCOMPARE(img.pixel(0, 0), qRgb(255, 0, 0));
953 img = fbo.toImage(flipped: true, colorAttachmentIndex: 1);
954 QCOMPARE(img.size(), sizes[1]);
955 QCOMPARE(img.format(), QImage::Format_A2BGR30_Premultiplied);
956 QCOMPARE(img.pixel(0, 0), qRgb(255, 0, 0));
957
958 fbo.release();
959}
960
961void tst_QOpenGL::imageFormatPainting()
962{
963 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
964
965 QOpenGLContext ctx;
966 QVERIFY(ctx.create());
967
968 QVERIFY(ctx.makeCurrent(surface.data()));
969
970 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
971 QSKIP("QOpenGLFramebufferObject not supported on this platform");
972
973 QOpenGLFramebufferObjectFormat fboFormat;
974 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
975
976 const QSize size(128, 128);
977 QOpenGLFramebufferObject fbo(size, fboFormat);
978
979 if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil)
980 QSKIP("FBOs missing combined depth~stencil support");
981
982 QVERIFY(fbo.bind());
983
984 QImage alpha(128, 128, QImage::Format_Alpha8);
985 alpha.fill(pixel: 127);
986
987 QPainter fboPainter;
988 QOpenGLPaintDevice device(fbo.width(), fbo.height());
989
990 QVERIFY(fboPainter.begin(&device));
991 fboPainter.fillRect(x: 0, y: 0, w: 128, h: 128, b: qRgb(r: 255, g: 0, b: 255));
992 fboPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
993 fboPainter.drawImage(x: 0, y: 0, image: alpha);
994 fboPainter.end();
995
996 QImage fb = fbo.toImage();
997 QCOMPARE(fb.pixel(0, 0), qRgba(127, 0, 127, 127));
998
999 QImage grayscale(128, 128, QImage::Format_Grayscale8);
1000 grayscale.fill(pixel: 128);
1001
1002 QVERIFY(fboPainter.begin(&device));
1003 fboPainter.setCompositionMode(QPainter::CompositionMode_Plus);
1004 fboPainter.drawImage(x: 0, y: 0, image: grayscale);
1005 fboPainter.end();
1006
1007 fb = fbo.toImage();
1008 QCOMPARE(fb.pixel(0, 0), qRgb(255, 128, 255));
1009
1010 QImage argb(128, 128, QImage::Format_ARGB32);
1011 argb.fill(pixel: qRgba(r: 255, g: 255, b: 255, a: 128));
1012
1013 QVERIFY(fboPainter.begin(&device));
1014 fboPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
1015 fboPainter.drawImage(x: 0, y: 0, image: argb);
1016 fboPainter.end();
1017
1018 fb = fbo.toImage();
1019 QCOMPARE(fb.pixel(0, 0), qRgb(255, 192, 255));
1020
1021}
1022
1023void tst_QOpenGL::openGLPaintDevice_data()
1024{
1025 QTest::addColumn<int>(name: "surfaceClass");
1026 QTest::addColumn<QImage::Format>(name: "imageFormat");
1027
1028 QTest::newRow(dataTag: "Using QWindow - RGB32") << int(QSurface::Window) << QImage::Format_RGB32;
1029 QTest::newRow(dataTag: "Using QOffscreenSurface - RGB32") << int(QSurface::Offscreen) << QImage::Format_RGB32;
1030 QTest::newRow(dataTag: "Using QOffscreenSurface - RGBx8888") << int(QSurface::Offscreen) << QImage::Format_RGBX8888;
1031 QTest::newRow(dataTag: "Using QOffscreenSurface - RGB888") << int(QSurface::Offscreen) << QImage::Format_RGB888;
1032 QTest::newRow(dataTag: "Using QOffscreenSurface - RGB16") << int(QSurface::Offscreen) << QImage::Format_RGB16;
1033}
1034
1035static void drawColoredRects(QPainter *p, const QSize &size)
1036{
1037 p->fillRect(x: 0, y: 0, w: size.width() / 2, h: size.height() / 2, c: Qt::red);
1038 p->fillRect(x: size.width() / 2, y: 0, w: size.width() / 2, h: size.height() / 2, c: Qt::green);
1039 p->fillRect(x: size.width() / 2, y: size.height() / 2, w: size.width() / 2, h: size.height() / 2, c: Qt::blue);
1040 p->fillRect(x: 0, y: size.height() / 2, w: size.width() / 2, h: size.height() / 2, c: Qt::white);
1041}
1042
1043void tst_QOpenGL::openGLPaintDevice()
1044{
1045#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
1046 QSKIP("QTBUG-22617");
1047#endif
1048
1049 QFETCH(int, surfaceClass);
1050 QFETCH(QImage::Format, imageFormat);
1051 QScopedPointer<QSurface> surface(createSurface(surfaceClass));
1052
1053 QOpenGLContext ctx;
1054 QVERIFY(ctx.create());
1055
1056 QSurfaceFormat format = ctx.format();
1057 if (format.majorVersion() < 2)
1058 QSKIP("This test requires at least OpenGL 2.0");
1059 QVERIFY(ctx.makeCurrent(surface.data()));
1060
1061 const QSize size(128, 128);
1062
1063 QImage image(size, imageFormat);
1064 QPainter p(&image);
1065 drawColoredRects(p: &p, size: image.size());
1066 p.end();
1067
1068 QOpenGLFramebufferObject fbo(size);
1069 QVERIFY(fbo.bind());
1070
1071 QOpenGLPaintDevice device(size);
1072 QVERIFY(p.begin(&device));
1073 drawColoredRects(p: &p, size: image.size());
1074 p.end();
1075
1076 QImage actual = fbo.toImage().convertToFormat(f: imageFormat);
1077 QCOMPARE(image.size(), actual.size());
1078 QCOMPARE(image, actual);
1079
1080 QVERIFY(p.begin(&device));
1081 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), c: Qt::black);
1082 p.drawImage(x: 0, y: 0, image);
1083 p.end();
1084
1085 actual = fbo.toImage().convertToFormat(f: imageFormat);
1086 QCOMPARE(image.size(), actual.size());
1087 QCOMPARE(image, actual);
1088
1089 QVERIFY(p.begin(&device));
1090 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), c: Qt::black);
1091 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), b: QBrush(image));
1092 p.end();
1093
1094 actual = fbo.toImage().convertToFormat(f: imageFormat);
1095 QCOMPARE(image.size(), actual.size());
1096 QCOMPARE(image, actual);
1097}
1098
1099void tst_QOpenGL::openGLPaintDeviceWithChangingContext()
1100{
1101 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1102 const QSize size(512, 512);
1103
1104 // QOpenGLPaintDevice has a thread-local paint engine. Therefore render
1105 // twice, with a different context and device. Under the hood it will
1106 // still use the same paint engine!
1107
1108 QOpenGLContext ctx;
1109 QVERIFY(ctx.create());
1110 QVERIFY(ctx.makeCurrent(surface.data()));
1111
1112 QOpenGLFramebufferObject fbo(size);
1113 QVERIFY(fbo.bind());
1114
1115 QOpenGLPaintDevice device(size);
1116
1117 QPainter p;
1118 QVERIFY(p.begin(&device));
1119 drawColoredRects(p: &p, size);
1120 p.end();
1121
1122 QImage img1 = fbo.toImage();
1123
1124 QOpenGLContext ctx2;
1125 // When supported, test the special case, where the second context is
1126 // totally incompatible due to being a core profile one.
1127 QSurfaceFormat coreFormat;
1128 coreFormat.setVersion(major: 3, minor: 2);
1129 coreFormat.setProfile(QSurfaceFormat::CoreProfile);
1130 ctx2.setFormat(coreFormat);
1131 if (!ctx2.create() || !ctx2.makeCurrent(surface: surface.data())) {
1132 ctx2.setFormat(QSurfaceFormat());
1133 QVERIFY(ctx2.create());
1134 }
1135
1136 QVERIFY(ctx2.makeCurrent(surface.data()));
1137
1138 QOpenGLFramebufferObject fbo2(size);
1139 QVERIFY(fbo2.bind());
1140
1141 QOpenGLPaintDevice device2(size);
1142
1143 QVERIFY(p.begin(&device2));
1144 drawColoredRects(p: &p, size);
1145 p.end();
1146
1147 QImage img2 = fbo2.toImage();
1148
1149 QFUZZY_COMPARE_IMAGES(img1, img2);
1150}
1151
1152void tst_QOpenGL::aboutToBeDestroyed()
1153{
1154 QWindow window;
1155 window.setSurfaceType(QWindow::OpenGLSurface);
1156 window.setGeometry(posx: 0, posy: 0, w: 128, h: 128);
1157 window.create();
1158
1159 QOpenGLContext *context = new QOpenGLContext;
1160 QSignalSpy spy(context, SIGNAL(aboutToBeDestroyed()));
1161
1162 QVERIFY(context->create());
1163 QVERIFY(context->makeCurrent(&window));
1164
1165 QCOMPARE(spy.size(), 0);
1166
1167 delete context;
1168
1169 QCOMPARE(spy.size(), 1);
1170}
1171
1172// Verify that QOpenGLContext works with QWindows that do
1173// not have an explicit size set.
1174void tst_QOpenGL::sizeLessWindow()
1175{
1176 // top-level window
1177 {
1178 QWindow window;
1179 window.setSurfaceType(QWindow::OpenGLSurface);
1180
1181 QOpenGLContext context;
1182 QVERIFY(context.create());
1183
1184 window.show();
1185 QVERIFY(context.makeCurrent(&window));
1186 QVERIFY(QOpenGLContext::currentContext());
1187 }
1188
1189 QVERIFY(!QOpenGLContext::currentContext());
1190
1191 // child window
1192 {
1193 QWindow parent;
1194 QWindow window(&parent);
1195 window.setSurfaceType(QWindow::OpenGLSurface);
1196
1197 QOpenGLContext context;
1198 QVERIFY(context.create());
1199
1200 parent.show();
1201 window.show();
1202 QVERIFY(context.makeCurrent(&window));
1203 QVERIFY(QOpenGLContext::currentContext());
1204 }
1205
1206 QVERIFY(!QOpenGLContext::currentContext());
1207}
1208
1209void tst_QOpenGL::QTBUG15621_triangulatingStrokerDivZero()
1210{
1211#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
1212 QSKIP("QTBUG-22617");
1213#endif
1214
1215 const QSize size(128, 128);
1216
1217 QWindow window;
1218 window.setSurfaceType(QWindow::OpenGLSurface);
1219 window.setGeometry(QRect(QPoint(0, 0), size));
1220 window.create();
1221
1222 QOpenGLContext ctx;
1223 QVERIFY(ctx.create());
1224 QVERIFY(ctx.makeCurrent(&window));
1225
1226 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
1227 QSKIP("QOpenGLFramebufferObject not supported on this platform");
1228
1229 QOpenGLFramebufferObject fbo(size);
1230 QVERIFY(fbo.bind());
1231
1232 QOpenGLPaintDevice device(size);
1233
1234 // QTBUG-15621 is only a problem when qreal is double, but do the test anyway.
1235 qreal delta = sizeof(qreal) == sizeof(float) ? 1e-4 : 1e-8;
1236 QVERIFY(128 != 128 + delta);
1237
1238 QPainterPath path;
1239 path.moveTo(x: 16 + delta, y: 16);
1240 path.moveTo(x: 16, y: 16);
1241
1242 path.lineTo(x: 16 + delta, y: 16); // Short lines to check for division by zero.
1243 path.lineTo(x: 112 - delta, y: 16);
1244 path.lineTo(x: 112, y: 16);
1245
1246 path.quadTo(ctrlPtx: 112, ctrlPty: 16, endPtx: 112, endPty: 16 + delta);
1247 path.quadTo(ctrlPtx: 112, ctrlPty: 64, endPtx: 112, endPty: 112 - delta);
1248 path.quadTo(ctrlPtx: 112, ctrlPty: 112, endPtx: 112, endPty: 112);
1249
1250 path.cubicTo(ctrlPt1x: 112, ctrlPt1y: 112, ctrlPt2x: 112, ctrlPt2y: 112, endPtx: 112 - delta, endPty: 112);
1251 path.cubicTo(ctrlPt1x: 80, ctrlPt1y: 112, ctrlPt2x: 48, ctrlPt2y: 112, endPtx: 16 + delta, endPty: 112);
1252 path.cubicTo(ctrlPt1x: 16 + delta, ctrlPt1y: 112, ctrlPt2x: 16 + delta, ctrlPt2y: 112, endPtx: 16, endPty: 112);
1253
1254 path.closeSubpath();
1255
1256 QPen pen(Qt::red, 28, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
1257
1258 QPainter p(&device);
1259 p.fillRect(r: QRect(QPoint(0, 0), size), c: Qt::blue);
1260 p.strokePath(path, pen);
1261 p.end();
1262 const QImage image = fbo.toImage().convertToFormat(f: QImage::Format_RGB32);
1263 QCOMPARE(image.size(), size);
1264
1265 const QRgb red = 0xffff0000;
1266 const QRgb blue = 0xff0000ff;
1267
1268 QCOMPARE(image.pixel(8, 8), red);
1269 QCOMPARE(image.pixel(119, 8), red);
1270 QCOMPARE(image.pixel(8, 119), red);
1271 QCOMPARE(image.pixel(119, 119), red);
1272
1273 QCOMPARE(image.pixel(0, 0), blue);
1274 QCOMPARE(image.pixel(127, 0), blue);
1275 QCOMPARE(image.pixel(0, 127), blue);
1276 QCOMPARE(image.pixel(127, 127), blue);
1277
1278 QCOMPARE(image.pixel(32, 32), blue);
1279 QCOMPARE(image.pixel(95, 32), blue);
1280 QCOMPARE(image.pixel(32, 95), blue);
1281 QCOMPARE(image.pixel(95, 95), blue);
1282}
1283
1284typedef QGenericMatrix<1, 3, float> TestVertex3D;
1285static const float uv_top_left[] = {0.f, 1.f, 1.f};
1286static const float uv_bottom_left[] = {0.f, 0.f, 1.f};
1287static const float uv_top_right[] = {1.f, 1.f, 1.f};
1288static const float uv_bottom_right[] = {1.f, 0.f, 1.f};
1289
1290bool q_fuzzy_compare(const TestVertex3D &left, const TestVertex3D &right) {
1291 return qFuzzyCompare(p1: left(0,0), p2: right(0,0)) &&
1292 qFuzzyCompare(p1: left(1,0), p2: right(1,0)) &&
1293 qFuzzyCompare(p1: left(2,0), p2: right(2,0));
1294}
1295
1296void tst_QOpenGL::textureblitterFullSourceRectTransform()
1297{
1298 TestVertex3D topLeft(uv_top_left);
1299 TestVertex3D bottomLeft(uv_bottom_left);
1300 TestVertex3D topRight(uv_top_right);
1301 TestVertex3D bottomRight(uv_bottom_right);
1302
1303 QRectF rect(0,0,1,1);
1304 QMatrix3x3 flippedMatrix = QOpenGLTextureBlitter::sourceTransform(subTexture: rect, textureSize: rect.size().toSize(), origin: QOpenGLTextureBlitter::OriginTopLeft);
1305
1306 TestVertex3D flippedTopLeft = flippedMatrix * topLeft;
1307 QCOMPARE(flippedTopLeft, bottomLeft);
1308
1309 TestVertex3D flippedBottomLeft = flippedMatrix * bottomLeft;
1310 QCOMPARE(flippedBottomLeft, topLeft);
1311
1312 TestVertex3D flippedTopRight = flippedMatrix * topRight;
1313 QCOMPARE(flippedTopRight, bottomRight);
1314
1315 TestVertex3D flippedBottomRight = flippedMatrix * bottomRight;
1316 QCOMPARE(flippedBottomRight, topRight);
1317
1318 QMatrix3x3 identityMatrix = QOpenGLTextureBlitter::sourceTransform(subTexture: rect, textureSize: rect.size().toSize(), origin: QOpenGLTextureBlitter::OriginBottomLeft);
1319
1320 TestVertex3D notFlippedTopLeft = identityMatrix * topLeft;
1321 QCOMPARE(notFlippedTopLeft, topLeft);
1322
1323 TestVertex3D notFlippedBottomLeft = identityMatrix * bottomLeft;
1324 QCOMPARE(notFlippedBottomLeft, bottomLeft);
1325
1326 TestVertex3D notFlippedTopRight = identityMatrix * topRight;
1327 QCOMPARE(notFlippedTopRight, topRight);
1328
1329 TestVertex3D notFlippedBottomRight = identityMatrix * bottomRight;
1330 QCOMPARE(notFlippedBottomRight, bottomRight);
1331}
1332
1333void tst_QOpenGL::textureblitterPartOriginBottomLeftSourceRectTransform()
1334{
1335 TestVertex3D topLeft(uv_top_left);
1336 TestVertex3D bottomLeft(uv_bottom_left);
1337 TestVertex3D topRight(uv_top_right);
1338 TestVertex3D bottomRight(uv_bottom_right);
1339
1340 QRectF sourceRect(50,200,200,200);
1341 QSize textureSize(400,400);
1342
1343 QMatrix3x3 sourceMatrix = QOpenGLTextureBlitter::sourceTransform(subTexture: sourceRect, textureSize, origin: QOpenGLTextureBlitter::OriginBottomLeft);
1344
1345 const float x_point_ratio = sourceRect.topLeft().x() / textureSize.width();
1346 const float y_point_ratio = sourceRect.topLeft().y() / textureSize.height();
1347 const float width_ratio = sourceRect.width() / textureSize.width();
1348 const float height_ratio = sourceRect.height() / textureSize.height();
1349
1350 TestVertex3D uvTopLeft = sourceMatrix * topLeft;
1351 const float expected_top_left[] = { x_point_ratio, y_point_ratio + height_ratio, 1 };
1352 TestVertex3D expectedTopLeft(expected_top_left);
1353 QCOMPARE(uvTopLeft, expectedTopLeft);
1354
1355 TestVertex3D uvBottomLeft = sourceMatrix * bottomLeft;
1356 const float expected_bottom_left[] = { x_point_ratio, y_point_ratio, 1 };
1357 TestVertex3D expectedBottomLeft(expected_bottom_left);
1358 QCOMPARE(uvBottomLeft, expectedBottomLeft);
1359
1360 TestVertex3D uvTopRight = sourceMatrix * topRight;
1361 const float expected_top_right[] = { x_point_ratio + width_ratio, y_point_ratio + height_ratio, 1 };
1362 TestVertex3D expectedTopRight(expected_top_right);
1363 QCOMPARE(uvTopRight, expectedTopRight);
1364
1365 TestVertex3D uvBottomRight = sourceMatrix * bottomRight;
1366 const float expected_bottom_right[] = { x_point_ratio + width_ratio, y_point_ratio, 1 };
1367 TestVertex3D expectedBottomRight(expected_bottom_right);
1368 QCOMPARE(uvBottomRight, expectedBottomRight);
1369}
1370
1371void tst_QOpenGL::textureblitterPartOriginTopLeftSourceRectTransform()
1372{
1373 TestVertex3D topLeft(uv_top_left);
1374 TestVertex3D bottomLeft(uv_bottom_left);
1375 TestVertex3D topRight(uv_top_right);
1376 TestVertex3D bottomRight(uv_bottom_right);
1377
1378 QRectF sourceRect(50,190,170,170);
1379 QSize textureSize(400,400);
1380
1381 QMatrix3x3 sourceMatrix = QOpenGLTextureBlitter::sourceTransform(subTexture: sourceRect, textureSize, origin: QOpenGLTextureBlitter::OriginTopLeft);
1382
1383 const float x_point_ratio = sourceRect.topLeft().x() / textureSize.width();
1384 const float y_point_ratio = sourceRect.topLeft().y() / textureSize.height();
1385 const float width_ratio = sourceRect.width() / textureSize.width();
1386 const float height_ratio = sourceRect.height() / textureSize.height();
1387
1388 TestVertex3D uvTopLeft = sourceMatrix * topLeft;
1389 const float expected_top_left[] = { x_point_ratio, 1 - y_point_ratio - height_ratio, 1 };
1390 TestVertex3D expectedTopLeft(expected_top_left);
1391 QVERIFY(q_fuzzy_compare(uvTopLeft, expectedTopLeft));
1392
1393 TestVertex3D uvBottomLeft = sourceMatrix * bottomLeft;
1394 const float expected_bottom_left[] = { x_point_ratio, 1 - y_point_ratio, 1 };
1395 TestVertex3D expectedBottomLeft(expected_bottom_left);
1396 QVERIFY(q_fuzzy_compare(uvBottomLeft, expectedBottomLeft));
1397
1398 TestVertex3D uvTopRight = sourceMatrix * topRight;
1399 const float expected_top_right[] = { x_point_ratio + width_ratio, 1 - y_point_ratio - height_ratio, 1 };
1400 TestVertex3D expectedTopRight(expected_top_right);
1401 QVERIFY(q_fuzzy_compare(uvTopRight, expectedTopRight));
1402
1403 TestVertex3D uvBottomRight = sourceMatrix * bottomRight;
1404 const float expected_bottom_right[] = { x_point_ratio + width_ratio, 1 - y_point_ratio, 1 };
1405 TestVertex3D expectedBottomRight(expected_bottom_right);
1406 QVERIFY(q_fuzzy_compare(uvBottomRight, expectedBottomRight));
1407}
1408
1409void tst_QOpenGL::textureblitterFullTargetRectTransform()
1410{
1411 QVector4D topLeft(-1.f, 1.f, 0.f, 1.f);
1412 QVector4D bottomLeft(-1.f, -1.f, 0.f, 1.f);
1413 QVector4D topRight(1.f, 1.f, 0.f, 1.f);
1414 QVector4D bottomRight(1.f, -1.f, 0.f, 1.f);
1415
1416 QRectF rect(0,0,200,200);
1417 QMatrix4x4 targetMatrix = QOpenGLTextureBlitter::targetTransform(target: rect,viewport: rect.toRect());
1418
1419 QVector4D translatedTopLeft = targetMatrix * topLeft;
1420 QCOMPARE(translatedTopLeft, topLeft);
1421
1422 QVector4D translatedBottomLeft = targetMatrix * bottomLeft;
1423 QCOMPARE(translatedBottomLeft, bottomLeft);
1424
1425 QVector4D translatedTopRight = targetMatrix * topRight;
1426 QCOMPARE(translatedTopRight, topRight);
1427
1428 QVector4D translatedBottomRight = targetMatrix * bottomRight;
1429 QCOMPARE(translatedBottomRight, bottomRight);
1430}
1431
1432void tst_QOpenGL::textureblitterPartTargetRectTransform()
1433{
1434 QVector4D topLeft(-1.f, 1.f, 0.f, 1.f);
1435 QVector4D bottomLeft(-1.f, -1.f, 0.f, 1.f);
1436 QVector4D topRight(1.f, 1.f, 0.f, 1.f);
1437 QVector4D bottomRight(1.f, -1.f, 0.f, 1.f);
1438
1439 QRectF targetRect(50,50,200,200);
1440 QRect viewport(0,0,400,400);
1441
1442 //multiply by 2 since coordinate system goes from -1 -> 1;
1443 qreal x_point_ratio = (50. / 400.) * 2;
1444 qreal y_point_ratio = (50. / 400.) * 2;
1445 qreal width_ratio = (200. / 400.) * 2;
1446 qreal height_ratio = (200. / 400.) * 2;
1447
1448 QMatrix4x4 targetMatrix = QOpenGLTextureBlitter::targetTransform(target: targetRect, viewport);
1449
1450 QVector4D targetTopLeft = targetMatrix * topLeft;
1451 QVector4D expectedTopLeft(-1 + x_point_ratio, 1 - y_point_ratio, .0, 1.0);
1452 QCOMPARE(targetTopLeft, expectedTopLeft);
1453
1454 QVector4D targetBottomLeft = targetMatrix * bottomLeft;
1455 QVector4D expectedBottomLeft(-1 + x_point_ratio, 1 - y_point_ratio - height_ratio, 0.0, 1.0);
1456 QCOMPARE(targetBottomLeft, expectedBottomLeft);
1457
1458 QVector4D targetTopRight = targetMatrix * topRight;
1459 QVector4D expectedTopRight(-1 + x_point_ratio + width_ratio, 1 - y_point_ratio, 0.0, 1.0);
1460 QCOMPARE(targetTopRight, expectedTopRight);
1461
1462 QVector4D targetBottomRight = targetMatrix * bottomRight;
1463 QVector4D expectedBottomRight(-1 + x_point_ratio + width_ratio, 1 - y_point_ratio - height_ratio, 0.0, 1.0);
1464 QCOMPARE(targetBottomRight, expectedBottomRight);
1465}
1466
1467void tst_QOpenGL::defaultSurfaceFormat()
1468{
1469 QSurfaceFormat fmt;
1470 QCOMPARE(QSurfaceFormat::defaultFormat(), fmt);
1471
1472 fmt.setDepthBufferSize(16);
1473 QSurfaceFormat::setDefaultFormat(fmt);
1474 QCOMPARE(QSurfaceFormat::defaultFormat(), fmt);
1475 QCOMPARE(QSurfaceFormat::defaultFormat().depthBufferSize(), 16);
1476
1477 QScopedPointer<QWindow> window(new QWindow);
1478 QCOMPARE(window->requestedFormat(), fmt);
1479
1480 QScopedPointer<QOpenGLContext> context(new QOpenGLContext);
1481 QCOMPARE(context->format(), fmt);
1482}
1483
1484#ifdef USE_GLX
1485void tst_QOpenGL::glxContextWrap()
1486{
1487 QWindow *window = new QWindow;
1488 window->setSurfaceType(QWindow::OpenGLSurface);
1489 window->setGeometry(posx: 0, posy: 0, w: 10, h: 10);
1490 window->show();
1491 QVERIFY(QTest::qWaitForWindowExposed(window));
1492
1493 QPlatformNativeInterface *nativeIf = QGuiApplicationPrivate::instance()->platformIntegration()->nativeInterface();
1494 QVERIFY(nativeIf);
1495
1496 // Fetch a GLXContext.
1497 QOpenGLContext *ctx0 = new QOpenGLContext;
1498 ctx0->setFormat(window->format());
1499 QVERIFY(ctx0->create());
1500 QVariant v = ctx0->nativeHandle();
1501 QVERIFY(!v.isNull());
1502 QVERIFY(v.canConvert<QGLXNativeContext>());
1503 GLXContext context = v.value<QGLXNativeContext>().context();
1504 QVERIFY(context);
1505
1506 // Then create another QOpenGLContext wrapping it.
1507 QOpenGLContext *ctx = new QOpenGLContext;
1508 ctx->setNativeHandle(QVariant::fromValue<QGLXNativeContext>(value: QGLXNativeContext(context)));
1509 QVERIFY(ctx->create());
1510 QCOMPARE(ctx->nativeHandle().value<QGLXNativeContext>().context(), context);
1511 QVERIFY(nativeIf->nativeResourceForContext(QByteArrayLiteral("glxcontext"), ctx) == (void *) context);
1512
1513 QVERIFY(ctx->makeCurrent(window));
1514 ctx->doneCurrent();
1515
1516 delete ctx;
1517 delete ctx0;
1518
1519 delete window;
1520}
1521#endif // USE_GLX
1522
1523#if defined(Q_OS_WIN32) && !defined(QT_OPENGL_ES_2)
1524void tst_QOpenGL::wglContextWrap()
1525{
1526 QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
1527 QVERIFY(ctx->create());
1528 if (ctx->isOpenGLES())
1529 QSKIP("Not applicable to EGL");
1530
1531 QScopedPointer<QWindow> window(new QWindow);
1532 window->setSurfaceType(QWindow::OpenGLSurface);
1533 window->setGeometry(0, 0, 256, 256);
1534 window->show();
1535 QVERIFY(QTest::qWaitForWindowExposed(window.data()));
1536
1537 QVariant v = ctx->nativeHandle();
1538 QVERIFY(!v.isNull());
1539 QVERIFY(v.canConvert<QWGLNativeContext>());
1540 QWGLNativeContext nativeContext = v.value<QWGLNativeContext>();
1541 QVERIFY(nativeContext.context());
1542
1543 // Now do a makeCurrent() do make sure the pixel format on the native
1544 // window (the HWND we are going to retrieve below) is set.
1545 QVERIFY(ctx->makeCurrent(window.data()));
1546 ctx->doneCurrent();
1547
1548 HWND wnd = (HWND) qGuiApp->platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("handle"), window.data());
1549 QVERIFY(wnd);
1550
1551 QScopedPointer<QOpenGLContext> adopted(new QOpenGLContext);
1552 adopted->setNativeHandle(QVariant::fromValue<QWGLNativeContext>(QWGLNativeContext(nativeContext.context(), wnd)));
1553 QVERIFY(adopted->create());
1554
1555 // This tests two things: that a regular, non-adopted QOpenGLContext is
1556 // able to return a QSurfaceFormat containing the real values after
1557 // create(), and that the adopted context got the correct pixel format from
1558 // window and was able to update its format accordingly.
1559 QCOMPARE(adopted->format().version(), ctx->format().version());
1560 QCOMPARE(adopted->format().profile(), ctx->format().profile());
1561 QVERIFY(ctx->format().redBufferSize() > 0);
1562 QCOMPARE(adopted->format().redBufferSize(), ctx->format().redBufferSize());
1563 QVERIFY(ctx->format().greenBufferSize() > 0);
1564 QCOMPARE(adopted->format().greenBufferSize(), ctx->format().greenBufferSize());
1565 QVERIFY(ctx->format().blueBufferSize() > 0);
1566 QCOMPARE(adopted->format().blueBufferSize(), ctx->format().blueBufferSize());
1567 QVERIFY(ctx->format().depthBufferSize() > 0);
1568 QCOMPARE(adopted->format().depthBufferSize(), ctx->format().depthBufferSize());
1569 QVERIFY(ctx->format().stencilBufferSize() > 0);
1570 QCOMPARE(adopted->format().stencilBufferSize(), ctx->format().stencilBufferSize());
1571
1572 // This must work since we are using the exact same window.
1573 QVERIFY(adopted->makeCurrent(window.data()));
1574 adopted->doneCurrent();
1575}
1576#endif // Q_OS_WIN32 && !QT_OPENGL_ES_2
1577
1578void tst_QOpenGL::vaoCreate()
1579{
1580 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1581 QOpenGLContext *ctx = new QOpenGLContext;
1582 ctx->create();
1583 ctx->makeCurrent(surface: surface.data());
1584
1585 QOpenGLVertexArrayObject vao;
1586 bool success = vao.create();
1587 if (ctx->isOpenGLES()) {
1588 if (ctx->format().majorVersion() >= 3 || ctx->hasExtension(QByteArrayLiteral("GL_OES_vertex_array_object")))
1589 QVERIFY(success);
1590 } else {
1591 if (ctx->format().majorVersion() >= 3 || ctx->hasExtension(QByteArrayLiteral("GL_ARB_vertex_array_object")))
1592 QVERIFY(success);
1593 }
1594
1595 vao.destroy();
1596 ctx->doneCurrent();
1597}
1598
1599void tst_QOpenGL::bufferCreate()
1600{
1601 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1602 QOpenGLContext *ctx = new QOpenGLContext;
1603 ctx->create();
1604 ctx->makeCurrent(surface: surface.data());
1605
1606 QOpenGLBuffer buf;
1607
1608 QVERIFY(!buf.isCreated());
1609
1610 QVERIFY(buf.create());
1611 QVERIFY(buf.isCreated());
1612
1613 QCOMPARE(buf.type(), QOpenGLBuffer::VertexBuffer);
1614
1615 buf.bind();
1616 buf.allocate(count: 128);
1617 QCOMPARE(buf.size(), 128);
1618
1619 buf.release();
1620
1621 buf.destroy();
1622 QVERIFY(!buf.isCreated());
1623
1624 ctx->doneCurrent();
1625}
1626
1627void tst_QOpenGL::bufferMapRange()
1628{
1629 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1630 QOpenGLContext *ctx = new QOpenGLContext;
1631 ctx->create();
1632 ctx->makeCurrent(surface: surface.data());
1633
1634 QOpenGLExtensions funcs(ctx);
1635 if (!funcs.hasOpenGLExtension(extension: QOpenGLExtensions::MapBufferRange))
1636 QSKIP("glMapBufferRange not supported");
1637
1638 QOpenGLBuffer buf;
1639 QVERIFY(buf.create());
1640 buf.bind();
1641 const char data[] = "some data";
1642 buf.allocate(data, count: sizeof(data));
1643
1644 char *p = (char *) buf.mapRange(offset: 0, count: sizeof(data), access: QOpenGLBuffer::RangeRead | QOpenGLBuffer::RangeWrite);
1645 QVERIFY(p);
1646 QVERIFY(!strcmp(p, data));
1647 p[1] = 'O';
1648 buf.unmap();
1649
1650 p = (char *) buf.mapRange(offset: 1, count: 2, access: QOpenGLBuffer::RangeWrite);
1651 QVERIFY(p);
1652 p[1] = 'M';
1653 buf.unmap();
1654
1655 p = (char *) buf.mapRange(offset: 0, count: sizeof(data), access: QOpenGLBuffer::RangeRead);
1656 QVERIFY(!strcmp(p, "sOMe data"));
1657 buf.unmap();
1658
1659 buf.destroy();
1660 ctx->doneCurrent();
1661}
1662
1663void tst_QOpenGL::defaultQGLCurrentBuffer()
1664{
1665 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1666 QScopedPointer<QOpenGLContext> ctx(new QOpenGLContext);
1667 ctx->create();
1668 ctx->makeCurrent(surface: surface.data());
1669
1670 // Bind default FBO on the current context, and record what's the current QGL FBO. It should
1671 // be nullptr because the default platform OpenGL FBO is not backed by a
1672 // QOpenGLFramebufferObject.
1673 QOpenGLFramebufferObject::bindDefault();
1674 QOpenGLFramebufferObject *defaultQFBO = QOpenGLContextPrivate::get(context: ctx.data())->qgl_current_fbo;
1675
1676 // Create new FBO, bind it, and see that the QGL FBO points to the newly created FBO.
1677 QScopedPointer<QOpenGLFramebufferObject> obj(new QOpenGLFramebufferObject(128, 128));
1678 obj->bind();
1679 QOpenGLFramebufferObject *customQFBO = QOpenGLContextPrivate::get(context: ctx.data())->qgl_current_fbo;
1680 QVERIFY(defaultQFBO != customQFBO);
1681
1682 // Bind the default FBO, and check that the QGL FBO points to the original FBO object.
1683 QOpenGLFramebufferObject::bindDefault();
1684 QOpenGLFramebufferObject *finalQFBO = QOpenGLContextPrivate::get(context: ctx.data())->qgl_current_fbo;
1685 QCOMPARE(defaultQFBO, finalQFBO);
1686
1687 ctx->doneCurrent();
1688}
1689
1690void tst_QOpenGL::nullTextureInitializtion()
1691{
1692 QScopedPointer<QSurface> surface(createSurface(surfaceClass: QSurface::Window));
1693 QOpenGLContext ctx;
1694 ctx.create();
1695 ctx.makeCurrent(surface: surface.data());
1696
1697 QImage i;
1698 QOpenGLTexture t(i);
1699 QVERIFY(!t.isCreated());
1700}
1701
1702/*
1703 Verify that the clipping works correctly.
1704 The red outline should be covered by the blue rect on top and left,
1705 while it should be clipped on the right and bottom and thus the red outline be visible
1706
1707 See: QTBUG-83229
1708*/
1709void tst_QOpenGL::clipRect()
1710{
1711#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__)
1712 QSKIP("QTBUG-22617");
1713#endif
1714
1715 QScopedPointer<QSurface> surface(createSurface(surfaceClass: int(QSurface::Window)));
1716
1717 QOpenGLContext ctx;
1718 QVERIFY(ctx.create());
1719
1720 QVERIFY(ctx.makeCurrent(surface.data()));
1721
1722 if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
1723 QSKIP("QOpenGLFramebufferObject not supported on this platform");
1724
1725 // No multisample with combined depth/stencil attachment:
1726 QOpenGLFramebufferObjectFormat fboFormat;
1727 fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
1728
1729 // Uncomplicate things by using POT:
1730 const QSize size(654, 480);
1731 const QRect rect(QPoint(0, 0), size);
1732 QOpenGLFramebufferObject fbo(size, fboFormat);
1733
1734 if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil)
1735 QSKIP("FBOs missing combined depth~stencil support");
1736
1737 QVERIFY(fbo.bind());
1738
1739 QPainter fboPainter;
1740 QOpenGLPaintDevice device(fbo.width(), fbo.height());
1741 bool painterBegun = fboPainter.begin(&device);
1742 QVERIFY(painterBegun);
1743
1744 qreal halfWidth = size.width() / 2.0;
1745 qreal halfHeight = size.height() / 2.0;
1746
1747 QRectF clipRect = QRectF(halfWidth - halfWidth / 2.0, halfHeight - halfHeight / 2.0,
1748 halfWidth / 2.0, halfHeight / 2.0);
1749
1750 fboPainter.fillRect(r: rect, c: Qt::white);
1751 fboPainter.setPen(Qt::red);
1752 fboPainter.drawRect(rect: clipRect);
1753
1754 fboPainter.setClipRect(clipRect, op: Qt::ReplaceClip);
1755 fboPainter.fillRect(r: rect, c: Qt::blue);
1756
1757 fboPainter.end();
1758
1759 const QImage fb = fbo.toImage().convertToFormat(f: QImage::Format_RGB32);
1760 QCOMPARE(fb.size(), size);
1761
1762 QCOMPARE(fb.pixelColor(clipRect.left() + 1, clipRect.top()), QColor(Qt::blue));
1763 QCOMPARE(fb.pixelColor(clipRect.left(), clipRect.top() + 1), QColor(Qt::blue));
1764 QCOMPARE(fb.pixelColor(clipRect.left() + 1, clipRect.bottom()), QColor(Qt::red));
1765
1766 // Enable this once QTBUG-85286 is fixed
1767 //QCOMPARE(fb.pixelColor(clipRect.right(), clipRect.top() + 1), QColor(Qt::red));
1768}
1769
1770QTEST_MAIN(tst_QOpenGL)
1771
1772#include "tst_qopengl.moc"
1773

source code of qtbase/tests/auto/gui/qopengl/tst_qopengl.cpp