2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the test suite of the Qt Toolkit.
|
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
2012-09-19 12:28:29 +00:00
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-15 12:36:27 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2016-01-15 12:36:27 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
|
|
|
|
#include <qcoreapplication.h>
|
|
|
|
#include <qdebug.h>
|
|
|
|
#include <qgl.h>
|
|
|
|
#include <qglpixelbuffer.h>
|
|
|
|
#include <qglframebufferobject.h>
|
|
|
|
#include <qglcolormap.h>
|
|
|
|
#include <qpaintengine.h>
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
#include <qopenglfunctions.h>
|
2014-10-31 15:56:25 +00:00
|
|
|
#include <qopenglframebufferobject.h>
|
|
|
|
#include <qopenglpaintdevice.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
#include <QGraphicsView>
|
|
|
|
#include <QGraphicsProxyWidget>
|
|
|
|
#include <QVBoxLayout>
|
|
|
|
|
|
|
|
#ifdef QT_BUILD_INTERNAL
|
Expose QPA API under qpa/*
The main reasons for doing this are:
1. _qpa.h end up in the master QtGui include file. QtGui is meant for
userland applications. qpa code is neither binary nor source compatible.
Inadvertant use of QPA api makes the user code binary-incompatible.
2. syncqt creates forwarding headers for non-private header files. This
gives people the impression that this is public API.
As discussed on the mailing list, even though QPA api is internal and subject
to change, it needs to treated differently from private headers since they
will be used by in-qtbase and out-of-qtbase plugins.
This commit does the following:
1. The _qpa in QPA header files is dropped.
2. syncqt now treats any file with qplatform prefix as a special file and
moves it to qpa/ directory. The recommended way of using QPA API in plugins
is: #include <qpa/qplatformfoo.h>. This allows the user include QPA API
from multiple modules (for example, qplatformfoo might be in QtPrintSupport)
3. The user needs to explicitly add QT += <module>-private to get access to
the qpa api.
4. Creates compat headers for the olden style qplatformfoo_qpa.h and QPlatformFoo
includes.
This commit does not change the cpp filenames. This requires a more careful
merging of existing non qpa cpp files and existing cpp files on a case by
case basis. This can be done at anytime.
The following files are not renamed as part of this changed but will be fixed
as part of a future change:
src/gui/kernel/qgenericpluginfactory_qpa.h
src/gui/kernel/qgenericplugin_qpa.h
src/gui/kernel/qwindowsysteminterface_qpa.h
files were renamed using
for x in `find . -name "qplatform*_qpa.h"`; do git mv $x "${x/_qpa.h/.h}"; done
for x in `find . -name "qplatform*_qpa_p.h"`; do git mv $x "${x/_qpa_p.h/_p.h}"; done
includes were renamed using script
for file in `find . -name "*.h" -or -name "*.cpp" -or -name "*.mm"`; do
sed -i -e 's,.*#.*include.*<\(Qt.*/\)\?\(QPlatform.*\)>,#include <qpa/\L\2.h>,g' \
-e 's,.*#.*include.*"\(Qt.*/\)\?\(QPlatform.*\)",#include <qpa/\L\2.h>,g' \
-e 's,.*#.*include.* "\(qplatform.*\)_qpa.h",#include <qpa/\L\1.h>,g' \
-e 's,.*#.*include.*"\(qplatform.*\)_qpa_p.h",#include <qpa/\L\1_p.h>,g' \
-e 's,.*#.*include.*<\(Qt.*/\|Qt.*/private/\|private/\)\?\(qplatform.*\)_qpa\(.*\)>,#include <qpa/\2\3>,g' \
-e 's,.*#.*include.*"\(Qt.*/\|Qt.*/private/\|private/\)\?\(qplatform.*\)_qpa\(.*\)",#include <qpa/\2\3>,g' \
$file
done
Change-Id: I04a350314a45746e3911f54b3b21ad03315afb67
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@nokia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
2012-04-26 23:33:35 +00:00
|
|
|
#include <qpa/qplatformpixmap.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#include <QtOpenGL/private/qgl_p.h>
|
|
|
|
#include <QtGui/private/qimage_p.h>
|
|
|
|
#include <QtGui/private/qimagepixmapcleanuphooks_p.h>
|
2014-03-19 11:52:03 +00:00
|
|
|
#include <QtGui/private/qopenglextensions_p.h>
|
2011-04-27 10:05:43 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
class tst_QGL : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
tst_QGL();
|
|
|
|
virtual ~tst_QGL();
|
|
|
|
|
|
|
|
private slots:
|
2011-10-26 06:12:27 +00:00
|
|
|
void initTestCase();
|
2011-04-27 10:05:43 +00:00
|
|
|
void getSetCheck();
|
2012-09-04 10:40:16 +00:00
|
|
|
#ifdef QT_BUILD_INTERNAL
|
|
|
|
void qglContextDefaultBindTexture();
|
2011-04-27 10:05:43 +00:00
|
|
|
void openGLVersionCheck();
|
2012-09-04 10:40:16 +00:00
|
|
|
void shareRegister();
|
|
|
|
void textureCleanup();
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
void partialGLWidgetUpdates_data();
|
|
|
|
void partialGLWidgetUpdates();
|
|
|
|
void glWidgetWithAlpha();
|
|
|
|
void glWidgetRendering();
|
|
|
|
void glFBOSimpleRendering();
|
|
|
|
void glFBORendering();
|
2014-10-31 15:56:25 +00:00
|
|
|
void currentFboSync();
|
2011-04-27 10:05:43 +00:00
|
|
|
void multipleFBOInterleavedRendering();
|
|
|
|
void glFBOUseInGLWidget();
|
|
|
|
void glPBufferRendering();
|
|
|
|
void glWidgetReparent();
|
|
|
|
void glWidgetRenderPixmap();
|
|
|
|
void colormap();
|
|
|
|
void fboFormat();
|
|
|
|
void testDontCrashOnDanglingResources();
|
|
|
|
void replaceClipping();
|
|
|
|
void clipTest();
|
|
|
|
void destroyFBOAfterContext();
|
|
|
|
void threadImages();
|
|
|
|
void nullRectCrash();
|
Transient QWindows centered; default-constructed geometry
Default-constructed geometry does not mean put the window at 0,0,
and it does not mean center the window on the screen: it means
let the window manager position the window. If the window is
explicitly positioned at 0,0 though, that is a higher priority
than the transient hint; without this change, the transientFor property
had no effect. On X11, transient means use center "gravity" to
make the transient window exactly centered. But the user can still
override the geometry of a transient window, as with any window.
On OSX and Windows, neither transient window functionality nor smart
initial positioning are provided, so a window with no position set
will be centered on the screen, and a transient window will be put
at the center of its transientParent.
Change-Id: I4f5e37480eef5d105e45ffd60362a57f13ec55f5
Task-number: QTBUG-26903
Reviewed-by: Gunnar Sletta <gunnar.sletta@digia.com>
2012-12-10 13:48:19 +00:00
|
|
|
void graphicsViewClipping();
|
2014-03-19 11:52:03 +00:00
|
|
|
void extensions();
|
2011-04-27 10:05:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
tst_QGL::tst_QGL()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
tst_QGL::~tst_QGL()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-10-26 06:12:27 +00:00
|
|
|
void tst_QGL::initTestCase()
|
|
|
|
{
|
|
|
|
QGLWidget glWidget;
|
|
|
|
if (!glWidget.isValid())
|
|
|
|
QSKIP("QGL is not supported on the test system");
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
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); }
|
|
|
|
};
|
|
|
|
|
|
|
|
static int appDefaultDepth()
|
|
|
|
{
|
|
|
|
static int depth = 0;
|
|
|
|
if (depth == 0) {
|
|
|
|
QPixmap pm(1, 1);
|
|
|
|
depth = pm.depth();
|
|
|
|
}
|
|
|
|
return depth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
{
|
|
|
|
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(1);
|
|
|
|
QCOMPARE(true, obj1.alpha()); // setAlphaBufferSize() enables alpha.
|
|
|
|
QCOMPARE(1, obj1.alphaBufferSize());
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setAlphaBufferSize: Cannot set negative alpha buffer size -2147483648");
|
|
|
|
obj1.setAlphaBufferSize(TEST_INT_MIN);
|
|
|
|
QCOMPARE(1, 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(1);
|
|
|
|
QCOMPARE(1, obj1.stencilBufferSize());
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setStencilBufferSize: Cannot set negative stencil buffer size -2147483648");
|
|
|
|
obj1.setStencilBufferSize(TEST_INT_MIN);
|
|
|
|
QCOMPARE(1, 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)
|
|
|
|
QCOMPARE(false, obj1.sampleBuffers());
|
|
|
|
QVERIFY(!obj1.testOption(QGL::SampleBuffers));
|
|
|
|
QVERIFY(obj1.testOption(QGL::NoSampleBuffers));
|
|
|
|
|
|
|
|
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)
|
|
|
|
obj1.setAccumBufferSize(0);
|
|
|
|
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());
|
|
|
|
|
|
|
|
// int QGLFormat::major/minorVersion()
|
|
|
|
// void QGLFormat::setVersion(int, int)
|
2012-10-16 12:58:47 +00:00
|
|
|
QCOMPARE(obj1.majorVersion(), 2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(obj1.minorVersion(), 0);
|
|
|
|
obj1.setVersion(3, 2);
|
|
|
|
QCOMPARE(obj1.majorVersion(), 3);
|
|
|
|
QCOMPARE(obj1.minorVersion(), 2);
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 0.1");
|
|
|
|
obj1.setVersion(0, 1);
|
|
|
|
QCOMPARE(obj1.majorVersion(), 3);
|
|
|
|
QCOMPARE(obj1.minorVersion(), 2);
|
|
|
|
QTest::ignoreMessage(QtWarningMsg, "QGLFormat::setVersion: Cannot set zero or negative version number 3.-1");
|
|
|
|
obj1.setVersion(3, -1);
|
|
|
|
QCOMPARE(obj1.majorVersion(), 3);
|
|
|
|
QCOMPARE(obj1.minorVersion(), 2);
|
|
|
|
obj1.setVersion(TEST_INT_MAX, TEST_INT_MAX - 1);
|
|
|
|
QCOMPARE(obj1.majorVersion(), TEST_INT_MAX);
|
|
|
|
QCOMPARE(obj1.minorVersion(), TEST_INT_MAX - 1);
|
|
|
|
|
|
|
|
|
|
|
|
// operator== and operator!= for QGLFormat
|
|
|
|
QGLFormat format1;
|
|
|
|
QGLFormat format2;
|
|
|
|
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
format1.setDoubleBuffer(false);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setDoubleBuffer(false);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setDepthBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setDepthBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setAccumBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setAccumBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setRedBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setRedBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setGreenBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setGreenBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setBlueBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setBlueBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setAlphaBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setAlphaBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setStencilBufferSize(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setStencilBufferSize(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setSamples(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setSamples(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setSwapInterval(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setSwapInterval(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setPlane(8);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setPlane(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setVersion(3, 2);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setVersion(3, 2);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setProfile(QGLFormat::CoreProfile);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setProfile(QGLFormat::CoreProfile);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
format1.setOption(QGL::NoDeprecatedFunctions);
|
|
|
|
QVERIFY(!(format1 == format2));
|
|
|
|
QVERIFY(format1 != format2);
|
|
|
|
format2.setOption(QGL::NoDeprecatedFunctions);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format2);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1 != format2));
|
|
|
|
|
|
|
|
// Copy constructor and assignment for QGLFormat.
|
|
|
|
QGLFormat format3(format1);
|
|
|
|
QGLFormat format4;
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format3);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(format1 != format4);
|
|
|
|
format4 = format1;
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1, format4);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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().
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Modify the default format and check that it was changed.
|
|
|
|
QGLFormat::setDefaultFormat(format1);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(QGLFormat::defaultFormat(), format1);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Restore the default format.
|
|
|
|
QGLFormat::setDefaultFormat(QGLFormat());
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(QGLFormat::defaultFormat(), QGLFormat());
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(QGLFormat::defaultOverlayFormat(), format1);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Restore the default overlay format.
|
|
|
|
QGLFormat::setDefaultOverlayFormat(overlay);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(QGLFormat::defaultOverlayFormat(), overlay);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2012-09-04 10:40:16 +00:00
|
|
|
#ifdef QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
void tst_QGL::openGLVersionCheck()
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
2016-11-16 15:57:24 +00:00
|
|
|
#if defined(QT_OPENGL_ES_2)
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
|
|
|
|
#else
|
2014-04-24 15:11:23 +00:00
|
|
|
if (QOpenGLContext::currentContext()->isOpenGLES())
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_ES_Version_2_0);
|
|
|
|
else
|
|
|
|
QVERIFY(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_1);
|
2016-11-16 15:57:24 +00:00
|
|
|
#endif //defined(QT_OPENGL_ES_2)
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2012-09-04 10:40:16 +00:00
|
|
|
#endif //QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
static bool fuzzyComparePixels(const QRgb testPixel, const QRgb refPixel, const char* file, int line, int x = -1, int y = -1)
|
|
|
|
{
|
|
|
|
static int maxFuzz = 1;
|
|
|
|
static bool maxFuzzSet = false;
|
|
|
|
|
|
|
|
// On 16 bpp systems, we need to allow for more fuzz:
|
|
|
|
if (!maxFuzzSet) {
|
|
|
|
maxFuzzSet = true;
|
|
|
|
if (appDefaultDepth() < 24)
|
|
|
|
maxFuzz = 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
int redFuzz = qAbs(qRed(testPixel) - qRed(refPixel));
|
|
|
|
int greenFuzz = qAbs(qGreen(testPixel) - qGreen(refPixel));
|
|
|
|
int blueFuzz = qAbs(qBlue(testPixel) - qBlue(refPixel));
|
|
|
|
int alphaFuzz = qAbs(qAlpha(testPixel) - qAlpha(refPixel));
|
|
|
|
|
|
|
|
if (refPixel != 0 && testPixel == 0) {
|
|
|
|
QString msg;
|
|
|
|
if (x >= 0) {
|
|
|
|
msg = QString("Test pixel [%1, %2] is null (black) when it should be (%3,%4,%5,%6)")
|
|
|
|
.arg(x).arg(y)
|
|
|
|
.arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
|
|
|
|
} else {
|
|
|
|
msg = QString("Test pixel is null (black) when it should be (%2,%3,%4,%5)")
|
|
|
|
.arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
|
|
|
|
}
|
|
|
|
|
|
|
|
QTest::qFail(msg.toLatin1(), file, line);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (redFuzz > maxFuzz || greenFuzz > maxFuzz || blueFuzz > maxFuzz || alphaFuzz > maxFuzz) {
|
|
|
|
QString msg;
|
|
|
|
|
|
|
|
if (x >= 0)
|
|
|
|
msg = QString("Pixel [%1,%2]: ").arg(x).arg(y);
|
|
|
|
else
|
|
|
|
msg = QString("Pixel ");
|
|
|
|
|
|
|
|
msg += QString("Max fuzz (%1) exceeded: (%2,%3,%4,%5) vs (%6,%7,%8,%9)")
|
|
|
|
.arg(maxFuzz)
|
|
|
|
.arg(qRed(testPixel)).arg(qGreen(testPixel)).arg(qBlue(testPixel)).arg(qAlpha(testPixel))
|
|
|
|
.arg(qRed(refPixel)).arg(qGreen(refPixel)).arg(qBlue(refPixel)).arg(qAlpha(refPixel));
|
|
|
|
QTest::qFail(msg.toLatin1(), file, line);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fuzzyCompareImages(const QImage &testImage, const QImage &referenceImage, const char* file, int line)
|
|
|
|
{
|
|
|
|
QCOMPARE(testImage.width(), referenceImage.width());
|
|
|
|
QCOMPARE(testImage.height(), referenceImage.height());
|
|
|
|
|
|
|
|
for (int y = 0; y < testImage.height(); y++) {
|
|
|
|
for (int x = 0; x < testImage.width(); x++) {
|
|
|
|
if (!fuzzyComparePixels(testImage.pixel(x, y), referenceImage.pixel(x, y), file, line, x, y)) {
|
|
|
|
// Might as well save the images for easier debugging:
|
|
|
|
referenceImage.save("referenceImage.png");
|
|
|
|
testImage.save("testImage.png");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define QFUZZY_COMPARE_IMAGES(A,B) \
|
|
|
|
fuzzyCompareImages(A, B, __FILE__, __LINE__)
|
|
|
|
|
|
|
|
#define QFUZZY_COMPARE_PIXELS(A,B) \
|
|
|
|
fuzzyComparePixels(A, B, __FILE__, __LINE__)
|
|
|
|
|
|
|
|
class UnclippedWidget : public QWidget
|
|
|
|
{
|
|
|
|
public:
|
2013-03-05 15:08:40 +00:00
|
|
|
bool painted;
|
|
|
|
|
|
|
|
UnclippedWidget()
|
|
|
|
: painted(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
void paintEvent(QPaintEvent *)
|
|
|
|
{
|
|
|
|
QPainter p(this);
|
|
|
|
p.fillRect(rect().adjusted(-1000, -1000, 1000, 1000), Qt::black);
|
2013-03-05 15:08:40 +00:00
|
|
|
|
|
|
|
painted = true;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
2012-07-18 11:12:59 +00:00
|
|
|
// Use Qt::Tool as fully decorated windows have a minimum width of 160 on Windows.
|
|
|
|
view.setWindowFlags(view.windowFlags() | Qt::Tool);
|
2011-04-27 10:05:43 +00:00
|
|
|
view.setBackgroundBrush(Qt::white);
|
|
|
|
view.resize(2*size, 2*size);
|
|
|
|
|
|
|
|
QGLWidget *viewport = new QGLWidget;
|
|
|
|
view.setViewport(viewport);
|
|
|
|
view.show();
|
2012-07-18 11:12:59 +00:00
|
|
|
qApp->setActiveWindow(&view);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
if (!viewport->isValid())
|
|
|
|
return;
|
|
|
|
|
|
|
|
scene.setSceneRect(view.viewport()->rect());
|
|
|
|
|
2017-07-17 13:24:02 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(view.viewport()->windowHandle()));
|
2013-04-22 11:11:29 +00:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
// The black rectangle jumps from the center to the upper left for some reason.
|
|
|
|
QTest::qWait(100);
|
|
|
|
#endif
|
2013-03-05 15:08:40 +00:00
|
|
|
|
|
|
|
QTRY_VERIFY(widget->painted);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_IMAGES(image, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::partialGLWidgetUpdates_data()
|
|
|
|
{
|
|
|
|
QTest::addColumn<bool>("doubleBufferedContext");
|
|
|
|
QTest::addColumn<bool>("autoFillBackground");
|
|
|
|
QTest::addColumn<bool>("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()
|
|
|
|
{
|
|
|
|
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();
|
2018-04-03 10:32:41 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
if (widget.format().doubleBuffer() != doubleBufferedContext)
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("Platform does not support requested format");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
widget.paintEventRegion = QRegion();
|
|
|
|
widget.repaint(50, 50, 50, 50);
|
2011-09-06 14:44:59 +00:00
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
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())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLPixelBuffer not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_IMAGES(fb, reference);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::glWidgetWithAlpha()
|
|
|
|
{
|
|
|
|
QGLWidget* w = new QGLWidget(QGLFormat(QGL::AlphaChannel));
|
|
|
|
w->show();
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(w));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
delete w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void qt_opengl_draw_test_pattern(QPainter* painter, int width, int height)
|
|
|
|
{
|
|
|
|
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();
|
|
|
|
|
|
|
|
painter->setTransform(QTransform()); // reset xform
|
|
|
|
painter->fillRect(-1, -1, width+2, height+2, Qt::red); // Background
|
|
|
|
painter->translate(14, 14);
|
|
|
|
painter->fillPath(intersectingPath, Qt::blue); // Test stencil buffer works
|
|
|
|
painter->translate(128, 0);
|
|
|
|
painter->setClipPath(trianglePath); // Test depth buffer works
|
|
|
|
painter->setTransform(QTransform()); // reset xform ready for fill
|
|
|
|
painter->fillRect(-1, -1, width+2, height+2, Qt::green);
|
|
|
|
}
|
|
|
|
|
|
|
|
void qt_opengl_check_test_pattern(const QImage& img)
|
|
|
|
{
|
|
|
|
// 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:
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(39, 64), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(89, 64), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(64, 39), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(64, 89), QColor(Qt::blue).rgb());
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(167, 39), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(217, 39), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(img.pixel(192, 64), QColor(Qt::green).rgb());
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
qt_opengl_draw_test_pattern(&p, width(), height());
|
|
|
|
|
|
|
|
// No p.end() or swap buffers, should be done automatically
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::glWidgetRendering()
|
|
|
|
{
|
|
|
|
GLWidget w;
|
|
|
|
w.resize(256, 128);
|
|
|
|
w.show();
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(w.beginOk);
|
|
|
|
QVERIFY(w.engineType == QPaintEngine::OpenGL || w.engineType == QPaintEngine::OpenGL2);
|
|
|
|
|
2014-03-17 16:45:08 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
// glReadPixels reads from the back buffer. On QNX the buffer is not preserved
|
|
|
|
// after a buffer swap. This is why we have to swap the buffer explicitly before calling
|
|
|
|
// grabFrameBuffer to retrieve the content of the front buffer.
|
|
|
|
w.swapBuffers();
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
QImage fb = w.grabFrameBuffer(false);
|
|
|
|
qt_opengl_check_test_pattern(fb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::glFBOSimpleRendering()
|
|
|
|
{
|
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QGLWidget glw;
|
|
|
|
glw.makeCurrent();
|
|
|
|
|
|
|
|
// No multisample with combined depth/stencil attachment:
|
|
|
|
QGLFramebufferObjectFormat fboFormat;
|
|
|
|
fboFormat.setAttachment(QGLFramebufferObject::NoAttachment);
|
|
|
|
|
|
|
|
QGLFramebufferObject *fbo = new QGLFramebufferObject(200, 100, fboFormat);
|
|
|
|
|
|
|
|
fbo->bind();
|
|
|
|
|
2014-04-23 07:08:16 +00:00
|
|
|
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
|
|
|
|
funcs->glClearColor(1.0, 0.0, 0.0, 1.0);
|
|
|
|
funcs->glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
funcs->glFinish();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
|
|
|
|
QImage reference(fb.size(), QImage::Format_RGB32);
|
|
|
|
reference.fill(0xffff0000);
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_IMAGES(fb, reference);
|
|
|
|
|
|
|
|
delete fbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
{
|
2014-03-17 16:45:08 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (fbo->attachment() != QGLFramebufferObject::CombinedDepthStencil) {
|
|
|
|
delete fbo;
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("FBOs missing combined depth~stencil support");
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPainter fboPainter;
|
|
|
|
bool painterBegun = fboPainter.begin(fbo);
|
|
|
|
QVERIFY(painterBegun);
|
|
|
|
|
|
|
|
qt_opengl_draw_test_pattern(&fboPainter, fbo->width(), fbo->height());
|
|
|
|
|
|
|
|
fboPainter.end();
|
|
|
|
|
|
|
|
QImage fb = fbo->toImage().convertToFormat(QImage::Format_RGB32);
|
|
|
|
delete fbo;
|
|
|
|
|
|
|
|
qt_opengl_check_test_pattern(fb);
|
|
|
|
}
|
|
|
|
|
2014-10-31 15:56:25 +00:00
|
|
|
class QOpenGLFramebufferObjectPaintDevice : public QOpenGLPaintDevice
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QOpenGLFramebufferObjectPaintDevice(int width, int height)
|
|
|
|
: QOpenGLPaintDevice(width, height)
|
|
|
|
, m_fbo(width, height, QOpenGLFramebufferObject::CombinedDepthStencil)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensureActiveTarget()
|
|
|
|
{
|
|
|
|
m_fbo.bind();
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage toImage() const
|
|
|
|
{
|
|
|
|
return m_fbo.toImage();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QOpenGLFramebufferObject m_fbo;
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::currentFboSync()
|
|
|
|
{
|
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
|
|
|
|
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
QSKIP("Reading the QGLFramebufferObject is unsupported on this platform");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
QGLWidget glw;
|
|
|
|
glw.makeCurrent();
|
|
|
|
|
2016-07-27 13:45:31 +00:00
|
|
|
// For some reason we offer inter-operatibility between QGL and QOpenGL
|
|
|
|
// paint engines. (?!) Let's check if the two engines can be used to perform
|
|
|
|
// drawing in turns on different targets within the same context.
|
|
|
|
|
2014-10-31 15:56:25 +00:00
|
|
|
{
|
|
|
|
QGLFramebufferObject fbo1(256, 256, QGLFramebufferObject::CombinedDepthStencil);
|
|
|
|
|
|
|
|
QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
|
|
|
|
|
|
|
|
QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
|
|
|
|
QPainter sourcePainter(&sourceImage);
|
|
|
|
qt_opengl_draw_test_pattern(&sourcePainter, 256, 256);
|
|
|
|
|
|
|
|
QPainter fbo1Painter(&fbo1);
|
|
|
|
|
|
|
|
QPainter fbo2Painter(&fbo2);
|
|
|
|
fbo2Painter.drawImage(0, 0, sourceImage);
|
|
|
|
fbo2Painter.end();
|
|
|
|
|
|
|
|
QImage fbo2Image = fbo2.toImage();
|
|
|
|
|
|
|
|
fbo1Painter.drawImage(0, 0, sourceImage);
|
|
|
|
fbo1Painter.end();
|
|
|
|
|
|
|
|
QGLFramebufferObject::bindDefault();
|
|
|
|
|
2017-03-17 14:23:37 +00:00
|
|
|
// Convert the QGLFBO's result since QOpenGLFBO uses a wider
|
|
|
|
// variety of possible return formats.
|
|
|
|
QCOMPARE(fbo1.toImage().convertToFormat(fbo2Image.format()), fbo2Image);
|
2014-10-31 15:56:25 +00:00
|
|
|
}
|
2014-11-04 19:24:38 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
QGLFramebufferObject fbo1(512, 512, QGLFramebufferObject::CombinedDepthStencil);
|
|
|
|
|
|
|
|
QOpenGLFramebufferObjectPaintDevice fbo2(256, 256);
|
|
|
|
|
|
|
|
QImage sourceImage(256, 256, QImage::Format_ARGB32_Premultiplied);
|
|
|
|
QPainter sourcePainter(&sourceImage);
|
|
|
|
qt_opengl_draw_test_pattern(&sourcePainter, 256, 256);
|
|
|
|
|
|
|
|
QPainter fbo2Painter(&fbo2);
|
|
|
|
fbo2Painter.drawImage(0, 0, sourceImage);
|
|
|
|
QImage fbo2Image1 = fbo2.toImage();
|
|
|
|
fbo2Painter.fillRect(0, 0, 256, 256, Qt::white);
|
|
|
|
|
|
|
|
QPainter fbo1Painter(&fbo1);
|
|
|
|
fbo1Painter.drawImage(0, 0, sourceImage);
|
|
|
|
fbo1Painter.end();
|
|
|
|
|
|
|
|
// check that the OpenGL paint engine now knows it needs to sync
|
|
|
|
fbo2Painter.drawImage(0, 0, sourceImage);
|
|
|
|
QImage fbo2Image2 = fbo2.toImage();
|
|
|
|
|
|
|
|
fbo2Painter.end();
|
|
|
|
|
|
|
|
QCOMPARE(fbo2Image1, fbo2Image2);
|
|
|
|
}
|
2014-10-31 15:56:25 +00:00
|
|
|
}
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
if ( (fbo1->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
|
|
|
|
(fbo2->attachment() != QGLFramebufferObject::CombinedDepthStencil) ||
|
|
|
|
(fbo3->attachment() != QGLFramebufferObject::CombinedDepthStencil) )
|
|
|
|
{
|
|
|
|
delete fbo1;
|
|
|
|
delete fbo2;
|
|
|
|
delete fbo3;
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("FBOs missing combined depth~stencil support");
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("Interleaved GL rendering requires OpenGL 2.0 or higher");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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:
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(39, 64), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(89, 64), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 39), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(64, 89), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(167, 39), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(217, 39), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb1.pixel(192, 64), QColor(Qt::green).rgb());
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(39, 64), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(89, 64), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 39), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(64, 89), QColor(Qt::red).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(167, 39), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(217, 39), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb2.pixel(192, 64), QColor(Qt::blue).rgb());
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(39, 64), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(89, 64), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 39), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(64, 89), QColor(Qt::green).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(167, 39), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(fb3.pixel(217, 39), QColor(Qt::blue).rgb());
|
|
|
|
QFUZZY_COMPARE_PIXELS(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::NoAttachment);
|
|
|
|
QGLFramebufferObject *fbo = new QGLFramebufferObject(100, 100, fboFormat);
|
|
|
|
|
|
|
|
QPainter fboPainter;
|
|
|
|
fboPainterBeginOk = fboPainter.begin(fbo);
|
|
|
|
fboPainter.fillRect(-1, -1, 130, 130, Qt::red);
|
|
|
|
fboPainter.end();
|
|
|
|
fboImage = fbo->toImage();
|
|
|
|
|
2014-03-17 16:45:08 +00:00
|
|
|
widgetPainter.fillRect(-1, -1, width()+2, height()+2, Qt::blue);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
delete fbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::glFBOUseInGLWidget()
|
|
|
|
{
|
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
FBOUseInGLWidget w;
|
|
|
|
w.resize(100, 100);
|
2013-08-05 15:46:55 +00:00
|
|
|
w.showNormal();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(w.widgetPainterBeginOk);
|
|
|
|
QVERIFY(w.fboPainterBeginOk);
|
|
|
|
|
2014-03-17 16:45:08 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
// glReadPixels reads from the back buffer. On QNX the buffer is not preserved
|
|
|
|
// after a buffer swap. This is why we have to swap the buffer explicitly before calling
|
|
|
|
// grabFrameBuffer to retrieve the content of the front buffer
|
|
|
|
w.swapBuffers();
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QImage widgetFB = w.grabFrameBuffer(false);
|
|
|
|
QImage widgetReference(widgetFB.size(), widgetFB.format());
|
|
|
|
widgetReference.fill(0xff0000ff);
|
|
|
|
QFUZZY_COMPARE_IMAGES(widgetFB, widgetReference);
|
|
|
|
|
|
|
|
QImage fboReference(w.fboImage.size(), w.fboImage.format());
|
|
|
|
fboReference.fill(0xffff0000);
|
|
|
|
QFUZZY_COMPARE_IMAGES(w.fboImage, fboReference);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::glWidgetReparent()
|
|
|
|
{
|
|
|
|
// Try it as a top-level first:
|
|
|
|
GLWidget *widget = new GLWidget;
|
2013-06-14 12:09:23 +00:00
|
|
|
widget->setObjectName(QStringLiteral("glWidget1"));
|
2011-04-27 10:05:43 +00:00
|
|
|
widget->setGeometry(0, 0, 200, 30);
|
|
|
|
widget->show();
|
|
|
|
|
|
|
|
QWidget grandParentWidget;
|
2013-06-14 12:09:23 +00:00
|
|
|
grandParentWidget.setObjectName(QStringLiteral("grandParentWidget"));
|
2011-04-27 10:05:43 +00:00
|
|
|
grandParentWidget.setPalette(Qt::blue);
|
|
|
|
QVBoxLayout grandParentLayout(&grandParentWidget);
|
|
|
|
|
|
|
|
QWidget parentWidget(&grandParentWidget);
|
2013-06-14 12:09:23 +00:00
|
|
|
parentWidget.setObjectName(QStringLiteral("parentWidget"));
|
2011-04-27 10:05:43 +00:00
|
|
|
grandParentLayout.addWidget(&parentWidget);
|
|
|
|
parentWidget.setPalette(Qt::green);
|
|
|
|
parentWidget.setAutoFillBackground(true);
|
|
|
|
QVBoxLayout parentLayout(&parentWidget);
|
|
|
|
|
|
|
|
grandParentWidget.setGeometry(0, 100, 200, 200);
|
|
|
|
grandParentWidget.show();
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(widget));
|
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(parentWidget.children().count() == 1); // The layout
|
|
|
|
|
|
|
|
// Now both widgets should be created & shown, time to re-parent:
|
|
|
|
parentLayout.addWidget(widget);
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(parentWidget.children().count() == 2); // Layout & glwidget
|
|
|
|
QVERIFY(parentWidget.children().contains(widget));
|
2012-07-18 11:12:59 +00:00
|
|
|
QTRY_VERIFY(widget->height() > 30);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
delete widget;
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QVERIFY(parentWidget.children().count() == 1); // The layout
|
|
|
|
|
|
|
|
// Now do pretty much the same thing, but don't show the
|
|
|
|
// widget first:
|
|
|
|
widget = new GLWidget;
|
2013-06-14 12:09:23 +00:00
|
|
|
widget->setObjectName(QStringLiteral("glWidget2"));
|
2011-04-27 10:05:43 +00:00
|
|
|
parentLayout.addWidget(widget);
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&grandParentWidget));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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:
|
2014-04-23 07:08:16 +00:00
|
|
|
QOpenGLContext::currentContext()->functions()->glClearColor(1.0, 0.0, 0.0, 1.0);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void paintGL() {
|
2014-04-23 07:08:16 +00:00
|
|
|
QOpenGLContext::currentContext()->functions()->glClear(GL_COLOR_BUFFER_BIT);
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::glWidgetRenderPixmap()
|
|
|
|
{
|
|
|
|
RenderPixmapWidget *w = new RenderPixmapWidget;
|
|
|
|
|
2013-12-30 16:09:12 +00:00
|
|
|
QSize pmSize = QSize(100, 100);
|
|
|
|
QPixmap pm = w->renderPixmap(pmSize.width(), pmSize.height(), false);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
delete w;
|
|
|
|
|
|
|
|
QImage fb = pm.toImage().convertToFormat(QImage::Format_RGB32);
|
2013-12-30 16:09:12 +00:00
|
|
|
QImage reference(pmSize, QImage::Format_RGB32);
|
2011-04-27 10:05:43 +00:00
|
|
|
reference.fill(0xffff0000);
|
|
|
|
|
|
|
|
QFUZZY_COMPARE_IMAGES(fb, reference);
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(cmap1.entryRgb(0), QRgb(0));
|
|
|
|
QCOMPARE(cmap1.entryRgb(-1), QRgb(0));
|
|
|
|
QCOMPARE(cmap1.entryRgb(100), QRgb(0));
|
2011-04-27 10:05:43 +00:00
|
|
|
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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(cmap1.entryRgb(0), QRgb(0));
|
2011-04-27 10:05:43 +00:00
|
|
|
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));
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(cmap4.handle(), Qt::HANDLE(42));
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!cmap4.isEmpty());
|
|
|
|
QCOMPARE(cmap4.size(), 256);
|
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1.attachment(), QGLFramebufferObject::NoAttachment);
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_2D));
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
int expectedFormat =
|
|
|
|
#ifdef QT_OPENGL_ES_2
|
|
|
|
GL_RGBA;
|
|
|
|
#else
|
2014-04-24 15:11:23 +00:00
|
|
|
QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8;
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
#endif
|
|
|
|
QCOMPARE(int(format1.internalTextureFormat()), expectedFormat);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
|
2011-04-27 10:05:43 +00:00
|
|
|
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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format2.attachment(), QGLFramebufferObject::CombinedDepthStencil);
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(int(format2.textureTarget()), int(GL_TEXTURE_3D));
|
|
|
|
QCOMPARE(int(format2.internalTextureFormat()), int(GL_RGB16));
|
|
|
|
format3 = format1;
|
|
|
|
QCOMPARE(format3.samples(), 8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format3.attachment(), QGLFramebufferObject::CombinedDepthStencil);
|
2011-04-27 10:05:43 +00:00
|
|
|
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);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1.attachment(), QGLFramebufferObject::CombinedDepthStencil);
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(int(format1.textureTarget()), int(GL_TEXTURE_3D));
|
|
|
|
QCOMPARE(int(format1.internalTextureFormat()), int(GL_RGB16));
|
|
|
|
|
|
|
|
// operator== and operator!= for QGLFramebufferObjectFormat.
|
|
|
|
QGLFramebufferObjectFormat format1c;
|
|
|
|
QGLFramebufferObjectFormat format2c;
|
|
|
|
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format2c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format2c));
|
|
|
|
format1c.setSamples(8);
|
|
|
|
QVERIFY(!(format1c == format2c));
|
|
|
|
QVERIFY(format1c != format2c);
|
|
|
|
format2c.setSamples(8);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format2c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format2c));
|
|
|
|
|
|
|
|
format1c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
|
|
|
|
QVERIFY(!(format1c == format2c));
|
|
|
|
QVERIFY(format1c != format2c);
|
|
|
|
format2c.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format2c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format2c));
|
|
|
|
|
|
|
|
format1c.setTextureTarget(GL_TEXTURE_3D);
|
|
|
|
QVERIFY(!(format1c == format2c));
|
|
|
|
QVERIFY(format1c != format2c);
|
|
|
|
format2c.setTextureTarget(GL_TEXTURE_3D);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format2c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format2c));
|
|
|
|
|
|
|
|
format1c.setInternalTextureFormat(GL_RGB16);
|
|
|
|
QVERIFY(!(format1c == format2c));
|
|
|
|
QVERIFY(format1c != format2c);
|
|
|
|
format2c.setInternalTextureFormat(GL_RGB16);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format2c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format2c));
|
|
|
|
|
|
|
|
QGLFramebufferObjectFormat format3c(format1c);
|
|
|
|
QGLFramebufferObjectFormat format4c;
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format3c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format3c));
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
format3c.setInternalTextureFormat(
|
|
|
|
#ifdef QT_OPENGL_ES_2
|
|
|
|
GL_RGBA
|
|
|
|
#else
|
2014-04-24 15:11:23 +00:00
|
|
|
QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
#endif
|
|
|
|
);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c == format3c));
|
|
|
|
QVERIFY(format1c != format3c);
|
|
|
|
|
|
|
|
format4c = format1c;
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(format1c, format4c);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c != format4c));
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
format4c.setInternalTextureFormat(
|
|
|
|
#ifdef QT_OPENGL_ES_2
|
|
|
|
GL_RGBA
|
|
|
|
#else
|
2014-04-24 15:11:23 +00:00
|
|
|
QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL ? GL_RGBA : GL_RGBA8
|
Dynamic GL switch on Windows
The patch introduces a new build configuration on Windows which
can be requested by passing -opengl dynamic to configure.
Platforms other than Windows (including WinRT) are not affected.
The existing Angle and desktop configurations are not affected.
These continue to function as before and Angle remains the default.
In the future, when all modules have added support for the dynamic
path, as described below, the default configuration could be changed
to be the dynamic one. This would allow providing a single set of
binaries in the official builds instead of the current two.
When requesting dynamic GL, Angle is built but QT_OPENGL_ES[_2] are
never defined. Instead, the code path that has traditionally been
desktop GL only becomes the dynamic path that has to do runtime
checks. Qt modules and applications are not linked to opengl32.dll or
libegl/glesv2.dll in this case. Instead, QtGui exports all necessary
egl/egl/gl functions which will, under the hood, forward all requests
to a dynamically loaded EGL/WGL/GL implementation.
Porting guide (better said, changes needed to prepare your code to
work with dynamic GL builds when the fallback to Angle is utilized):
1. In !QT_OPENGL_ES[_2] code branches use QOpenGLFunctions::isES() to
differentiate between desktop and ES where needed. Keep in mind that
it is the desktop GL header (plus qopenglext.h) that is included,
not the GLES one.
QtGui's proxy will handle some differences, for example calling
glClearDepth will route to glClearDepthf when needed. The built-in
eglGetProcAddress is able to retrieve pointers for standard GLES2
functions too so code resolving OpenGL 2 functions will function
in any case.
2. QT_CONFIG will contain "opengl" and "dynamicgl" in dynamic builds,
but never "angle" or "opengles2".
3. The preprocessor define QT_OPENGL_DYNAMIC is also available in
dynamic builds. The usage of this is strongly discouraged and should
not be needed anywhere except for QtGui and the platform plugin.
4. Code in need of the library handle can use
QOpenGLFunctions::platformGLHandle().
The decision on which library to load is currently based on a simple
test that creates a dummy window/context and tries to resolve an
OpenGL 2 function. If this fails, it goes for Angle. This seems to work
well on Win7 PCs for example that do not have proper graphics drivers
providing OpenGL installed but are D3D9 capable using the default drivers.
Setting QT_OPENGL to desktop or angle skips the test and forces
usage of the given GL. There are also two new application attributes
that could be used for the same purpose.
If Angle is requested but the libraries are not present, desktop is
tried. If desktop is requested, or if angle is requested but nothing
works, the EGL/WGL functions will still be callable but will return 0.
This conveniently means that eglInitialize() and such will report a failure.
Debug messages can be enabled by setting QT_OPENGLPROXY_DEBUG. This will
tell which implementation is chosen.
The textures example application is ported to OpenGL 2, the GL 1
code path is removed.
[ChangeLog][QtGui] Qt builds on Windows can now be configured for
dynamic loading of the OpenGL implementation. This can be requested
by passing -opengl dynamic to configure. In this mode no modules will
link to opengl32.dll or Angle's libegl/libglesv2. Instead, QtGui will
dynamically choose between desktop and Angle during the first GL/EGL/WGL
call. This allows deploying applications with a single set of Qt libraries
with the ability of transparently falling back to Angle in case the
opengl32.dll is not suitable, due to missing graphics drivers for example.
Task-number: QTBUG-36483
Change-Id: I716fdebbf60b355b7d9ef57d1e069eef366b4ab9
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jørgen Lind <jorgen.lind@digia.com>
2014-01-27 13:45:11 +00:00
|
|
|
#endif
|
|
|
|
);
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!(format1c == format4c));
|
|
|
|
QVERIFY(format1c != format4c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::testDontCrashOnDanglingResources()
|
|
|
|
{
|
2012-12-28 18:09:39 +00:00
|
|
|
// We have a number of Q_GLOBAL_STATICS inside the Qt OpenGL
|
|
|
|
// module. This test is verify that we don't crash as a result of
|
2011-04-27 10:05:43 +00:00
|
|
|
// 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
|
2014-04-23 07:08:16 +00:00
|
|
|
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
|
|
|
|
funcs->glStencilMask(0xFFFF);
|
|
|
|
funcs->glClearStencil(0xFFFF);
|
|
|
|
funcs->glDisable(GL_STENCIL_TEST);
|
|
|
|
funcs->glDisable(GL_SCISSOR_TEST);
|
|
|
|
funcs->glClear(GL_STENCIL_BUFFER_BIT);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QPainter painter(this);
|
|
|
|
paint(&painter);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::replaceClipping()
|
|
|
|
{
|
|
|
|
ReplaceClippingGLWidget glw;
|
|
|
|
glw.resize(300, 300);
|
|
|
|
glw.show();
|
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&glw));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QImage reference(300, 300, QImage::Format_RGB32);
|
|
|
|
QPainter referencePainter(&reference);
|
|
|
|
glw.paint(&referencePainter);
|
|
|
|
referencePainter.end();
|
|
|
|
|
2014-03-17 16:45:08 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
// glReadPixels reads from the back buffer. On QNX the buffer is not preserved
|
|
|
|
// after a buffer swap. This is why we have to swap the buffer explicitly before calling
|
|
|
|
// grabFrameBuffer to retrieve the content of the front buffer
|
|
|
|
glw.swapBuffers();
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
|
|
|
|
|
|
|
|
// Sample pixels in a grid pattern which avoids false failures due to
|
|
|
|
// off-by-one pixel errors on some buggy GL implementations
|
|
|
|
for (int x = 25; x < reference.width(); x += 50) {
|
|
|
|
for (int y = 25; y < reference.width(); y += 50) {
|
|
|
|
QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class ClipTestGLWidget : public QGLWidget
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
void paint(QPainter *painter)
|
|
|
|
{
|
|
|
|
painter->fillRect(-1, -1, width()+2, height()+2, 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->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();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void paintEvent(QPaintEvent*)
|
|
|
|
{
|
|
|
|
QPainter painter(this);
|
|
|
|
paint(&painter);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void tst_QGL::clipTest()
|
|
|
|
{
|
|
|
|
ClipTestGLWidget glw;
|
|
|
|
glw.resize(220, 220);
|
2013-08-05 15:46:55 +00:00
|
|
|
glw.showNormal();
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-07-18 11:12:59 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&glw));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QImage reference(glw.size(), QImage::Format_RGB32);
|
|
|
|
QPainter referencePainter(&reference);
|
|
|
|
glw.paint(&referencePainter);
|
|
|
|
referencePainter.end();
|
|
|
|
|
2014-03-17 16:45:08 +00:00
|
|
|
#if defined(Q_OS_QNX)
|
|
|
|
// glReadPixels reads from the back buffer. On QNX the buffer is not preserved
|
|
|
|
// after a buffer swap. This is why we have to swap the buffer explicitly before calling
|
|
|
|
// grabFrameBuffer to retrieve the content of the front buffer
|
|
|
|
glw.swapBuffers();
|
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
const QImage widgetFB = glw.grabFrameBuffer(false).convertToFormat(QImage::Format_RGB32);
|
|
|
|
|
|
|
|
// Sample pixels in a grid pattern which avoids false failures due to
|
|
|
|
// off-by-one pixel errors on some buggy GL implementations
|
|
|
|
for (int x = 2; x < reference.width(); x += 5) {
|
|
|
|
for (int y = 2; y < reference.height(); y += 5) {
|
|
|
|
QFUZZY_COMPARE_PIXELS(widgetFB.pixel(x, y), reference.pixel(x, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::destroyFBOAfterContext()
|
|
|
|
{
|
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
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.
|
2015-07-24 13:49:08 +00:00
|
|
|
QVERIFY(!fbo->handle());
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(!fbo->isValid());
|
|
|
|
|
|
|
|
delete fbo;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef QT_BUILD_INTERNAL
|
|
|
|
|
|
|
|
class tst_QGLResource
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
tst_QGLResource(const QGLContext * = 0) {}
|
|
|
|
~tst_QGLResource() { ++deletions; }
|
|
|
|
|
|
|
|
static int deletions;
|
|
|
|
};
|
|
|
|
|
|
|
|
int tst_QGLResource::deletions = 0;
|
|
|
|
|
2011-08-29 12:13:08 +00:00
|
|
|
#ifdef TODO
|
|
|
|
Q_GLOBAL_STATIC(QOpenGLContextGroupResource<tst_QGLResource>, qt_shared_test)
|
|
|
|
#endif //TODO
|
2012-09-04 10:40:16 +00:00
|
|
|
#endif // QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-09-04 10:40:16 +00:00
|
|
|
#ifdef QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
void tst_QGL::shareRegister()
|
|
|
|
{
|
2011-08-29 12:13:08 +00:00
|
|
|
#ifdef TODO
|
2011-04-27 10:05:43 +00:00
|
|
|
// Create a context.
|
|
|
|
QGLWidget *glw1 = new QGLWidget();
|
|
|
|
glw1->makeCurrent();
|
|
|
|
|
|
|
|
// Nothing should be sharing with glw1's context yet.
|
|
|
|
QVERIFY(!glw1->isSharing());
|
|
|
|
|
|
|
|
// Create a guard for the first context.
|
2011-08-29 12:13:08 +00:00
|
|
|
QOpenGLSharedResourceGuard guard(glw1->context()->contextHandle());
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(guard.id(), 0);
|
2011-04-27 10:05:43 +00:00
|
|
|
guard.setId(3);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(guard.id(), 3);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Request a tst_QGLResource object for the first context.
|
2011-08-29 12:13:08 +00:00
|
|
|
tst_QGLResource *res1 = qt_shared_test()->value(glw1->context()->contextHandle());
|
2011-04-27 10:05:43 +00:00
|
|
|
QVERIFY(res1);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(qt_shared_test()->value(glw1->context()->contextHandle()), res1);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Create another context that shares with the first.
|
|
|
|
QVERIFY(!glw1->isSharing());
|
|
|
|
QGLWidget *glw2 = new QGLWidget(0, glw1);
|
|
|
|
if (!glw2->isSharing()) {
|
|
|
|
delete glw2;
|
|
|
|
delete glw1;
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("Context sharing is not supported");
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
QVERIFY(glw1->isSharing());
|
|
|
|
QVERIFY(glw1->context() != glw2->context());
|
|
|
|
|
|
|
|
// Check that the first context's resource is also on the second.
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
|
|
|
|
QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Guard should still be the same.
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(guard.context(), glw1->context());
|
|
|
|
QCOMPARE(guard.id(), 3);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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();
|
|
|
|
QVERIFY(!glw3->isSharing());
|
|
|
|
|
|
|
|
// Create a guard on the standalone context.
|
|
|
|
QGLSharedResourceGuard guard3(glw3->context());
|
|
|
|
guard3.setId(5);
|
|
|
|
|
|
|
|
// Request a resource to the third context.
|
|
|
|
tst_QGLResource *res3 = qt_shared_test()->value(glw3->context());
|
|
|
|
QVERIFY(res3);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(qt_shared_test()->value(glw1->context()), res1);
|
|
|
|
QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
|
|
|
|
QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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.
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(guard.context(), glw1->context());
|
|
|
|
QCOMPARE(guard.id(), 3);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Delete the first context.
|
|
|
|
delete glw1;
|
|
|
|
|
|
|
|
// The second context should no longer register as sharing.
|
|
|
|
QVERIFY(!glw2->isSharing());
|
|
|
|
|
|
|
|
// The first context's resource should transfer to the second context.
|
|
|
|
QCOMPARE(tst_QGLResource::deletions, 0);
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(qt_shared_test()->value(glw2->context()), res1);
|
|
|
|
QCOMPARE(qt_shared_test()->value(glw3->context()), res3);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Shared guard should now be the second context, with the id the same.
|
2015-07-24 13:49:08 +00:00
|
|
|
QCOMPARE(guard.context(), glw2->context());
|
|
|
|
QCOMPARE(guard.id(), 3);
|
|
|
|
QCOMPARE(guard3.context(), glw3->context());
|
|
|
|
QCOMPARE(guard3.id(), 5);
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// 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);
|
2011-08-29 12:13:08 +00:00
|
|
|
#endif //TODO
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2012-09-04 10:40:16 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Tests QGLContext::bindTexture with default options
|
2012-09-04 10:40:16 +00:00
|
|
|
#ifdef QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
void tst_QGL::qglContextDefaultBindTexture()
|
|
|
|
{
|
|
|
|
QGLWidget w;
|
|
|
|
w.makeCurrent();
|
|
|
|
QGLContext *ctx = const_cast<QGLContext*>(w.context());
|
|
|
|
|
|
|
|
QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
|
|
|
|
boundImage->fill(0xFFFFFFFF);
|
|
|
|
QPixmap *boundPixmap = new QPixmap(256, 256);
|
|
|
|
boundPixmap->fill(Qt::red);
|
|
|
|
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
|
|
|
|
GLuint boundImageTextureId = ctx->bindTexture(*boundImage);
|
|
|
|
GLuint boundPixmapTextureId = ctx->bindTexture(*boundPixmap);
|
|
|
|
|
|
|
|
// Make sure the image & pixmap have been added to the cache:
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
// Make sure the image & pixmap have the is_cached flag set:
|
|
|
|
QVERIFY(QImagePixmapCleanupHooks::isImageCached(*boundImage));
|
|
|
|
QVERIFY(QImagePixmapCleanupHooks::isPixmapCached(*boundPixmap));
|
|
|
|
|
2014-04-23 07:08:16 +00:00
|
|
|
QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
|
2011-04-27 10:05:43 +00:00
|
|
|
// Make sure the texture IDs returned are valid:
|
2015-07-16 14:40:42 +00:00
|
|
|
QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
|
|
|
|
QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
// Make sure the textures are still valid after we delete the image/pixmap:
|
|
|
|
// Also check that although the textures are left intact, the cache entries are removed:
|
|
|
|
delete boundImage;
|
|
|
|
boundImage = 0;
|
2015-07-16 14:40:42 +00:00
|
|
|
QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_TRUE));
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
delete boundPixmap;
|
|
|
|
boundPixmap = 0;
|
2015-07-16 14:40:42 +00:00
|
|
|
QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_TRUE));
|
2011-04-27 10:05:43 +00:00
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
// Finally, make sure QGLContext::deleteTexture deletes the texture IDs:
|
|
|
|
ctx->deleteTexture(boundImageTextureId);
|
|
|
|
ctx->deleteTexture(boundPixmapTextureId);
|
2015-07-16 14:40:42 +00:00
|
|
|
QCOMPARE(funcs->glIsTexture(boundImageTextureId), GLboolean(GL_FALSE));
|
|
|
|
QCOMPARE(funcs->glIsTexture(boundPixmapTextureId), GLboolean(GL_FALSE));
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
2012-09-04 10:40:16 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
2012-09-04 10:40:16 +00:00
|
|
|
#ifdef QT_BUILD_INTERNAL
|
2011-04-27 10:05:43 +00:00
|
|
|
void tst_QGL::textureCleanup()
|
|
|
|
{
|
|
|
|
QGLWidget w;
|
|
|
|
w.resize(200,200);
|
|
|
|
w.show();
|
2017-09-08 16:42:25 +00:00
|
|
|
QVERIFY(QTest::qWaitForWindowExposed(&w));
|
2011-04-27 10:05:43 +00:00
|
|
|
w.makeCurrent();
|
|
|
|
|
|
|
|
// Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
|
|
|
|
// when the pixmap cache is cleared
|
|
|
|
{
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QPixmap boundPixmap(":designer.png");
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
// Check that the texture doesn't get removed from the cache when the pixmap is cleared
|
|
|
|
// as it should still be in the cache:
|
|
|
|
boundPixmap = QPixmap();
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
QPixmapCache::clear();
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test pixmaps which have been loaded via QPixmapCache are removed from the texture cache
|
|
|
|
// when they are explicitly removed from the pixmap cache
|
|
|
|
{
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QPixmap boundPixmap(128, 128);
|
|
|
|
QString cacheKey = QString::fromLatin1("myPixmap");
|
|
|
|
QPixmapCache::insert(cacheKey, boundPixmap);
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
// Check that the texture doesn't get removed from the cache when the pixmap is cleared
|
|
|
|
// as it should still be in the cache:
|
|
|
|
boundPixmap = QPixmap();
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
// Finally, we check that the texture cache entry is removed when we remove the
|
|
|
|
// pixmap cache entry, which should hold the last reference:
|
|
|
|
QPixmapCache::remove(cacheKey);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check images & pixmaps are removed from the cache when they are deleted
|
|
|
|
{
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QImage *boundImage = new QImage(256, 256, QImage::Format_RGB32);
|
|
|
|
boundImage->fill(0xFFFFFFFF);
|
|
|
|
QPixmap *boundPixmap = new QPixmap(256, 256);
|
|
|
|
boundPixmap->fill(Qt::red);
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawImage(0, 0, *boundImage);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, *boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
delete boundImage;
|
|
|
|
boundImage = 0;
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
delete boundPixmap;
|
|
|
|
boundPixmap = 0;
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check images & pixmaps are removed from the cache when they are assigned to
|
|
|
|
{
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QImage boundImage(256, 256, QImage::Format_RGB32);
|
|
|
|
boundImage.fill(0xFFFFFFFF);
|
|
|
|
QPixmap boundPixmap(256, 256);
|
|
|
|
boundPixmap.fill(Qt::red);
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawImage(0, 0, boundImage);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
boundImage = QImage(64, 64, QImage::Format_RGB32);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
boundPixmap = QPixmap(64, 64);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check images & pixmaps are removed from the cache when they are modified (detached)
|
|
|
|
{
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QImage boundImage(256, 256, QImage::Format_RGB32);
|
|
|
|
boundImage.fill(0xFFFFFFFF);
|
|
|
|
QPixmap boundPixmap(256, 256);
|
|
|
|
boundPixmap.fill(Qt::red);
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawImage(0, 0, boundImage);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
boundImage.fill(0x00000000);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
boundPixmap.fill(Qt::blue);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that images/pixmaps aren't removed from the cache if a shallow copy has been made
|
|
|
|
QImage copyOfImage;
|
|
|
|
QPixmap copyOfPixmap;
|
|
|
|
int startCacheItemCount = QGLTextureCache::instance()->size();
|
|
|
|
{
|
|
|
|
QPainter p(&w);
|
|
|
|
|
|
|
|
QImage boundImage(256, 256, QImage::Format_RGB32);
|
|
|
|
boundImage.fill(0xFFFFFFFF);
|
|
|
|
QPixmap boundPixmap(256, 256);
|
|
|
|
boundPixmap.fill(Qt::red);
|
|
|
|
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
|
|
|
|
p.drawImage(0, 0, boundImage);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
p.drawPixmap(0, 0, boundPixmap);
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
// Need to call end for the GL2 paint engine to release references to pixmap if using tfp
|
|
|
|
p.end();
|
|
|
|
|
|
|
|
copyOfImage = boundImage;
|
|
|
|
copyOfPixmap = boundPixmap;
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
} // boundImage & boundPixmap would have been deleted when they went out of scope
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+2);
|
|
|
|
|
|
|
|
copyOfImage = QImage();
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount+1);
|
|
|
|
|
|
|
|
copyOfPixmap = QPixmap();
|
|
|
|
QCOMPARE(QGLTextureCache::instance()->size(), startCacheItemCount);
|
|
|
|
}
|
2012-09-04 10:40:16 +00:00
|
|
|
#endif
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
namespace ThreadImages {
|
|
|
|
|
|
|
|
class Producer : public QObject
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
Producer()
|
|
|
|
{
|
|
|
|
startTimer(20);
|
|
|
|
|
|
|
|
QThread *thread = new QThread;
|
|
|
|
thread->start();
|
|
|
|
|
|
|
|
connect(this, SIGNAL(destroyed()), thread, SLOT(quit()));
|
|
|
|
|
|
|
|
moveToThread(thread);
|
|
|
|
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
|
|
|
}
|
|
|
|
|
|
|
|
signals:
|
|
|
|
void imageReady(const QImage &image);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void timerEvent(QTimerEvent *)
|
|
|
|
{
|
|
|
|
QImage image(256, 256, QImage::Format_RGB32);
|
|
|
|
QLinearGradient g(0, 0, 0, 256);
|
|
|
|
g.setColorAt(0, QColor(255, 180, 180));
|
|
|
|
g.setColorAt(1, Qt::white);
|
|
|
|
g.setSpread(QGradient::ReflectSpread);
|
|
|
|
|
|
|
|
QBrush brush(g);
|
|
|
|
brush.setTransform(QTransform::fromTranslate(0, delta));
|
|
|
|
delta += 10;
|
|
|
|
|
|
|
|
QPainter p(&image);
|
|
|
|
p.fillRect(image.rect(), brush);
|
|
|
|
|
|
|
|
if (images.size() > 10)
|
|
|
|
images.removeFirst();
|
|
|
|
|
|
|
|
images.append(image);
|
|
|
|
|
|
|
|
emit imageReady(image);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QList<QImage> images;
|
|
|
|
int delta;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class DisplayWidget : public QGLWidget
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
DisplayWidget(QWidget *parent) : QGLWidget(parent) {}
|
|
|
|
void paintEvent(QPaintEvent *)
|
|
|
|
{
|
|
|
|
QPainter p(this);
|
|
|
|
p.drawImage(rect(), m_image);
|
|
|
|
}
|
|
|
|
|
|
|
|
public slots:
|
|
|
|
void setImage(const QImage &image)
|
|
|
|
{
|
|
|
|
m_image = image;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
QImage m_image;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Widget : public QWidget
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
Widget()
|
|
|
|
: iterations(0)
|
|
|
|
, display(0)
|
|
|
|
, producer(new Producer)
|
|
|
|
{
|
|
|
|
startTimer(400);
|
|
|
|
connect(this, SIGNAL(destroyed()), producer, SLOT(deleteLater()));
|
|
|
|
}
|
|
|
|
|
|
|
|
int iterations;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void timerEvent(QTimerEvent *)
|
|
|
|
{
|
|
|
|
++iterations;
|
|
|
|
|
|
|
|
delete display;
|
|
|
|
display = new DisplayWidget(this);
|
2012-10-16 09:15:37 +00:00
|
|
|
connect(producer, SIGNAL(imageReady(QImage)), display, SLOT(setImage(QImage)));
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
display->setGeometry(rect());
|
|
|
|
display->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DisplayWidget *display;
|
|
|
|
Producer *producer;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::threadImages()
|
|
|
|
{
|
|
|
|
ThreadImages::Widget *widget = new ThreadImages::Widget;
|
|
|
|
widget->show();
|
|
|
|
|
|
|
|
while (widget->iterations <= 5) {
|
|
|
|
qApp->processEvents();
|
|
|
|
}
|
|
|
|
|
|
|
|
delete widget;
|
|
|
|
}
|
|
|
|
|
|
|
|
void tst_QGL::nullRectCrash()
|
|
|
|
{
|
|
|
|
if (!QGLFramebufferObject::hasOpenGLFramebufferObjects())
|
2011-10-19 02:53:13 +00:00
|
|
|
QSKIP("QGLFramebufferObject not supported on this platform");
|
2011-04-27 10:05:43 +00:00
|
|
|
|
|
|
|
QGLWidget glw;
|
|
|
|
glw.makeCurrent();
|
|
|
|
|
|
|
|
QGLFramebufferObjectFormat fboFormat;
|
|
|
|
fboFormat.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
|
|
|
|
|
|
|
|
QGLFramebufferObject *fbo = new QGLFramebufferObject(128, 128, fboFormat);
|
|
|
|
|
|
|
|
QPainter fboPainter(fbo);
|
|
|
|
|
|
|
|
fboPainter.setPen(QPen(QColor(255, 127, 127, 127), 2));
|
|
|
|
fboPainter.setBrush(QColor(127, 255, 127, 127));
|
|
|
|
fboPainter.drawRect(QRectF());
|
|
|
|
|
|
|
|
fboPainter.end();
|
|
|
|
}
|
|
|
|
|
2014-03-19 11:52:03 +00:00
|
|
|
void tst_QGL::extensions()
|
|
|
|
{
|
|
|
|
QGLWidget glw;
|
|
|
|
glw.makeCurrent();
|
|
|
|
|
|
|
|
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
|
|
|
QVERIFY(ctx);
|
|
|
|
QOpenGLFunctions *funcs = ctx->functions();
|
|
|
|
QVERIFY(funcs);
|
|
|
|
QSurfaceFormat format = ctx->format();
|
|
|
|
|
|
|
|
#ifdef QT_BUILD_INTERNAL
|
|
|
|
QOpenGLExtensions *exts = static_cast<QOpenGLExtensions *>(funcs);
|
|
|
|
QOpenGLExtensions::OpenGLExtensions allExts = exts->openGLExtensions();
|
|
|
|
// Mipmapping is always available in GL2/GLES2+. Verify this.
|
|
|
|
if (format.majorVersion() >= 2)
|
|
|
|
QVERIFY(allExts.testFlag(QOpenGLExtensions::GenerateMipmap));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Now look for some features should always be available in a given version.
|
|
|
|
QOpenGLFunctions::OpenGLFeatures allFeatures = funcs->openGLFeatures();
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multitexture));
|
|
|
|
if (format.majorVersion() >= 2) {
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Shaders));
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Buffers));
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Multisample));
|
2014-04-24 15:11:23 +00:00
|
|
|
QVERIFY(!ctx->isOpenGLES() || allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
|
2014-03-19 11:52:03 +00:00
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::NPOTTextures)
|
|
|
|
&& allFeatures.testFlag(QOpenGLFunctions::NPOTTextureRepeat));
|
2014-04-24 15:11:23 +00:00
|
|
|
if (ctx->isOpenGLES()) {
|
2014-03-19 11:52:03 +00:00
|
|
|
QVERIFY(!allFeatures.testFlag(QOpenGLFunctions::FixedFunctionPipeline));
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (format.majorVersion() >= 3)
|
|
|
|
QVERIFY(allFeatures.testFlag(QOpenGLFunctions::Framebuffers));
|
|
|
|
}
|
|
|
|
|
2011-10-26 06:12:27 +00:00
|
|
|
QTEST_MAIN(tst_QGL)
|
2011-04-27 10:05:43 +00:00
|
|
|
#include "tst_qgl.moc"
|