diff -r 000000000000 -r 1918ee327afb tests/auto/qgl/tst_qgl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qgl/tst_qgl.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,1923 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef QT_BUILD_INTERNAL +#include +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QGL : public QObject +{ +Q_OBJECT + +public: + tst_QGL(); + virtual ~tst_QGL(); + +private slots: + void getSetCheck(); + void openGLVersionCheck(); + void graphicsViewClipping(); + void partialGLWidgetUpdates_data(); + void partialGLWidgetUpdates(); + void glWidgetRendering(); + void glFBORendering(); + void multipleFBOInterleavedRendering(); + void glFBOUseInGLWidget(); + void glPBufferRendering(); + void glWidgetReparent(); + void glWidgetRenderPixmap(); + void stackedFBOs(); + void colormap(); + void fboFormat(); + void testDontCrashOnDanglingResources(); + void replaceClipping(); + void clipTest(); + void destroyFBOAfterContext(); + void shareRegister(); +}; + +tst_QGL::tst_QGL() +{ +} + +tst_QGL::~tst_QGL() +{ +} + +class MyGLContext : public QGLContext +{ +public: + MyGLContext(const QGLFormat& format) : QGLContext(format) {} + bool windowCreated() const { return QGLContext::windowCreated(); } + void setWindowCreated(bool on) { QGLContext::setWindowCreated(on); } + bool initialized() const { return QGLContext::initialized(); } + void setInitialized(bool on) { QGLContext::setInitialized(on); } +}; + +class MyGLWidget : public QGLWidget +{ +public: + MyGLWidget() : QGLWidget() {} + bool autoBufferSwap() const { return QGLWidget::autoBufferSwap(); } + void setAutoBufferSwap(bool on) { QGLWidget::setAutoBufferSwap(on); } +}; + +// Using INT_MIN and INT_MAX will cause failures on systems +// where "int" is 64-bit, so use the explicit values instead. +#define TEST_INT_MIN (-2147483647 - 1) +#define TEST_INT_MAX 2147483647 + +// Testing get/set functions +void tst_QGL::getSetCheck() +{ + if (!QGLFormat::hasOpenGL()) + QSKIP("QGL not supported on this platform", SkipAll); + + QGLFormat obj1; + // int QGLFormat::depthBufferSize() + // void QGLFormat::setDepthBufferSize(int) + QCOMPARE(-1, obj1.depthBufferSize()); + obj1.setDepthBufferSize(0); + QCOMPARE(0, obj1.depthBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -2147483648"); + obj1.setDepthBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.depthBufferSize()); // Makes no sense with a negative buffer size + obj1.setDepthBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setDepthBufferSize: Cannot set negative depth buffer size -1"); + obj1.setDepthBufferSize(-1); + QCOMPARE(3, obj1.depthBufferSize()); + obj1.setDepthBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.depthBufferSize()); + + // int QGLFormat::accumBufferSize() + // void QGLFormat::setAccumBufferSize(int) + QCOMPARE(-1, obj1.accumBufferSize()); + obj1.setAccumBufferSize(0); + QCOMPARE(0, obj1.accumBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -2147483648"); + obj1.setAccumBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.accumBufferSize()); // Makes no sense with a negative buffer size + obj1.setAccumBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAccumBufferSize: Cannot set negative accumulate buffer size -1"); + obj1.setAccumBufferSize(-1); + QCOMPARE(3, obj1.accumBufferSize()); + obj1.setAccumBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.accumBufferSize()); + + // int QGLFormat::redBufferSize() + // void QGLFormat::setRedBufferSize(int) + QCOMPARE(-1, obj1.redBufferSize()); + obj1.setRedBufferSize(0); + QCOMPARE(0, obj1.redBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -2147483648"); + obj1.setRedBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.redBufferSize()); // Makes no sense with a negative buffer size + obj1.setRedBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setRedBufferSize: Cannot set negative red buffer size -1"); + obj1.setRedBufferSize(-1); + QCOMPARE(3, obj1.redBufferSize()); + obj1.setRedBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.redBufferSize()); + + // int QGLFormat::greenBufferSize() + // void QGLFormat::setGreenBufferSize(int) + QCOMPARE(-1, obj1.greenBufferSize()); + obj1.setGreenBufferSize(0); + QCOMPARE(0, obj1.greenBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -2147483648"); + obj1.setGreenBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.greenBufferSize()); // Makes no sense with a negative buffer size + obj1.setGreenBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setGreenBufferSize: Cannot set negative green buffer size -1"); + obj1.setGreenBufferSize(-1); + QCOMPARE(3, obj1.greenBufferSize()); + obj1.setGreenBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.greenBufferSize()); + + // int QGLFormat::blueBufferSize() + // void QGLFormat::setBlueBufferSize(int) + QCOMPARE(-1, obj1.blueBufferSize()); + obj1.setBlueBufferSize(0); + QCOMPARE(0, obj1.blueBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -2147483648"); + obj1.setBlueBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.blueBufferSize()); // Makes no sense with a negative buffer size + obj1.setBlueBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setBlueBufferSize: Cannot set negative blue buffer size -1"); + obj1.setBlueBufferSize(-1); + QCOMPARE(3, obj1.blueBufferSize()); + obj1.setBlueBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.blueBufferSize()); + + // int QGLFormat::alphaBufferSize() + // void QGLFormat::setAlphaBufferSize(int) + QCOMPARE(-1, obj1.alphaBufferSize()); + QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlphaBufferSize(0); + QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha. + QCOMPARE(0, obj1.alphaBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648"); + obj1.setAlphaBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.alphaBufferSize()); // Makes no sense with a negative buffer size + obj1.setAlphaBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -1"); + obj1.setAlphaBufferSize(-1); + QCOMPARE(3, obj1.alphaBufferSize()); + obj1.setAlphaBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.alphaBufferSize()); + + // int QGLFormat::stencilBufferSize() + // void QGLFormat::setStencilBufferSize(int) + QCOMPARE(-1, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(0); + QCOMPARE(0, obj1.stencilBufferSize()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648"); + obj1.setStencilBufferSize(TEST_INT_MIN); + QCOMPARE(0, obj1.stencilBufferSize()); // Makes no sense with a negative buffer size + obj1.setStencilBufferSize(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -1"); + obj1.setStencilBufferSize(-1); + QCOMPARE(3, obj1.stencilBufferSize()); + obj1.setStencilBufferSize(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.stencilBufferSize()); + + // bool QGLFormat::sampleBuffers() + // void QGLFormat::setSampleBuffers(bool) +#if !defined(QT_OPENGL_ES_2) + QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(!obj1.testOption(QGL::SampleBuffers)); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); +#else + QCOMPARE(true, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::SampleBuffers)); + QVERIFY(!obj1.testOption(QGL::NoSampleBuffers)); +#endif + obj1.setSampleBuffers(false); + QCOMPARE(false, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::NoSampleBuffers)); + obj1.setSampleBuffers(true); + QCOMPARE(true, obj1.sampleBuffers()); + QVERIFY(obj1.testOption(QGL::SampleBuffers)); + + // int QGLFormat::samples() + // void QGLFormat::setSamples(int) + QCOMPARE(-1, obj1.samples()); + obj1.setSamples(0); + QCOMPARE(0, obj1.samples()); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -2147483648"); + obj1.setSamples(TEST_INT_MIN); + QCOMPARE(0, obj1.samples()); // Makes no sense with a negative sample size + obj1.setSamples(3); + QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setSamples: Cannot have negative number of samples per pixel -1"); + obj1.setSamples(-1); + QCOMPARE(3, obj1.samples()); + obj1.setSamples(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.samples()); + + // int QGLFormat::swapInterval() + // void QGLFormat::setSwapInterval(int) + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(0); + QCOMPARE(0, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.swapInterval()); + obj1.setSwapInterval(-1); + QCOMPARE(-1, obj1.swapInterval()); + obj1.setSwapInterval(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.swapInterval()); + + // bool QGLFormat::doubleBuffer() + // void QGLFormat::setDoubleBuffer(bool) + QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); + obj1.setDoubleBuffer(false); + QCOMPARE(false, obj1.doubleBuffer()); + QVERIFY(!obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(obj1.testOption(QGL::SingleBuffer)); + obj1.setDoubleBuffer(true); + QCOMPARE(true, obj1.doubleBuffer()); + QVERIFY(obj1.testOption(QGL::DoubleBuffer)); + QVERIFY(!obj1.testOption(QGL::SingleBuffer)); + + // bool QGLFormat::depth() + // void QGLFormat::setDepth(bool) + QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); + obj1.setDepth(false); + QCOMPARE(false, obj1.depth()); + QVERIFY(!obj1.testOption(QGL::DepthBuffer)); + QVERIFY(obj1.testOption(QGL::NoDepthBuffer)); + obj1.setDepth(true); + QCOMPARE(true, obj1.depth()); + QVERIFY(obj1.testOption(QGL::DepthBuffer)); + QVERIFY(!obj1.testOption(QGL::NoDepthBuffer)); + + // bool QGLFormat::rgba() + // void QGLFormat::setRgba(bool) + QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); + obj1.setRgba(false); + QCOMPARE(false, obj1.rgba()); + QVERIFY(!obj1.testOption(QGL::Rgba)); + QVERIFY(obj1.testOption(QGL::ColorIndex)); + obj1.setRgba(true); + QCOMPARE(true, obj1.rgba()); + QVERIFY(obj1.testOption(QGL::Rgba)); + QVERIFY(!obj1.testOption(QGL::ColorIndex)); + + // bool QGLFormat::alpha() + // void QGLFormat::setAlpha(bool) + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlpha(false); + QCOMPARE(false, obj1.alpha()); + QVERIFY(!obj1.testOption(QGL::AlphaChannel)); + QVERIFY(obj1.testOption(QGL::NoAlphaChannel)); + obj1.setAlpha(true); + QCOMPARE(true, obj1.alpha()); + QVERIFY(obj1.testOption(QGL::AlphaChannel)); + QVERIFY(!obj1.testOption(QGL::NoAlphaChannel)); + + // bool QGLFormat::accum() + // void QGLFormat::setAccum(bool) + QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); + obj1.setAccum(false); + QCOMPARE(false, obj1.accum()); + QVERIFY(!obj1.testOption(QGL::AccumBuffer)); + QVERIFY(obj1.testOption(QGL::NoAccumBuffer)); + obj1.setAccum(true); + QCOMPARE(true, obj1.accum()); + QVERIFY(obj1.testOption(QGL::AccumBuffer)); + QVERIFY(!obj1.testOption(QGL::NoAccumBuffer)); + + // bool QGLFormat::stencil() + // void QGLFormat::setStencil(bool) + QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); + obj1.setStencil(false); + QCOMPARE(false, obj1.stencil()); + QVERIFY(!obj1.testOption(QGL::StencilBuffer)); + QVERIFY(obj1.testOption(QGL::NoStencilBuffer)); + obj1.setStencil(true); + QCOMPARE(true, obj1.stencil()); + QVERIFY(obj1.testOption(QGL::StencilBuffer)); + QVERIFY(!obj1.testOption(QGL::NoStencilBuffer)); + + // bool QGLFormat::stereo() + // void QGLFormat::setStereo(bool) + QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); + obj1.setStereo(false); + QCOMPARE(false, obj1.stereo()); + QVERIFY(!obj1.testOption(QGL::StereoBuffers)); + QVERIFY(obj1.testOption(QGL::NoStereoBuffers)); + obj1.setStereo(true); + QCOMPARE(true, obj1.stereo()); + QVERIFY(obj1.testOption(QGL::StereoBuffers)); + QVERIFY(!obj1.testOption(QGL::NoStereoBuffers)); + + // bool QGLFormat::directRendering() + // void QGLFormat::setDirectRendering(bool) + QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); + obj1.setDirectRendering(false); + QCOMPARE(false, obj1.directRendering()); + QVERIFY(!obj1.testOption(QGL::DirectRendering)); + QVERIFY(obj1.testOption(QGL::IndirectRendering)); + obj1.setDirectRendering(true); + QCOMPARE(true, obj1.directRendering()); + QVERIFY(obj1.testOption(QGL::DirectRendering)); + QVERIFY(!obj1.testOption(QGL::IndirectRendering)); + + // bool QGLFormat::overlay() + // void QGLFormat::setOverlay(bool) + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(false); + QCOMPARE(false, obj1.hasOverlay()); + QVERIFY(!obj1.testOption(QGL::HasOverlay)); + QVERIFY(obj1.testOption(QGL::NoOverlay)); + obj1.setOverlay(true); + QCOMPARE(true, obj1.hasOverlay()); + QVERIFY(obj1.testOption(QGL::HasOverlay)); + QVERIFY(!obj1.testOption(QGL::NoOverlay)); + + // int QGLFormat::plane() + // void QGLFormat::setPlane(int) + QCOMPARE(0, obj1.plane()); + obj1.setPlane(0); + QCOMPARE(0, obj1.plane()); + obj1.setPlane(TEST_INT_MIN); + QCOMPARE(TEST_INT_MIN, obj1.plane()); + obj1.setPlane(TEST_INT_MAX); + QCOMPARE(TEST_INT_MAX, obj1.plane()); + + // operator== and operator!= for QGLFormat + QGLFormat format1; + QGLFormat format2; + + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + format1.setDoubleBuffer(false); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setDoubleBuffer(false); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setDepthBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setDepthBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setAccumBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setAccumBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setRedBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setRedBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setGreenBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setGreenBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setBlueBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setBlueBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setAlphaBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setAlphaBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setStencilBufferSize(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setStencilBufferSize(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setSamples(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setSamples(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setSwapInterval(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setSwapInterval(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + format1.setPlane(8); + QVERIFY(!(format1 == format2)); + QVERIFY(format1 != format2); + format2.setPlane(8); + QVERIFY(format1 == format2); + QVERIFY(!(format1 != format2)); + + // Copy constructor and assignment for QGLFormat. + QGLFormat format3(format1); + QGLFormat format4; + QVERIFY(format1 == format3); + QVERIFY(format1 != format4); + format4 = format1; + QVERIFY(format1 == format4); + + // Check that modifying a copy doesn't affect the original. + format3.setRedBufferSize(16); + format4.setPlane(16); + QCOMPARE(format1.redBufferSize(), 8); + QCOMPARE(format1.plane(), 8); + + // Check the QGLFormat constructor that takes an option list. + QGLFormat format5 + (QGL::DepthBuffer | QGL::StereoBuffers | QGL::ColorIndex, 3); + QVERIFY(format5.depth()); + QVERIFY(format5.stereo()); + QVERIFY(format5.doubleBuffer()); // From defaultFormat() + QVERIFY(!format5.hasOverlay()); // From defaultFormat() + QVERIFY(!format5.rgba()); + QCOMPARE(format5.plane(), 3); + + // The default format should be the same as QGLFormat(). + QVERIFY(QGLFormat::defaultFormat() == QGLFormat()); + + // Modify the default format and check that it was changed. + QGLFormat::setDefaultFormat(format1); + QVERIFY(QGLFormat::defaultFormat() == format1); + + // Restore the default format. + QGLFormat::setDefaultFormat(QGLFormat()); + QVERIFY(QGLFormat::defaultFormat() == QGLFormat()); + + // Check the default overlay format's expected values. + QGLFormat overlay(QGLFormat::defaultOverlayFormat()); + QCOMPARE(overlay.depthBufferSize(), -1); + QCOMPARE(overlay.accumBufferSize(), -1); + QCOMPARE(overlay.redBufferSize(), -1); + QCOMPARE(overlay.greenBufferSize(), -1); + QCOMPARE(overlay.blueBufferSize(), -1); + QCOMPARE(overlay.alphaBufferSize(), -1); + QCOMPARE(overlay.samples(), -1); + QCOMPARE(overlay.swapInterval(), -1); + QCOMPARE(overlay.plane(), 1); + QVERIFY(!overlay.sampleBuffers()); + QVERIFY(!overlay.doubleBuffer()); + QVERIFY(!overlay.depth()); + QVERIFY(!overlay.rgba()); + QVERIFY(!overlay.alpha()); + QVERIFY(!overlay.accum()); + QVERIFY(!overlay.stencil()); + QVERIFY(!overlay.stereo()); + QVERIFY(overlay.directRendering()); // Only option that should be on. + QVERIFY(!overlay.hasOverlay()); // Overlay doesn't need an overlay! + + // Modify the default overlay format and check that it was changed. + QGLFormat::setDefaultOverlayFormat(format1); + QVERIFY(QGLFormat::defaultOverlayFormat() == format1); + + // Restore the default overlay format. + QGLFormat::setDefaultOverlayFormat(overlay); + QVERIFY(QGLFormat::defaultOverlayFormat() == overlay); + + MyGLContext obj2(obj1); + // bool QGLContext::windowCreated() + // void QGLContext::setWindowCreated(bool) + obj2.setWindowCreated(false); + QCOMPARE(false, obj2.windowCreated()); + obj2.setWindowCreated(true); + QCOMPARE(true, obj2.windowCreated()); + + // bool QGLContext::initialized() + // void QGLContext::setInitialized(bool) + obj2.setInitialized(false); + QCOMPARE(false, obj2.initialized()); + obj2.setInitialized(true); + QCOMPARE(true, obj2.initialized()); + + MyGLWidget obj3; + // bool QGLWidget::autoBufferSwap() + // void QGLWidget::setAutoBufferSwap(bool) + obj3.setAutoBufferSwap(false); + QCOMPARE(false, obj3.autoBufferSwap()); + obj3.setAutoBufferSwap(true); + QCOMPARE(true, obj3.autoBufferSwap()); +} + +#ifdef QT_BUILD_INTERNAL +QT_BEGIN_NAMESPACE +extern QGLFormat::OpenGLVersionFlags qOpenGLVersionFlagsFromString(const QString &versionString); +QT_END_NAMESPACE +#endif + +void tst_QGL::openGLVersionCheck() +{ +#ifdef QT_BUILD_INTERNAL + if (!QGLFormat::hasOpenGL()) + QSKIP("QGL not supported on this platform", SkipAll); + + QString versionString; + QGLFormat::OpenGLVersionFlags expectedFlag; + QGLFormat::OpenGLVersionFlags versionFlag; + + versionString = "1.1 Irix 6.5"; + expectedFlag = QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.2 Microsoft"; + expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.2.1"; + expectedFlag = QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.3 NVIDIA"; + expectedFlag = QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.4"; + expectedFlag = QGLFormat::OpenGL_Version_1_4 | QGLFormat::OpenGL_Version_1_3 | QGLFormat::OpenGL_Version_1_2 | QGLFormat::OpenGL_Version_1_1; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "1.5 NVIDIA"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.0.2 NVIDIA 87.62"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.1 NVIDIA"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "2.1"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CM 1.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_Common_Version_1_0 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CL 1.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CM 1.1 ATI"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES-CL 1.1 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_CommonLite_Version_1_1 | QGLFormat::OpenGL_ES_CommonLite_Version_1_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "OpenGL ES 2.0 ATI"; + expectedFlag = QGLFormat::OpenGL_ES_Version_2_0; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + versionString = "3.0"; + 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; + versionFlag = qOpenGLVersionFlagsFromString(versionString); + QCOMPARE(versionFlag, expectedFlag); + + QGLWidget glWidget; + glWidget.show(); + glWidget.makeCurrent(); + + // This is unfortunately the only test we can make on the actual openGLVersionFlags() + // However, the complicated parts are in openGLVersionFlags(const QString &versionString) + // tested above + +#if defined(QT_OPENGL_ES_1) + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Common_Version_1_0); +#elif defined(QT_OPENGL_ES_1_CL) + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_CommonLite_Version_1_0); +#elif defined(QT_OPENGL_ES_2) + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0); +#else + QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1); +#endif //defined(QT_OPENGL_ES_1) +#endif //QT_BUILD_INTERNAL +} + +class UnclippedWidget : public QWidget +{ +public: + void paintEvent(QPaintEvent *) + { + QPainter p(this); + p.fillRect(rect().adjusted(-1000, -1000, 1000, 1000), Qt::black); + } +}; + +void tst_QGL::graphicsViewClipping() +{ + const int size = 64; + UnclippedWidget *widget = new UnclippedWidget; + widget->setFixedSize(size, size); + + QGraphicsScene scene; + + scene.addWidget(widget)->setPos(0, 0); + + QGraphicsView view(&scene); + view.resize(2*size, 2*size); + + QGLWidget *viewport = new QGLWidget; + view.setViewport(viewport); + view.show(); + + if (!viewport->isValid()) + return; + + scene.setSceneRect(view.viewport()->rect()); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&view); +#endif + QTest::qWait(500); + + QImage image = viewport->grabFrameBuffer(); + QImage expected = image; + + QPainter p(&expected); + p.fillRect(expected.rect(), Qt::white); + p.fillRect(QRect(0, 0, size, size), Qt::black); + p.end(); + + QCOMPARE(image, expected); +} + +void tst_QGL::partialGLWidgetUpdates_data() +{ + QTest::addColumn("doubleBufferedContext"); + QTest::addColumn("autoFillBackground"); + QTest::addColumn("supportsPartialUpdates"); + + QTest::newRow("Double buffered context") << true << true << false; + QTest::newRow("Double buffered context without auto-fill background") << true << false << false; + QTest::newRow("Single buffered context") << false << true << false; + QTest::newRow("Single buffered context without auto-fill background") << false << false << true; +} + +void tst_QGL::partialGLWidgetUpdates() +{ + if (!QGLFormat::hasOpenGL()) + QSKIP("QGL not supported on this platform", SkipAll); + + QFETCH(bool, doubleBufferedContext); + QFETCH(bool, autoFillBackground); + QFETCH(bool, supportsPartialUpdates); + + class MyGLWidget : public QGLWidget + { + public: + QRegion paintEventRegion; + void paintEvent(QPaintEvent *e) + { + paintEventRegion = e->region(); + } + }; + + QGLFormat format = QGLFormat::defaultFormat(); + format.setDoubleBuffer(doubleBufferedContext); + QGLFormat::setDefaultFormat(format); + + MyGLWidget widget; + widget.setFixedSize(150, 150); + widget.setAutoFillBackground(autoFillBackground); + widget.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); +#endif + QTest::qWait(200); + + if (widget.format().doubleBuffer() != doubleBufferedContext) + QSKIP("Platform does not support requested format", SkipAll); + + widget.paintEventRegion = QRegion(); + widget.repaint(50, 50, 50, 50); +#ifdef Q_WS_MAC + // repaint() is not immediate on the Mac; it has to go through the event loop. + QTest::qWait(200); +#endif + if (supportsPartialUpdates) + QCOMPARE(widget.paintEventRegion, QRegion(50, 50, 50, 50)); + else + QCOMPARE(widget.paintEventRegion, QRegion(widget.rect())); +} + + +// This tests that rendering to a QGLPBuffer using QPainter works. +void tst_QGL::glPBufferRendering() +{ + if (!QGLPixelBuffer::hasOpenGLPbuffers()) + QSKIP("QGLPixelBuffer not supported on this platform", SkipSingle); + + QGLPixelBuffer* pbuf = new QGLPixelBuffer(128, 128); + + QPainter p; + bool begun = p.begin(pbuf); + QVERIFY(begun); + + QPaintEngine::Type engineType = p.paintEngine()->type(); + QVERIFY(engineType == QPaintEngine::OpenGL || engineType == QPaintEngine::OpenGL2); + + p.fillRect(0, 0, 128, 128, Qt::red); + p.fillRect(32, 32, 64, 64, Qt::blue); + p.end(); + + QImage fb = pbuf->toImage(); + delete pbuf; + + QImage reference(128, 128, fb.format()); + p.begin(&reference); + p.fillRect(0, 0, 128, 128, Qt::red); + p.fillRect(32, 32, 64, 64, Qt::blue); + p.end(); + + QCOMPARE(fb, reference); +} + +class GLWidget : public QGLWidget +{ +public: + GLWidget(QWidget* p = 0) + : QGLWidget(p), beginOk(false), engineType(QPaintEngine::MaxUser) {} + bool beginOk; + QPaintEngine::Type engineType; + void paintGL() + { + QPainter p; + beginOk = p.begin(this); + QPaintEngine* pe = p.paintEngine(); + engineType = pe->type(); + + // This test only ensures it's possible to paint onto a QGLWidget. Full + // paint engine feature testing is way out of scope! + + p.fillRect(0, 0, width(), height(), Qt::red); + // No p.end() or swap buffers, should be done automatically + } + +}; + +void tst_QGL::glWidgetRendering() +{ + GLWidget w; + w.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&w); +#endif + QTest::qWait(200); + + QVERIFY(w.beginOk); + QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2); + + QImage fb = w.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + QImage reference(fb.size(), QImage::Format_RGB32); + reference.fill(0xffff0000); + + QCOMPARE(fb, reference); +} + +// NOTE: This tests that CombinedDepthStencil attachment works by assuming the +// GL2 engine is being used and is implemented the same way as it was when +// this autotest was written. If this is not the case, there may be some +// false-positives: I.e. The test passes when either the depth or stencil +// buffer is actually missing. But that's probably ok anyway. +void tst_QGL::glFBORendering() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); + + QGLWidget glw; + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + // Don't complicate things by using NPOT: + QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat); + + QPainter fboPainter; + bool painterBegun = fboPainter.begin(fbo); + QVERIFY(painterBegun); + + QPainterPath intersectingPath; + intersectingPath.moveTo(0, 0); + intersectingPath.lineTo(100, 0); + intersectingPath.lineTo(0, 100); + intersectingPath.lineTo(100, 100); + intersectingPath.closeSubpath(); + + QPainterPath trianglePath; + trianglePath.moveTo(50, 0); + trianglePath.lineTo(100, 100); + trianglePath.lineTo(0, 100); + trianglePath.closeSubpath(); + + fboPainter.fillRect(0, 0, fbo->width(), fbo->height(), Qt::red); // Background + fboPainter.translate(14, 14); + fboPainter.fillPath(intersectingPath, Qt::blue); // Test stencil buffer works + fboPainter.translate(128, 0); + fboPainter.setClipPath(trianglePath); // Test depth buffer works + fboPainter.setTransform(QTransform()); // reset xform + fboPainter.fillRect(0, 0, fbo->width(), fbo->height(), Qt::green); + fboPainter.end(); + + QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32); + delete fbo; + + // As we're doing more than trivial painting, we can't just compare to + // an image rendered with raster. Instead, we sample at well-defined + // test-points: + QCOMPARE(fb.pixel(39, 64), QColor(Qt::red).rgb()); + QCOMPARE(fb.pixel(89, 64), QColor(Qt::red).rgb()); + QCOMPARE(fb.pixel(64, 39), QColor(Qt::blue).rgb()); + QCOMPARE(fb.pixel(64, 89), QColor(Qt::blue).rgb()); + + QCOMPARE(fb.pixel(167, 39), QColor(Qt::red).rgb()); + QCOMPARE(fb.pixel(217, 39), QColor(Qt::red).rgb()); + QCOMPARE(fb.pixel(192, 64), QColor(Qt::green).rgb()); +} + + +// Tests multiple QPainters active on different FBOs at the same time, with +// interleaving painting. Performance-wise, this is sub-optimal, but it still +// has to work flawlessly +void tst_QGL::multipleFBOInterleavedRendering() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); + + QGLWidget glw; + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + QGLFramebufferObject *fbo1 = new QGLFramebufferObject(256, 128, fboFormat); + QGLFramebufferObject *fbo2 = new QGLFramebufferObject(256, 128, fboFormat); + QGLFramebufferObject *fbo3 = new QGLFramebufferObject(256, 128, fboFormat); + + QPainter fbo1Painter; + QPainter fbo2Painter; + QPainter fbo3Painter; + + QVERIFY(fbo1Painter.begin(fbo1)); + QVERIFY(fbo2Painter.begin(fbo2)); + QVERIFY(fbo3Painter.begin(fbo3)); + + // Confirm we're using the GL2 engine, as interleaved rendering isn't supported + // on the GL1 engine: + if (fbo1Painter.paintEngine()->type() != QPaintEngine::OpenGL2) + QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher", SkipSingle); + + QPainterPath intersectingPath; + intersectingPath.moveTo(0, 0); + intersectingPath.lineTo(100, 0); + intersectingPath.lineTo(0, 100); + intersectingPath.lineTo(100, 100); + intersectingPath.closeSubpath(); + + QPainterPath trianglePath; + trianglePath.moveTo(50, 0); + trianglePath.lineTo(100, 100); + trianglePath.lineTo(0, 100); + trianglePath.closeSubpath(); + + fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::red); // Background + fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::green); // Background + fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::blue); // Background + + fbo1Painter.translate(14, 14); + fbo2Painter.translate(14, 14); + fbo3Painter.translate(14, 14); + + fbo1Painter.fillPath(intersectingPath, Qt::blue); // Test stencil buffer works + fbo2Painter.fillPath(intersectingPath, Qt::red); // Test stencil buffer works + fbo3Painter.fillPath(intersectingPath, Qt::green); // Test stencil buffer works + + fbo1Painter.translate(128, 0); + fbo2Painter.translate(128, 0); + fbo3Painter.translate(128, 0); + + fbo1Painter.setClipPath(trianglePath); + fbo2Painter.setClipPath(trianglePath); + fbo3Painter.setClipPath(trianglePath); + + fbo1Painter.setTransform(QTransform()); // reset xform + fbo2Painter.setTransform(QTransform()); // reset xform + fbo3Painter.setTransform(QTransform()); // reset xform + + fbo1Painter.fillRect(0, 0, fbo1->width(), fbo1->height(), Qt::green); + fbo2Painter.fillRect(0, 0, fbo2->width(), fbo2->height(), Qt::blue); + fbo3Painter.fillRect(0, 0, fbo3->width(), fbo3->height(), Qt::red); + + fbo1Painter.end(); + fbo2Painter.end(); + fbo3Painter.end(); + + QImage fb1 = fbo1->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb2 = fbo2->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb3 = fbo3->toImage().convertToFormat(QImage::Format_RGB32); + delete fbo1; + delete fbo2; + delete fbo3; + + // As we're doing more than trivial painting, we can't just compare to + // an image rendered with raster. Instead, we sample at well-defined + // test-points: + QCOMPARE(fb1.pixel(39, 64), QColor(Qt::red).rgb()); + QCOMPARE(fb1.pixel(89, 64), QColor(Qt::red).rgb()); + QCOMPARE(fb1.pixel(64, 39), QColor(Qt::blue).rgb()); + QCOMPARE(fb1.pixel(64, 89), QColor(Qt::blue).rgb()); + QCOMPARE(fb1.pixel(167, 39), QColor(Qt::red).rgb()); + QCOMPARE(fb1.pixel(217, 39), QColor(Qt::red).rgb()); + QCOMPARE(fb1.pixel(192, 64), QColor(Qt::green).rgb()); + + QCOMPARE(fb2.pixel(39, 64), QColor(Qt::green).rgb()); + QCOMPARE(fb2.pixel(89, 64), QColor(Qt::green).rgb()); + QCOMPARE(fb2.pixel(64, 39), QColor(Qt::red).rgb()); + QCOMPARE(fb2.pixel(64, 89), QColor(Qt::red).rgb()); + QCOMPARE(fb2.pixel(167, 39), QColor(Qt::green).rgb()); + QCOMPARE(fb2.pixel(217, 39), QColor(Qt::green).rgb()); + QCOMPARE(fb2.pixel(192, 64), QColor(Qt::blue).rgb()); + + QCOMPARE(fb3.pixel(39, 64), QColor(Qt::blue).rgb()); + QCOMPARE(fb3.pixel(89, 64), QColor(Qt::blue).rgb()); + QCOMPARE(fb3.pixel(64, 39), QColor(Qt::green).rgb()); + QCOMPARE(fb3.pixel(64, 89), QColor(Qt::green).rgb()); + QCOMPARE(fb3.pixel(167, 39), QColor(Qt::blue).rgb()); + QCOMPARE(fb3.pixel(217, 39), QColor(Qt::blue).rgb()); + QCOMPARE(fb3.pixel(192, 64), QColor(Qt::red).rgb()); +} + +class FBOUseInGLWidget : public QGLWidget +{ +public: + bool widgetPainterBeginOk; + bool fboPainterBeginOk; + QImage fboImage; +protected: + void paintEvent(QPaintEvent*) + { + QPainter widgetPainter; + widgetPainterBeginOk = widgetPainter.begin(this); + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat); + + QPainter fboPainter; + fboPainterBeginOk = fboPainter.begin(fbo); + fboPainter.fillRect(0, 0, 128, 128, Qt::red); + fboPainter.end(); + fboImage = fbo->toImage(); + + widgetPainter.fillRect(rect(), Qt::blue); + + delete fbo; + } + +}; + +void tst_QGL::glFBOUseInGLWidget() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); + + FBOUseInGLWidget w; + w.resize(128, 128); + w.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&w); +#endif + QTest::qWait(200); + + QVERIFY(w.widgetPainterBeginOk); + QVERIFY(w.fboPainterBeginOk); + + QImage widgetFB = w.grabFrameBuffer(false); + QImage widgetReference(widgetFB.size(), widgetFB.format()); + widgetReference.fill(0xff0000ff); + QCOMPARE(widgetFB, widgetReference); + + QImage fboReference(w.fboImage.size(), w.fboImage.format()); + fboReference.fill(0xffff0000); + QCOMPARE(w.fboImage, fboReference); +} + +void tst_QGL::glWidgetReparent() +{ + // Try it as a top-level first: + GLWidget *widget = new GLWidget; + widget->setGeometry(0, 0, 200, 30); + widget->show(); + + QWidget grandParentWidget; + grandParentWidget.setPalette(Qt::blue); + QVBoxLayout grandParentLayout(&grandParentWidget); + + QWidget parentWidget(&grandParentWidget); + grandParentLayout.addWidget(&parentWidget); + parentWidget.setPalette(Qt::green); + parentWidget.setAutoFillBackground(true); + QVBoxLayout parentLayout(&parentWidget); + + grandParentWidget.setGeometry(0, 100, 200, 200); + grandParentWidget.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(widget); + qt_x11_wait_for_window_manager(&parentWidget); +#endif + QTest::qWait(200); + + QVERIFY(parentWidget.children().count() == 1); // The layout + + // Now both widgets should be created & shown, time to re-parent: + parentLayout.addWidget(widget); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&parentWidget); +#endif + QTest::qWait(200); + + QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget + QVERIFY(parentWidget.children().contains(widget)); + QVERIFY(widget->height() > 30); + + delete widget; + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&parentWidget); +#endif + QTest::qWait(200); + + QVERIFY(parentWidget.children().count() == 1); // The layout + + // Now do pretty much the same thing, but don't show the + // widget first: + widget = new GLWidget; + parentLayout.addWidget(widget); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&parentWidget); +#endif + QTest::qWait(200); + + QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget + QVERIFY(parentWidget.children().contains(widget)); + QVERIFY(widget->height() > 30); + + delete widget; +} + +class RenderPixmapWidget : public QGLWidget +{ +protected: + void initializeGL() { + // Set some gl state: + glClearColor(1.0, 0.0, 0.0, 1.0); + } + + void paintGL() { + glClear(GL_COLOR_BUFFER_BIT); + } +}; + +void tst_QGL::glWidgetRenderPixmap() +{ + RenderPixmapWidget *w = new RenderPixmapWidget; + + QPixmap pm = w->renderPixmap(100, 100, false); + + delete w; + + QImage fb = pm.toImage().convertToFormat(QImage::Format_RGB32); + QImage reference(fb.size(), QImage::Format_RGB32); + reference.fill(0xffff0000); + + QCOMPARE(fb, reference); +} + + +// When using multiple FBOs at the same time, unbinding one FBO should re-bind the +// previous. I.e. It should be possible to have a stack of FBOs where pop'ing there +// top re-binds the one underneeth. +void tst_QGL::stackedFBOs() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); + + QGLWidget glw; + glw.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&glw); +#endif + QTest::qWait(200); + + glw.makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + // Don't complicate things by using NPOT: + QGLFramebufferObject *fbo1 = new QGLFramebufferObject(128, 128, fboFormat); + QGLFramebufferObject *fbo2 = new QGLFramebufferObject(128, 128, fboFormat); + QGLFramebufferObject *fbo3 = new QGLFramebufferObject(128, 128, fboFormat); + + glClearColor(1.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + fbo1->bind(); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + fbo2->bind(); + glClearColor(0.0, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + fbo3->bind(); + glClearColor(0.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(32, 32, 64, 64); + glEnable(GL_SCISSOR_TEST); + glClearColor(0.0, 1.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + fbo3->release(); + + // Scissor rect & test should be left untouched by the fbo release... + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + fbo2->release(); + + glClearColor(1.0, 1.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + fbo1->release(); + + glClearColor(1.0, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glw.swapBuffers(); + + QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + QImage fb1 = fbo1->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb2 = fbo2->toImage().convertToFormat(QImage::Format_RGB32); + QImage fb3 = fbo3->toImage().convertToFormat(QImage::Format_RGB32); + + delete fbo1; + delete fbo2; + delete fbo3; + + QImage widgetReference(widgetFB.size(), widgetFB.format()); + QImage fb1Reference(fb1.size(), fb1.format()); + QImage fb2Reference(fb2.size(), fb2.format()); + QImage fb3Reference(fb3.size(), fb3.format()); + + QPainter widgetReferencePainter(&widgetReference); + QPainter fb1ReferencePainter(&fb1Reference); + QPainter fb2ReferencePainter(&fb2Reference); + QPainter fb3ReferencePainter(&fb3Reference); + + widgetReferencePainter.fillRect(0, 0, widgetReference.width(), widgetReference.height(), Qt::magenta); + fb1ReferencePainter.fillRect(0, 0, fb1Reference.width(), fb1Reference.height(), Qt::red); + fb2ReferencePainter.fillRect(0, 0, fb2Reference.width(), fb2Reference.height(), Qt::green); + fb3ReferencePainter.fillRect(0, 0, fb3Reference.width(), fb3Reference.height(), Qt::blue); + + // Flip y-coords to match GL for the widget (which can be any size) + widgetReferencePainter.fillRect(32, glw.height() - 96, 64, 64, Qt::yellow); + fb1ReferencePainter.fillRect(32, 32, 64, 64, Qt::white); + fb2ReferencePainter.fillRect(32, 32, 64, 64, Qt::black); + fb3ReferencePainter.fillRect(32, 32, 64, 64, Qt::cyan); + + widgetReferencePainter.end(); + fb1ReferencePainter.end(); + fb2ReferencePainter.end(); + fb3ReferencePainter.end(); + + QCOMPARE(widgetFB, widgetReference); + QCOMPARE(fb1, fb1Reference); + QCOMPARE(fb2, fb2Reference); + QCOMPARE(fb3, fb3Reference); +} + + +class ColormapExtended : public QGLColormap +{ +public: + ColormapExtended() {} + + Qt::HANDLE handle() { return QGLColormap::handle(); } + void setHandle(Qt::HANDLE handle) { QGLColormap::setHandle(handle); } +}; + +void tst_QGL::colormap() +{ + // Check the properties of the default empty colormap. + QGLColormap cmap1; + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 0); + QVERIFY(cmap1.entryRgb(0) == 0); + QVERIFY(cmap1.entryRgb(-1) == 0); + QVERIFY(cmap1.entryRgb(100) == 0); + QVERIFY(!cmap1.entryColor(0).isValid()); + QVERIFY(!cmap1.entryColor(-1).isValid()); + QVERIFY(!cmap1.entryColor(100).isValid()); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), -1); + + // Set an entry and re-test. + cmap1.setEntry(56, qRgb(255, 0, 0)); + // The colormap is still considered "empty" even though it + // has entries in it now. The isEmpty() method is used to + // detect when the colormap is in use by a GL widget, + // not to detect when it is empty! + QVERIFY(cmap1.isEmpty()); + QCOMPARE(cmap1.size(), 256); + QVERIFY(cmap1.entryRgb(0) == 0); + QVERIFY(cmap1.entryColor(0) == QColor(0, 0, 0, 255)); + QVERIFY(cmap1.entryRgb(56) == qRgb(255, 0, 0)); + QVERIFY(cmap1.entryColor(56) == QColor(255, 0, 0, 255)); + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + + // Set some more entries. + static QRgb const colors[] = { + qRgb(255, 0, 0), + qRgb(0, 255, 0), + qRgb(255, 255, 255), + qRgb(0, 0, 255), + qRgb(0, 0, 0) + }; + cmap1.setEntry(57, QColor(0, 255, 0)); + cmap1.setEntries(3, colors + 2, 58); + cmap1.setEntries(5, colors, 251); + int idx; + for (idx = 0; idx < 5; ++idx) { + QVERIFY(cmap1.entryRgb(56 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(56 + idx) == QColor(colors[idx])); + QVERIFY(cmap1.entryRgb(251 + idx) == colors[idx]); + QVERIFY(cmap1.entryColor(251 + idx) == QColor(colors[idx])); + } + QCOMPARE(cmap1.size(), 256); + + // Perform color lookups. + QCOMPARE(cmap1.find(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.find(qRgb(0, 0, 0)), 60); // Actually finds 0, 0, 0, 255. + QCOMPARE(cmap1.find(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.find(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.find(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.find(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.find(qRgb(140, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 140, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 140)), -1); + QCOMPARE(cmap1.find(qRgb(64, 0, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 64, 0)), -1); + QCOMPARE(cmap1.find(qRgb(0, 0, 64)), -1); + QCOMPARE(cmap1.findNearest(qRgb(255, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 0)), 60); + QCOMPARE(cmap1.findNearest(qRgba(0, 0, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 255, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(255, 255, 255)), 58); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 255)), 59); + QCOMPARE(cmap1.findNearest(qRgb(140, 0, 0)), 56); + QCOMPARE(cmap1.findNearest(qRgb(0, 140, 0)), 57); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 140)), 59); + QCOMPARE(cmap1.findNearest(qRgb(64, 0, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 64, 0)), 0); + QCOMPARE(cmap1.findNearest(qRgb(0, 0, 64)), 0); + + // Make some copies of the colormap and check that they are the same. + QGLColormap cmap2(cmap1); + QGLColormap cmap3; + cmap3 = cmap1; + QVERIFY(cmap2.isEmpty()); + QVERIFY(cmap3.isEmpty()); + QCOMPARE(cmap2.size(), 256); + QCOMPARE(cmap3.size(), 256); + for (idx = 0; idx < 256; ++idx) { + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Modify an entry in one of the copies and recheck the original. + cmap2.setEntry(45, qRgb(255, 0, 0)); + for (idx = 0; idx < 256; ++idx) { + if (idx != 45) + QCOMPARE(cmap1.entryRgb(idx), cmap2.entryRgb(idx)); + else + QCOMPARE(cmap2.entryRgb(45), qRgb(255, 0, 0)); + QCOMPARE(cmap1.entryRgb(idx), cmap3.entryRgb(idx)); + } + + // Check that setting the handle will cause isEmpty() to work right. + ColormapExtended cmap4; + cmap4.setEntry(56, qRgb(255, 0, 0)); + QVERIFY(cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); + cmap4.setHandle(Qt::HANDLE(42)); + QVERIFY(cmap4.handle() == Qt::HANDLE(42)); + QVERIFY(!cmap4.isEmpty()); + QCOMPARE(cmap4.size(), 256); +} + +#ifndef QT_OPENGL_ES +#define DEFAULT_FORMAT GL_RGBA8 +#else +#define DEFAULT_FORMAT GL_RGBA +#endif + +#ifndef GL_TEXTURE_3D +#define GL_TEXTURE_3D 0x806F +#endif + +#ifndef GL_RGB16 +#define GL_RGB16 0x8054 +#endif + +void tst_QGL::fboFormat() +{ + // Check the initial conditions. + QGLFramebufferObjectFormat format1; + QCOMPARE(format1.samples(), 0); + QVERIFY(format1.attachment() == QGLFramebufferObject::NoAttachment); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D)); + QCOMPARE(int(format1.internalTextureFormat()), int(DEFAULT_FORMAT)); + + // Modify the values and re-check. + format1.setSamples(8); + format1.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format1.setTextureTarget(GL_TEXTURE_3D); + format1.setInternalTextureFormat(GL_RGB16); + QCOMPARE(format1.samples(), 8); + QVERIFY(format1.attachment() == QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16)); + + // Make copies and check that they are the same. + QGLFramebufferObjectFormat format2(format1); + QGLFramebufferObjectFormat format3; + QCOMPARE(format2.samples(), 8); + QVERIFY(format2.attachment() == QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16)); + format3 = format1; + QCOMPARE(format3.samples(), 8); + QVERIFY(format3.attachment() == QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format3.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format3.internalTextureFormat()), int(GL_RGB16)); + + // Modify the copies and check that the original is unchanged. + format2.setSamples(9); + format3.setTextureTarget(GL_TEXTURE_2D); + QCOMPARE(format1.samples(), 8); + QVERIFY(format1.attachment() == QGLFramebufferObject::CombinedDepthStencil); + QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D)); + QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16)); + + // operator== and operator!= for QGLFramebufferObjectFormat. + QGLFramebufferObjectFormat format1c; + QGLFramebufferObjectFormat format2c; + + QVERIFY(format1c == format2c); + QVERIFY(!(format1c != format2c)); + format1c.setSamples(8); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setSamples(8); + QVERIFY(format1c == format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + QVERIFY(format1c == format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setTextureTarget(GL_TEXTURE_3D); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setTextureTarget(GL_TEXTURE_3D); + QVERIFY(format1c == format2c); + QVERIFY(!(format1c != format2c)); + + format1c.setInternalTextureFormat(GL_RGB16); + QVERIFY(!(format1c == format2c)); + QVERIFY(format1c != format2c); + format2c.setInternalTextureFormat(GL_RGB16); + QVERIFY(format1c == format2c); + QVERIFY(!(format1c != format2c)); + + QGLFramebufferObjectFormat format3c(format1c); + QGLFramebufferObjectFormat format4c; + QVERIFY(format1c == format3c); + QVERIFY(!(format1c != format3c)); + format3c.setInternalTextureFormat(DEFAULT_FORMAT); + QVERIFY(!(format1c == format3c)); + QVERIFY(format1c != format3c); + + format4c = format1c; + QVERIFY(format1c == format4c); + QVERIFY(!(format1c != format4c)); + format4c.setInternalTextureFormat(DEFAULT_FORMAT); + QVERIFY(!(format1c == format4c)); + QVERIFY(format1c != format4c); +} + +void tst_QGL::testDontCrashOnDanglingResources() +{ + // We have a number of Q_GLOBAL_STATICS inside the QtOpenGL + // library. This test is verify that we don't crash as a result of + // them calling into libgl on application shutdown. + QWidget *widget = new UnclippedWidget(); + widget->show(); + qApp->processEvents(); + widget->hide(); +} + +class ReplaceClippingGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(rect(), Qt::white); + + QPainterPath path; + path.addRect(0, 0, 100, 100); + path.addRect(50, 50, 100, 100); + + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + + painter->translate(150, 150); + painter->setClipRect(0, 0, 150, 150); + painter->fillPath(path, Qt::red); + } + +protected: + void paintEvent(QPaintEvent*) + { + // clear the stencil with junk + glStencilMask(0xFFFF); + glClearStencil(0xFFFF); + glDisable(GL_STENCIL_TEST); + glDisable(GL_SCISSOR_TEST); + glClear(GL_STENCIL_BUFFER_BIT); + + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::replaceClipping() +{ + ReplaceClippingGLWidget glw; + glw.resize(300, 300); + glw.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&glw); +#endif + QTest::qWait(200); + + QImage reference(300, 300, QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + QCOMPARE(widgetFB, reference); +} + +class ClipTestGLWidget : public QGLWidget +{ +public: + void paint(QPainter *painter) + { + painter->fillRect(rect(), Qt::white); + painter->setClipRect(10, 10, width()-20, height()-20); + painter->fillRect(rect(), Qt::cyan); + + painter->save(); + painter->setClipRect(10, 10, 100, 100, Qt::IntersectClip); + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + painter->setClipRect(10, 10, 50, 50, Qt::IntersectClip); + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + painter->setClipRect(0, 0, 35, 35, Qt::IntersectClip); + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + painter->setClipRect(60, 10, 50, 50, Qt::ReplaceClip); + painter->fillRect(rect(), Qt::green); + painter->restore(); + + painter->save(); + painter->setClipRect(0, 60, 60, 25, Qt::IntersectClip); + painter->setClipRect(60, 60, 50, 25, Qt::UniteClip); + painter->fillRect(rect(), Qt::yellow); + painter->restore(); + + painter->restore(); + + painter->translate(100, 100); + + { + QPainterPath path; + path.addRect(10, 10, 100, 100); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + + painter->fillRect(rect(), Qt::blue); + + painter->save(); + { + QPainterPath path; + path.addRect(10, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::red); + painter->restore(); + painter->fillRect(0, 0, 40, 40, Qt::white); + painter->save(); + + { + QPainterPath path; + path.addRect(0, 0, 35, 35); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->fillRect(rect(), Qt::black); + painter->restore(); + + painter->fillRect(0, 0, 30, 30, Qt::magenta); + + painter->save(); + { + QPainterPath path; + path.addRect(60, 10, 50, 50); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::ReplaceClip); + } + painter->fillRect(rect(), Qt::green); + painter->restore(); + + painter->save(); + { + QPainterPath path; + path.addRect(0, 60, 60, 25); + path.addRect(10, 10, 10, 10); + painter->setClipPath(path, Qt::IntersectClip); + } + painter->setClipRect(60, 60, 50, 25, Qt::UniteClip); + painter->fillRect(rect(), Qt::yellow); + painter->restore(); + } + +protected: + void paintEvent(QPaintEvent*) + { + QPainter painter(this); + paint(&painter); + } +}; + +void tst_QGL::clipTest() +{ + ClipTestGLWidget glw; + glw.resize(220, 220); + glw.show(); + +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&glw); +#endif + QTest::qWait(200); + + QImage reference(glw.size(), QImage::Format_RGB32); + QPainter referencePainter(&reference); + glw.paint(&referencePainter); + referencePainter.end(); + + const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32); + + QCOMPARE(widgetFB, reference); +} + +void tst_QGL::destroyFBOAfterContext() +{ + if (!QGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QGLFramebufferObject not supported on this platform", SkipSingle); + + QGLWidget *glw = new QGLWidget(); + glw->makeCurrent(); + + // No multisample with combined depth/stencil attachment: + QGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + + // Don't complicate things by using NPOT: + QGLFramebufferObject *fbo = new QGLFramebufferObject(256, 128, fboFormat); + + // The handle should be valid until the context is destroyed. + QVERIFY(fbo->handle() != 0); + QVERIFY(fbo->isValid()); + + delete glw; + + // The handle should now be zero. + QVERIFY(fbo->handle() == 0); + QVERIFY(!fbo->isValid()); + + delete fbo; +} + +#ifdef QT_BUILD_INTERNAL + +class tst_QGLResource : public QObject +{ + Q_OBJECT +public: + tst_QGLResource(QObject *parent = 0) : QObject(parent) {} + ~tst_QGLResource() { ++deletions; } + + static int deletions; +}; + +int tst_QGLResource::deletions = 0; + +static void qt_shared_test_free(void *data) +{ + delete reinterpret_cast(data); +} + +Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_test, (qt_shared_test_free)) + +#endif + +void tst_QGL::shareRegister() +{ +#ifdef QT_BUILD_INTERNAL + QGLShareRegister *shareReg = qgl_share_reg(); + QVERIFY(shareReg != 0); + + // Create a context. + QGLWidget *glw1 = new QGLWidget(); + glw1->makeCurrent(); + + // Nothing should be sharing with glw1's context yet. + QList list; + list = shareReg->shares(glw1->context()); + QCOMPARE(list.size(), 0); + + // Create a guard for the first context. + QGLSharedResourceGuard guard(glw1->context()); + QVERIFY(guard.id() == 0); + guard.setId(3); + QVERIFY(guard.id() == 3); + + // Add a resource to the first context. + tst_QGLResource *res1 = new tst_QGLResource(); + QVERIFY(!qt_shared_test()->value(glw1->context())); + qt_shared_test()->insert(glw1->context(), res1); + QVERIFY(qt_shared_test()->value(glw1->context()) == res1); + + // Create another context that shares with the first. + QGLWidget *glw2 = new QGLWidget(0, glw1); + if (!glw2->isSharing()) { + delete glw2; + delete glw1; + QSKIP("Context sharing is not supported", SkipSingle); + } + QVERIFY(glw1->context() != glw2->context()); + + // Check that the first context's resource is also on the second. + QVERIFY(qt_shared_test()->value(glw1->context()) == res1); + QVERIFY(qt_shared_test()->value(glw2->context()) == res1); + + // Guard should still be the same. + QVERIFY(guard.context() == glw1->context()); + QVERIFY(guard.id() == 3); + + // Now there are two items in the share lists. + list = shareReg->shares(glw1->context()); + QCOMPARE(list.size(), 2); + QVERIFY(list.contains(glw1->context())); + QVERIFY(list.contains(glw2->context())); + list = shareReg->shares(glw2->context()); + QCOMPARE(list.size(), 2); + QVERIFY(list.contains(glw1->context())); + QVERIFY(list.contains(glw2->context())); + + // Check the sharing relationships. + QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(0, glw2->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, 0)); + + // Create a third context, not sharing with the others. + QGLWidget *glw3 = new QGLWidget(); + + // Create a guard on the standalone context. + QGLSharedResourceGuard guard3(glw3->context()); + guard3.setId(5); + + // Add a resource to the third context. + tst_QGLResource *res3 = new tst_QGLResource(); + QVERIFY(!qt_shared_test()->value(glw3->context())); + qt_shared_test()->insert(glw3->context(), res3); + QVERIFY(qt_shared_test()->value(glw1->context()) == res1); + QVERIFY(qt_shared_test()->value(glw2->context()) == res1); + QVERIFY(qt_shared_test()->value(glw3->context()) == res3); + + // First two should still be sharing, but third is in its own list. + list = shareReg->shares(glw1->context()); + QCOMPARE(list.size(), 2); + QVERIFY(list.contains(glw1->context())); + QVERIFY(list.contains(glw2->context())); + list = shareReg->shares(glw2->context()); + QCOMPARE(list.size(), 2); + QVERIFY(list.contains(glw1->context())); + QVERIFY(list.contains(glw2->context())); + list = shareReg->shares(glw3->context()); + QCOMPARE(list.size(), 0); + + // Check the sharing relationships again. + QVERIFY(QGLContext::areSharing(glw1->context(), glw1->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw1->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw2->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(glw2->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), glw1->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), glw2->context())); + QVERIFY(QGLContext::areSharing(glw3->context(), glw3->context())); + QVERIFY(!QGLContext::areSharing(0, glw2->context())); + QVERIFY(!QGLContext::areSharing(glw1->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, glw3->context())); + QVERIFY(!QGLContext::areSharing(glw3->context(), 0)); + QVERIFY(!QGLContext::areSharing(0, 0)); + + // Shared guard should still be the same. + QVERIFY(guard.context() == glw1->context()); + QVERIFY(guard.id() == 3); + + // Delete the first context. + delete glw1; + + // The first context's resource should transfer to the second context. + QCOMPARE(tst_QGLResource::deletions, 0); + QVERIFY(qt_shared_test()->value(glw2->context()) == res1); + QVERIFY(qt_shared_test()->value(glw3->context()) == res3); + + // Shared guard should now be the second context, with the id the same. + QVERIFY(guard.context() == glw2->context()); + QVERIFY(guard.id() == 3); + QVERIFY(guard3.context() == glw3->context()); + QVERIFY(guard3.id() == 5); + + // Re-check the share list for the second context (should be empty now). + list = shareReg->shares(glw2->context()); + QCOMPARE(list.size(), 0); + + // Clean up and check that the resources are properly deleted. + delete glw2; + QCOMPARE(tst_QGLResource::deletions, 1); + delete glw3; + QCOMPARE(tst_QGLResource::deletions, 2); + + // Guards should now be null and the id zero. + QVERIFY(guard.context() == 0); + QVERIFY(guard.id() == 0); + QVERIFY(guard3.context() == 0); + QVERIFY(guard3.id() == 0); +#endif +} + +QTEST_MAIN(tst_QGL) +#include "tst_qgl.moc"