summaryrefslogtreecommitdiffstats
path: root/tests/auto/render/opengl
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/render/opengl')
-rw-r--r--tests/auto/render/opengl/computecommand/computecommand.pro15
-rw-r--r--tests/auto/render/opengl/computecommand/tst_computecommand.cpp143
-rw-r--r--tests/auto/render/opengl/filtercompatibletechniquejob/BLACKLIST6
-rw-r--r--tests/auto/render/opengl/filtercompatibletechniquejob/filtercompatibletechniquejob.pro15
-rw-r--r--tests/auto/render/opengl/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp263
-rw-r--r--tests/auto/render/opengl/glshadermanager/glshadermanager.pro15
-rw-r--r--tests/auto/render/opengl/glshadermanager/tst_glshadermanager.cpp199
-rw-r--r--tests/auto/render/opengl/graphicshelpergl2/graphicshelpergl2.pro16
-rw-r--r--tests/auto/render/opengl/graphicshelpergl2/tst_graphicshelpergl2.cpp1606
-rw-r--r--tests/auto/render/opengl/graphicshelpergl3_2/graphicshelpergl3_2.pro16
-rw-r--r--tests/auto/render/opengl/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp2295
-rw-r--r--tests/auto/render/opengl/graphicshelpergl3_3/graphicshelpergl3_3.pro16
-rw-r--r--tests/auto/render/opengl/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp2394
-rw-r--r--tests/auto/render/opengl/graphicshelpergl4/graphicshelpergl4.pro16
-rw-r--r--tests/auto/render/opengl/graphicshelpergl4/tst_graphicshelpergl4.cpp2584
-rw-r--r--tests/auto/render/opengl/materialparametergathererjob/materialparametergathererjob.pro14
-rw-r--r--tests/auto/render/opengl/materialparametergathererjob/tst_materialparametergathererjob.cpp801
-rw-r--r--tests/auto/render/opengl/opengl.pro19
-rw-r--r--tests/auto/render/opengl/opengl_render_plugin.pri18
-rw-r--r--tests/auto/render/opengl/qgraphicsutils/qgraphicsutils.pro12
-rw-r--r--tests/auto/render/opengl/qgraphicsutils/tst_qgraphicsutils.cpp355
-rw-r--r--tests/auto/render/opengl/renderer/renderer.pro12
-rw-r--r--tests/auto/render/opengl/renderer/tst_renderer.cpp348
-rw-r--r--tests/auto/render/opengl/renderqueue/renderqueue.pro14
-rw-r--r--tests/auto/render/opengl/renderqueue/tst_renderqueue.cpp243
-rw-r--r--tests/auto/render/opengl/renderviewbuilder/renderviewbuilder.pro15
-rw-r--r--tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp670
-rw-r--r--tests/auto/render/opengl/renderviews/renderviews.pro15
-rw-r--r--tests/auto/render/opengl/renderviews/tst_renderviews.cpp556
-rw-r--r--tests/auto/render/opengl/renderviewutils/renderviewutils.pro15
-rw-r--r--tests/auto/render/opengl/renderviewutils/tst_renderviewutils.cpp801
-rw-r--r--tests/auto/render/opengl/textures/textures.pro15
-rw-r--r--tests/auto/render/opengl/textures/tst_textures.cpp821
33 files changed, 14343 insertions, 0 deletions
diff --git a/tests/auto/render/opengl/computecommand/computecommand.pro b/tests/auto/render/opengl/computecommand/computecommand.pro
new file mode 100644
index 000000000..38515059a
--- /dev/null
+++ b/tests/auto/render/opengl/computecommand/computecommand.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_computecommand
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_computecommand.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/computecommand/tst_computecommand.cpp b/tests/auto/render/opengl/computecommand/tst_computecommand.cpp
new file mode 100644
index 000000000..3caf11c8f
--- /dev/null
+++ b/tests/auto/render/opengl/computecommand/tst_computecommand.cpp
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QTest>
+#include <Qt3DRender/qcomputecommand.h>
+#include <Qt3DRender/private/qcomputecommand_p.h>
+#include <Qt3DRender/private/computecommand_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DCore/private/qbackendnode_p.h>
+#include <Qt3DCore/private/qaspectmanager_p.h>
+#include <Qt3DCore/private/qscene_p.h>
+#include <Qt3DCore/qpropertyupdatedchange.h>
+#include <renderer_p.h>
+#include "qbackendnodetester.h"
+#include "testrenderer.h"
+#include "testpostmanarbiter.h"
+
+
+// tst_Renderer is a friend class of Renderer
+class tst_Renderer : public Qt3DRender::Render::OpenGL::Renderer
+{
+public:
+ tst_Renderer()
+ : Qt3DRender::Render::OpenGL::Renderer(Qt3DRender::QRenderAspect::Synchronous)
+ {}
+
+ ~tst_Renderer() {
+ shutdown();
+ }
+};
+
+
+class tst_ComputeCommand : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkDisablesFrontend()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManager;
+ tst_Renderer renderer;
+ TestArbiter arbiter;
+
+ Qt3DCore::QAspectManager manager;
+ Qt3DCore::QScene scene;
+
+ Qt3DCore::QEntity rootEntity;
+ Qt3DCore::QNodePrivate::get(&rootEntity)->setScene(&scene);
+
+ Qt3DRender::QComputeCommand computeCommand;
+ Qt3DRender::Render::ComputeCommand *backendComputeCommand = nullptr;
+
+ renderer.setNodeManagers(&nodeManager);
+
+ // WHEN
+ computeCommand.setParent(&rootEntity);
+ // RootEntity is the entry point to retrieve the scene instance for lookups
+ manager.setRootEntity(&rootEntity, {});
+
+ // THEN
+ QVERIFY(scene.lookupNode(computeCommand.id()) != nullptr);
+
+ // WHEN
+ auto handle = nodeManager.computeJobManager()->getOrAcquireHandle(computeCommand.id());
+ backendComputeCommand = nodeManager.computeJobManager()->data(handle);
+
+ // WHEN
+ computeCommand.setWorkGroupX(256);
+ computeCommand.setWorkGroupY(512);
+ computeCommand.setWorkGroupZ(128);
+ computeCommand.setRunType(Qt3DRender::QComputeCommand::Manual);
+ computeCommand.trigger(1);
+
+ Qt3DCore::QBackendNodePrivate::get(backendComputeCommand)->setArbiter(&arbiter);
+ backendComputeCommand->setRenderer(&renderer);
+ simulateInitializationSync(&computeCommand, backendComputeCommand);
+
+ // THEN
+ QCOMPARE(backendComputeCommand->frameCount(),1);
+ QCOMPARE(backendComputeCommand->isEnabled(), true);
+ QCOMPARE(computeCommand.isEnabled(), true);
+ QCOMPARE(backendComputeCommand->hasReachedFrameCount(), false);
+
+ // WHEN
+ backendComputeCommand->updateFrameCount();
+
+ // THEN
+ QCOMPARE(backendComputeCommand->frameCount(), 0);
+ QCOMPARE(backendComputeCommand->hasReachedFrameCount(), true);
+
+
+ // Still enabled as we have yet to notify the fronted
+ QCOMPARE(backendComputeCommand->isEnabled(), true);
+ QCOMPARE(computeCommand.isEnabled(), true);
+
+ // WHEN
+ renderer.jobsDone(&manager); // so Renderer::sendDisablesToFrontend gets called
+
+ // THEN
+ QCOMPARE(computeCommand.isEnabled(), false);
+ QCOMPARE(backendComputeCommand->hasReachedFrameCount(), false);
+
+ // WHEN
+ backendComputeCommand->syncFromFrontEnd(&computeCommand, false);
+
+ // THEN
+ QCOMPARE(backendComputeCommand->frameCount(), 0);
+ QCOMPARE(backendComputeCommand->isEnabled(), false);
+ }
+};
+
+QTEST_MAIN(tst_ComputeCommand)
+
+#include "tst_computecommand.moc"
diff --git a/tests/auto/render/opengl/filtercompatibletechniquejob/BLACKLIST b/tests/auto/render/opengl/filtercompatibletechniquejob/BLACKLIST
new file mode 100644
index 000000000..7cbc7c4c0
--- /dev/null
+++ b/tests/auto/render/opengl/filtercompatibletechniquejob/BLACKLIST
@@ -0,0 +1,6 @@
+#[checkRunRendererRunning]
+windows-7
+windows-7sp1
+#QTBUG-64271
+# Offscreen platform does not support OpenGL context creation
+b2qt
diff --git a/tests/auto/render/opengl/filtercompatibletechniquejob/filtercompatibletechniquejob.pro b/tests/auto/render/opengl/filtercompatibletechniquejob/filtercompatibletechniquejob.pro
new file mode 100644
index 000000000..5d533839c
--- /dev/null
+++ b/tests/auto/render/opengl/filtercompatibletechniquejob/filtercompatibletechniquejob.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_filtercompatibletechniquejob
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_filtercompatibletechniquejob.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp b/tests/auto/render/opengl/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
new file mode 100644
index 000000000..e5c34f804
--- /dev/null
+++ b/tests/auto/render/opengl/filtercompatibletechniquejob/tst_filtercompatibletechniquejob.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QTest>
+#include <Qt3DCore/private/qaspectjobmanager_p.h>
+#include <Qt3DCore/private/qnodevisitor_p.h>
+#include <Qt3DCore/private/qnode_p.h>
+#include <Qt3DCore/qentity.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qviewport.h>
+#include <Qt3DRender/private/technique_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <renderer_p.h>
+#include <submissioncontext_p.h>
+#include <filtercompatibletechniquejob_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class TestAspect : public Qt3DRender::QRenderAspect
+{
+public:
+ TestAspect(Qt3DCore::QNode *root)
+ : Qt3DRender::QRenderAspect(Qt3DRender::QRenderAspect::Synchronous)
+ , m_jobManager(new Qt3DCore::QAspectJobManager())
+ , m_window(new QWindow())
+ , m_contextCreationSuccessful(false)
+ {
+ m_window->setSurfaceType(QWindow::OpenGLSurface);
+ m_window->setGeometry(0, 0, 10, 10);
+ m_window->create();
+
+ if (!m_glContext.create()) {
+ qWarning() << "Failed to create OpenGL context";
+ return;
+ }
+
+ if (!m_glContext.makeCurrent(m_window.data())) {
+ qWarning() << "Failed to make OpenGL context current";
+ return;
+ }
+
+ m_contextCreationSuccessful = true;
+
+ Qt3DCore::QAbstractAspectPrivate::get(this)->m_jobManager = m_jobManager.data();
+ QRenderAspect::onRegistered();
+
+ QVector<Qt3DCore::QNode *> nodes;
+ Qt3DCore::QNodeVisitor v;
+ v.traverse(root, [&nodes](Qt3DCore::QNode *node) {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(node);
+ d->m_typeInfo = const_cast<QMetaObject*>(Qt3DCore::QNodePrivate::findStaticMetaObject(node->metaObject()));
+ d->m_hasBackendNode = true;
+ nodes << node;
+ });
+
+ for (const auto node: nodes)
+ d_func()->createBackendNode({
+ node->id(),
+ Qt3DCore::QNodePrivate::get(node)->m_typeInfo,
+ Qt3DCore::NodeTreeChange::Added,
+ node
+ });
+ }
+
+ ~TestAspect()
+ {
+ if (m_contextCreationSuccessful)
+ QRenderAspect::onUnregistered();
+ }
+
+ Qt3DRender::Render::NodeManagers *nodeManagers() const
+ {
+ return d_func()->m_renderer
+ ? d_func()->m_renderer->nodeManagers() : nullptr;
+ }
+
+ bool contextCreationSuccessful() const
+ {
+ return m_contextCreationSuccessful;
+ }
+
+ void initializeRenderer()
+ {
+ renderer()->setOpenGLContext(&m_glContext);
+ d_func()->m_renderer->initialize();
+ renderer()->submissionContext()->beginDrawing(m_window.data());
+ }
+
+ Render::OpenGL::Renderer *renderer() const
+ {
+ return static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
+ }
+
+ void onRegistered() { QRenderAspect::onRegistered(); }
+ void onUnregistered() { QRenderAspect::onUnregistered(); }
+
+private:
+ QScopedPointer<Qt3DCore::QAspectJobManager> m_jobManager;
+ QScopedPointer<QWindow> m_window;
+ QOpenGLContext m_glContext;
+ bool m_contextCreationSuccessful;
+};
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+namespace {
+
+Qt3DCore::QEntity *buildTestScene()
+{
+ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
+
+ // FrameGraph
+ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
+ renderSettings->setActiveFrameGraph(new Qt3DRender::QViewport());
+ root->addComponent(renderSettings);
+
+ // Scene
+ Qt3DRender::QTechnique *gl2Technique = new Qt3DRender::QTechnique(root);
+ gl2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ gl2Technique->graphicsApiFilter()->setMajorVersion(2);
+ gl2Technique->graphicsApiFilter()->setMinorVersion(0);
+ gl2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+
+ Qt3DRender::QTechnique *gl3Technique = new Qt3DRender::QTechnique(root);
+ gl3Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGL);
+ gl3Technique->graphicsApiFilter()->setMajorVersion(3);
+ gl3Technique->graphicsApiFilter()->setMinorVersion(2);
+ gl3Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::CoreProfile);
+
+ Qt3DRender::QTechnique *es2Technique = new Qt3DRender::QTechnique(root);
+ es2Technique->graphicsApiFilter()->setApi(Qt3DRender::QGraphicsApiFilter::OpenGLES);
+ es2Technique->graphicsApiFilter()->setMajorVersion(2);
+ es2Technique->graphicsApiFilter()->setMinorVersion(0);
+ es2Technique->graphicsApiFilter()->setProfile(Qt3DRender::QGraphicsApiFilter::NoProfile);
+
+ return root;
+}
+
+} // anonymous
+
+class tst_FilterCompatibleTechniqueJob : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void initTestCase()
+ {
+ QSurfaceFormat format;
+#ifdef QT_OPENGL_ES_2
+ format.setRenderableType(QSurfaceFormat::OpenGLES);
+#else
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
+ format.setVersion(4, 3);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ }
+#endif
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ QSurfaceFormat::setDefaultFormat(format);
+ }
+
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::Render::OpenGL::FilterCompatibleTechniqueJob backendFilterCompatibleTechniqueJob;
+
+ // THEN
+ QVERIFY(backendFilterCompatibleTechniqueJob.manager() == nullptr);
+ QVERIFY(backendFilterCompatibleTechniqueJob.renderer() == nullptr);
+
+ // WHEN
+ Qt3DRender::Render::TechniqueManager techniqueManager;
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ backendFilterCompatibleTechniqueJob.setManager(&techniqueManager);
+ backendFilterCompatibleTechniqueJob.setRenderer(&renderer);
+
+ // THEN
+ QCOMPARE(backendFilterCompatibleTechniqueJob.manager(), &techniqueManager);
+ QCOMPARE(backendFilterCompatibleTechniqueJob.renderer(), &renderer);
+
+ renderer.shutdown();
+ }
+
+ void checkRunRendererRunning()
+ {
+ // GIVEN
+ Qt3DRender::Render::OpenGL::FilterCompatibleTechniqueJob backendFilterCompatibleTechniqueJob;
+ Qt3DRender::TestAspect testAspect(buildTestScene());
+
+ const bool unableToCreateContext = !testAspect.contextCreationSuccessful();
+
+ if (unableToCreateContext)
+ QSKIP("Initialization failed, unable to create GL context");
+
+ // WHEN
+ Qt3DRender::Render::NodeManagers *nodeManagers = testAspect.nodeManagers();
+ QVERIFY(nodeManagers);
+ Qt3DRender::Render::TechniqueManager *techniqueManager = nodeManagers->techniqueManager();
+ QVERIFY(techniqueManager);
+ backendFilterCompatibleTechniqueJob.setManager(techniqueManager);
+ backendFilterCompatibleTechniqueJob.setRenderer(testAspect.renderer());
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.renderer()->isRunning(), true);
+ QCOMPARE(testAspect.renderer()->submissionContext()->isInitialized(), true);
+ const QVector<Qt3DRender::Render::HTechnique> handles = testAspect.nodeManagers()->techniqueManager()->activeHandles();
+ QCOMPARE(handles.size(), 3);
+
+ // WHEN
+ backendFilterCompatibleTechniqueJob.run();
+
+ // THEN -> empty if job ran properly
+ const QVector<Qt3DCore::QNodeId> dirtyTechniquesId = testAspect.nodeManagers()->techniqueManager()->takeDirtyTechniques();
+ QCOMPARE(dirtyTechniquesId.size(), 0);
+
+ // Check at least one technique is valid
+ bool foundValid = false;
+ for (const auto handle: handles) {
+ Qt3DRender::Render::Technique *technique = testAspect.nodeManagers()->techniqueManager()->data(handle);
+ foundValid |= technique->isCompatibleWithRenderer();
+ }
+ QCOMPARE(foundValid, true);
+ }
+};
+
+QTEST_MAIN(tst_FilterCompatibleTechniqueJob)
+
+#include "tst_filtercompatibletechniquejob.moc"
diff --git a/tests/auto/render/opengl/glshadermanager/glshadermanager.pro b/tests/auto/render/opengl/glshadermanager/glshadermanager.pro
new file mode 100644
index 000000000..dc96ab3bb
--- /dev/null
+++ b/tests/auto/render/opengl/glshadermanager/glshadermanager.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_glshadermanager
+
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_glshadermanager.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/glshadermanager/tst_glshadermanager.cpp b/tests/auto/render/opengl/glshadermanager/tst_glshadermanager.cpp
new file mode 100644
index 000000000..6a5bc87ea
--- /dev/null
+++ b/tests/auto/render/opengl/glshadermanager/tst_glshadermanager.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <glresourcemanagers_p.h>
+#include <Qt3DCore/qnodeid.h>
+#include "qbackendnodetester.h"
+#include "testrenderer.h"
+
+class tst_GLShaderManager : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void adopt();
+ void lookupResource();
+ void abandon();
+ void insertAfterRemoval();
+};
+
+void tst_GLShaderManager::adopt()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::GLShaderManager cache;
+ Qt3DRender::QShaderProgram frontendShader1;
+ Qt3DRender::QShaderProgram frontendShader2;
+ TestRenderer renderer;
+ Qt3DRender::Render::Shader backendShaderNode1;
+ Qt3DRender::Render::Shader backendShaderNode2;
+
+ backendShaderNode1.setRenderer(&renderer);
+ backendShaderNode2.setRenderer(&renderer);
+ simulateInitialization(&frontendShader1, &backendShaderNode1);
+ simulateInitialization(&frontendShader2, &backendShaderNode2);
+
+ // THEN
+ QVERIFY(cache.lookupResource(backendShaderNode1.peerId()) == nullptr);
+ QVERIFY(cache.lookupResource(backendShaderNode2.peerId()) == nullptr);
+ QVERIFY(backendShaderNode1.peerId() != backendShaderNode2.peerId());
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::GLShader *glShader1 = cache.createOrAdoptExisting(&backendShaderNode1);
+
+ // THEN
+ QVERIFY(glShader1 != nullptr);
+ QVector<Qt3DCore::QNodeId> shaderNodeIds = cache.shaderIdsForProgram(glShader1);
+ QCOMPARE(shaderNodeIds.size(), 1);
+ QCOMPARE(shaderNodeIds.first(), backendShaderNode1.peerId());
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::GLShader *glShader2 = cache.createOrAdoptExisting(&backendShaderNode2);
+
+ // THEN
+ QCOMPARE(glShader1, glShader2);
+
+ shaderNodeIds = cache.shaderIdsForProgram(glShader2);
+ QCOMPARE(shaderNodeIds.size(), 2);
+ QCOMPARE(shaderNodeIds.first(), backendShaderNode1.peerId());
+ QCOMPARE(shaderNodeIds.last(), backendShaderNode2.peerId());
+}
+
+void tst_GLShaderManager::lookupResource()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::GLShaderManager cache;
+ Qt3DRender::QShaderProgram frontendShader1;
+ Qt3DRender::QShaderProgram frontendShader2;
+ TestRenderer renderer;
+ Qt3DRender::Render::Shader backendShaderNode1;
+ Qt3DRender::Render::Shader backendShaderNode2;
+
+ backendShaderNode1.setRenderer(&renderer);
+ backendShaderNode2.setRenderer(&renderer);
+ simulateInitialization(&frontendShader1, &backendShaderNode1);
+ simulateInitialization(&frontendShader2, &backendShaderNode2);
+
+ // WHEN
+ cache.createOrAdoptExisting(&backendShaderNode1);
+ cache.createOrAdoptExisting(&backendShaderNode2);
+
+ // THEN
+ Qt3DRender::Render::OpenGL::GLShader *glShader1 = cache.lookupResource(backendShaderNode1.peerId());
+ Qt3DRender::Render::OpenGL::GLShader *glShader2 = cache.lookupResource(backendShaderNode2.peerId());
+ QVERIFY(glShader1 != nullptr);
+ QCOMPARE(glShader1, glShader2);
+ const QVector<Qt3DCore::QNodeId> shaderNodeIds = cache.shaderIdsForProgram(glShader1);
+ QCOMPARE(shaderNodeIds.size(), 2);
+ QVERIFY(shaderNodeIds.contains(frontendShader1.id()));
+ QVERIFY(shaderNodeIds.contains(frontendShader2.id()));
+}
+
+void tst_GLShaderManager::abandon()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::GLShaderManager cache;
+ Qt3DRender::QShaderProgram frontendShader1;
+ Qt3DRender::QShaderProgram frontendShader2;
+ TestRenderer renderer;
+ Qt3DRender::Render::Shader backendShaderNode1;
+ Qt3DRender::Render::Shader backendShaderNode2;
+
+ backendShaderNode1.setRenderer(&renderer);
+ backendShaderNode2.setRenderer(&renderer);
+ simulateInitialization(&frontendShader1, &backendShaderNode1);
+ simulateInitialization(&frontendShader2, &backendShaderNode2);
+ cache.createOrAdoptExisting(&backendShaderNode1);
+ cache.createOrAdoptExisting(&backendShaderNode2);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::GLShader *glShader = cache.lookupResource(backendShaderNode1.peerId());
+ cache.abandon(glShader, &backendShaderNode1);
+
+ // THEN
+ QVector<Qt3DCore::QNodeId> shaderNodeIds = cache.shaderIdsForProgram(glShader);
+ QVERIFY(cache.takeAbandonned().isEmpty());
+ QCOMPARE(shaderNodeIds.size(), 1);
+ QCOMPARE(shaderNodeIds.first(), backendShaderNode2.peerId());
+
+ // WHEN
+ cache.abandon(glShader, &backendShaderNode2);
+
+ // THEN
+ shaderNodeIds = cache.shaderIdsForProgram(glShader);
+ QCOMPARE(shaderNodeIds.size(), 0);
+ const QVector<Qt3DRender::Render::OpenGL::GLShader *> releasedShaders = cache.takeAbandonned();
+ QCOMPARE(releasedShaders.size(), 1);
+ QCOMPARE(releasedShaders.first(), glShader);
+}
+
+void tst_GLShaderManager::insertAfterRemoval()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::GLShaderManager cache;
+ Qt3DRender::QShaderProgram frontendShader;
+ TestRenderer renderer;
+ Qt3DRender::Render::Shader backendShaderNode;
+
+
+ backendShaderNode.setRenderer(&renderer);
+ simulateInitialization(&frontendShader, &backendShaderNode);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::GLShader *apiShader1 = cache.createOrAdoptExisting(&backendShaderNode);
+ const Qt3DRender::Render::OpenGL::GLShader *originalApiShader = apiShader1;
+
+ // THEN
+ auto apiShader2 = cache.lookupResource(frontendShader.id());
+ QVERIFY(apiShader1 != nullptr);
+ QVERIFY(apiShader2 != nullptr);
+ QVERIFY(apiShader1 == originalApiShader);
+ QVERIFY(apiShader1 == apiShader2);
+
+ // WHEN
+ cache.abandon(apiShader1, &backendShaderNode);
+
+ // THEN
+ Qt3DRender::Render::OpenGL::GLShader *apiShaderEmpty = cache.lookupResource(frontendShader.id());
+ QVERIFY(apiShaderEmpty == nullptr);
+
+ // WHEN
+ apiShader1 = cache.createOrAdoptExisting(&backendShaderNode);
+ cache.purge();
+ apiShader2 = cache.lookupResource(frontendShader.id());
+
+ // THEN
+ QVERIFY(apiShader1 != nullptr);
+ QVERIFY(apiShader2 != nullptr);
+ QVERIFY(apiShader1 == apiShader2);
+ QVERIFY(apiShader2 == originalApiShader);
+}
+
+QTEST_APPLESS_MAIN(tst_GLShaderManager)
+
+#include "tst_glshadermanager.moc"
diff --git a/tests/auto/render/opengl/graphicshelpergl2/graphicshelpergl2.pro b/tests/auto/render/opengl/graphicshelpergl2/graphicshelpergl2.pro
new file mode 100644
index 000000000..eb8ba7f04
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl2/graphicshelpergl2.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+
+TARGET = tst_graphicshelpergl2
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib openglextensions
+
+CONFIG += testcase
+
+SOURCES += \
+ tst_graphicshelpergl2.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/graphicshelpergl2/tst_graphicshelpergl2.cpp b/tests/auto/render/opengl/graphicshelpergl2/tst_graphicshelpergl2.cpp
new file mode 100644
index 000000000..5a1d22283
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl2/tst_graphicshelpergl2.cpp
@@ -0,0 +1,1606 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DRender/qrendertargetoutput.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <graphicshelpergl2_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <QtOpenGLExtensions/QOpenGLExtensions>
+#include <QOpenGLContext>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions_2_0>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QSurfaceFormat>
+
+#ifndef QT_OPENGL_ES_2
+
+#define TEST_SHOULD_BE_PERFORMED 1
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt3DRender;
+using namespace Qt3DRender::Render;
+using namespace Qt3DRender::Render::OpenGL;
+
+namespace {
+
+const QByteArray vertCode = QByteArrayLiteral(
+ "#version 120\n" \
+ "attribute vec3 vertexPosition;\n" \
+ "attribute vec2 vertexTexCoord;\n" \
+ "varying vec2 texCoord;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloat = QByteArrayLiteral(
+ "#version 120\n" \
+ "uniform float multiplier;\n" \
+ "uniform vec2 multiplierVec2;\n" \
+ "uniform vec3 multiplierVec3;\n" \
+ "uniform vec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " vec4 randomMult = multiplierVec4 + vec4(multiplierVec3, 0.0) + vec4(multiplierVec2, 0.0, 0.0);\n" \
+ " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsInt = QByteArrayLiteral(
+ "#version 120\n" \
+ "uniform int multiplier;\n" \
+ "uniform ivec2 multiplierVec2;\n" \
+ "uniform ivec3 multiplierVec3;\n" \
+ "uniform ivec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " ivec4 randomMult = multiplierVec4 + ivec4(multiplierVec3, 0) + ivec4(multiplierVec2, 0, 0);\n" \
+ " gl_FragColor = ivec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloatMatrices = QByteArrayLiteral(
+ "#version 120\n" \
+ "uniform mat2 m2;\n" \
+ "uniform mat2x3 m23;\n" \
+ "uniform mat3x2 m32;\n" \
+ "uniform mat2x4 m24;\n" \
+ "uniform mat4x2 m42;\n" \
+ "uniform mat3 m3;\n" \
+ "uniform mat3x4 m34;\n" \
+ "uniform mat4x3 m43;\n" \
+ "uniform mat4 m4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " float lengthSum = m2[0][0] + m23[0][0] + m32[0][0] + m24[0][0] + m42[0][0] + m3[0][0] + m34[0][0] + m43[0][0] + m4[0][0];\n" \
+ " gl_FragColor = vec4(1, 0, 0, 1) * lengthSum;\n" \
+ "}\n");
+
+
+const QByteArray fragCodeSamplers = QByteArrayLiteral(
+ "#version 120\n" \
+ "varying vec2 texCoord;\n" \
+ "uniform sampler1D s1;\n" \
+ "uniform sampler2D s2;\n" \
+ "uniform sampler3D s3;\n" \
+ "uniform samplerCube scube;\n" \
+ "void main()\n" \
+ "{\n" \
+ " gl_FragColor = vec4(1, 0, 0, 1) *" \
+ " texture1D(s1, texCoord.x) *" \
+ " texture2D(s2, texCoord) *" \
+ " texture3D(s3, vec3(texCoord, 0.0)) *" \
+ " textureCube(scube, vec3(texCoord, 0));\n" \
+ "}\n");
+
+} // anonymous
+
+class tst_GraphicsHelperGL2 : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+
+ void init()
+ {
+ m_window.reset(new QWindow);
+ m_window->setSurfaceType(QWindow::OpenGLSurface);
+ m_window->setGeometry(0, 0, 10, 10);
+ m_window->create();
+
+ QSurfaceFormat format;
+ format.setVersion(2, 0);
+ format.setProfile(QSurfaceFormat::NoProfile);
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ m_window->setFormat(format);
+ m_glContext.setFormat(format);
+
+ if (!m_glContext.create()) {
+ qWarning() << "Failed to create OpenGL context";
+ return;
+ }
+
+ if (!m_glContext.makeCurrent(m_window.data())) {
+ qWarning() << "Failed to make OpenGL context current";
+ return;
+ }
+
+ if ((m_func = m_glContext.versionFunctions<QOpenGLFunctions_2_0>()) != nullptr) {
+ if (m_glContext.hasExtension(QByteArrayLiteral("GL_ARB_framebuffer_object"))) {
+ m_fboFuncs = new QOpenGLExtension_ARB_framebuffer_object();
+ m_fboFuncs->initializeOpenGLFunctions();
+ }
+ m_glHelper.initializeHelper(&m_glContext, m_func);
+ m_initializationSuccessful = true;
+ }
+ }
+
+ void cleanup()
+ {
+ m_glContext.doneCurrent();
+ }
+
+ void alphaTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Deprecated
+ }
+
+ void bindBufferBase()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // Not supported by GL2
+ }
+
+ void bindFragDataLocation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void bindFrameBufferAttachment()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA32F);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_fboFuncs->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureAttachmentId = 0;
+ m_fboFuncs->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ &textureAttachmentId);
+ QCOMPARE(GLuint(textureAttachmentId), texture.textureId());
+
+ // Restore state
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+
+ // THEN
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBORead);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // Cleanup
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void bindUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void blendEquation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ GLint equation = 0;
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_ADD);
+
+ // WHEN
+ m_glHelper.blendEquation(GL_FUNC_REVERSE_SUBTRACT);
+
+ // THEN
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_REVERSE_SUBTRACT);
+ }
+
+ void blendFunci()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void blendFuncSeparatei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void boundFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ // WHEN
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ // THEN
+ GLint boundBuffer = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundBuffer);
+ QCOMPARE(GLuint(boundBuffer), fboId);
+
+ // THEN
+ QCOMPARE(m_glHelper.boundFrameBufferObject(), fboId);
+
+ // Reset state
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void checkFrameBufferComplete()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_fboFuncs->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ QVERIFY(m_glHelper.checkFrameBufferComplete());
+
+ // Restore
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void clearBufferf()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void createFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // WHEN
+ const GLuint fboId = m_glHelper.createFrameBufferObject();
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // Restore
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void depthMask()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ GLboolean depthWritingEnabled = false;
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+
+ // THEN
+ QVERIFY(depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_FALSE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(!depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_TRUE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(depthWritingEnabled);
+ }
+
+ void depthTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_DEPTH_TEST);
+ m_func->glDepthFunc(GL_LESS);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LEQUAL);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ GLint depthMode = 0;
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LEQUAL);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LESS);
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LESS);
+ }
+
+ void disableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_CLIP_DISTANCE0 + 5);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+
+ // WHEN
+ m_glHelper.disableClipPlane(5);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+ }
+
+ void disablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void disablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void drawBuffers()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ QOpenGLTexture *textures[4];
+
+ // Create 4 attachments
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+ // THEN
+ GLenum status = m_fboFuncs->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // WHEN
+ GLenum bufferEnum = GL_COLOR_ATTACHMENT4;
+ m_func->glDrawBuffers(1, &bufferEnum);
+
+ // THEN
+ GLint enumValue = -1;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT4);
+
+ // WHEN
+ GLint newBufferEnum = 2;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // WHEN
+ newBufferEnum = 0;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // Restore
+ m_fboFuncs->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_fboFuncs->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void enableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_CLIP_DISTANCE0 + 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+
+ // WHEN
+ m_glHelper.enableClipPlane(4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+ }
+
+ void enablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void enablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void enableVertexAttribute()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+ shaderProgram.bind();
+
+ // WHEN
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void frontFace()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glFrontFace(GL_CW);
+
+ // THEN
+ GLint face = 0;
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CW);
+
+ // WHEN
+ m_glHelper.frontFace(GL_CCW);
+
+ // THEN
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CCW);
+ }
+
+ void getRenderBufferDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void getTextureDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+
+ // WHEN
+ const QSize dimensions = m_glHelper.getTextureDimensions(texture.textureId(), GL_TEXTURE_2D);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+ }
+
+ void pointSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // WHEN
+ m_glHelper.pointSize(false, 0.5f);
+ // THEN
+ GLfloat size = 0.0f;
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 0.5f);
+ }
+
+ void maxClipPlaneCount()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ GLint maxCount = -1;
+ m_func->glGetIntegerv(GL_MAX_CLIP_PLANES, &maxCount);
+
+ // THEN
+ QCOMPARE(maxCount, m_glHelper.maxClipPlaneCount());
+ }
+
+ void programUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // Not supported by GL2
+ }
+
+ void programAttributesAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderAttribute> activeAttributes = m_glHelper.programAttributesAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeAttributes.size(), 2);
+ std::sort(activeAttributes.begin(), activeAttributes.end(), [] (const ShaderAttribute &a, const ShaderAttribute &b) { return a.m_name < b.m_name; });
+
+ const ShaderAttribute attribute1 = activeAttributes.at(0);
+ QCOMPARE(attribute1.m_name, QStringLiteral("vertexPosition"));
+ QCOMPARE(attribute1.m_size, 1);
+ QCOMPARE(attribute1.m_location, shaderProgram.attributeLocation("vertexPosition"));
+ QCOMPARE(attribute1.m_type, GLenum(GL_FLOAT_VEC3));
+
+ const ShaderAttribute attribute2 = activeAttributes.at(1);
+ QCOMPARE(attribute2.m_name, QStringLiteral("vertexTexCoord"));
+ QCOMPARE(attribute2.m_size, 1);
+ QCOMPARE(attribute2.m_location, shaderProgram.attributeLocation("vertexTexCoord"));
+ QCOMPARE(attribute2.m_type, GLenum(GL_FLOAT_VEC2));
+ }
+
+ void programUniformsAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniforms.size(), 4);
+ std::sort(activeUniforms.begin(), activeUniforms.end(), [] (const ShaderUniform &a, const ShaderUniform &b) { return a.m_name < b.m_name; });
+
+ const ShaderUniform uniform1 = activeUniforms.at(0);
+ QCOMPARE(uniform1.m_location, shaderProgram.uniformLocation("multiplier"));
+ QCOMPARE(uniform1.m_offset, -1);
+ QCOMPARE(uniform1.m_blockIndex, -1);
+ QCOMPARE(uniform1.m_arrayStride, -1);
+ QCOMPARE(uniform1.m_matrixStride, -1);
+ QCOMPARE(uniform1.m_size, 1);
+ QCOMPARE(uniform1.m_type, GLenum(GL_FLOAT));
+ QCOMPARE(uniform1.m_name, QStringLiteral("multiplier"));
+
+ const ShaderUniform uniform2 = activeUniforms.at(1);
+ QCOMPARE(uniform2.m_location, shaderProgram.uniformLocation("multiplierVec2"));
+ QCOMPARE(uniform2.m_offset, -1);
+ QCOMPARE(uniform2.m_blockIndex, -1);
+ QCOMPARE(uniform2.m_arrayStride, -1);
+ QCOMPARE(uniform2.m_matrixStride, -1);
+ QCOMPARE(uniform2.m_size, 1);
+ QCOMPARE(uniform2.m_type, GLenum(GL_FLOAT_VEC2));
+ QCOMPARE(uniform2.m_name, QStringLiteral("multiplierVec2"));
+
+ const ShaderUniform uniform3 = activeUniforms.at(2);
+ QCOMPARE(uniform3.m_location, shaderProgram.uniformLocation("multiplierVec3"));
+ QCOMPARE(uniform3.m_offset, -1);
+ QCOMPARE(uniform3.m_blockIndex, -1);
+ QCOMPARE(uniform3.m_arrayStride, -1);
+ QCOMPARE(uniform3.m_matrixStride, -1);
+ QCOMPARE(uniform3.m_size, 1);
+ QCOMPARE(uniform3.m_type, GLenum(GL_FLOAT_VEC3));
+ QCOMPARE(uniform3.m_name, QStringLiteral("multiplierVec3"));
+
+ const ShaderUniform uniform4 = activeUniforms.at(3);
+ QCOMPARE(uniform4.m_location, shaderProgram.uniformLocation("multiplierVec4"));
+ QCOMPARE(uniform4.m_offset, -1);
+ QCOMPARE(uniform4.m_blockIndex, -1);
+ QCOMPARE(uniform4.m_arrayStride, -1);
+ QCOMPARE(uniform4.m_matrixStride, -1);
+ QCOMPARE(uniform4.m_size, 1);
+ QCOMPARE(uniform4.m_type, GLenum(GL_FLOAT_VEC4));
+ QCOMPARE(uniform4.m_name, QStringLiteral("multiplierVec4"));
+ }
+
+ void programShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void releaseFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ if (!m_fboFuncs)
+ QSKIP("FBO not supported by OpenGL 2.0");
+ // GIVEN
+ GLuint fboId;
+ m_fboFuncs->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.releaseFrameBufferObject(fboId);
+
+ // THEN
+ QVERIFY(!m_fboFuncs->glIsFramebuffer(fboId));
+ }
+
+ void setMSAAEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+ }
+
+ void setAlphaCoverageEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+ }
+
+ void setClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // Deprecated in 3.3 core
+ }
+
+ void setSeamlessCubemap()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported in GL2
+ }
+
+ void setVerticesPerPatch()
+ {
+ // Not supported in GL2
+ }
+
+#define SUPPORTS_FEATURE(Feature, IsSupported) \
+ QVERIFY(m_glHelper.supportsFeature(Feature) == IsSupported);
+
+ void supportsFeature()
+ {
+ SUPPORTS_FEATURE(GraphicsHelperInterface::MRT, (m_fboFuncs != nullptr));
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BindableFragmentOutputs, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::PrimitiveRestart, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::RenderBufferDimensionRetrieval, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::TextureDimensionRetrieval, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::ShaderStorageObject, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Compute, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::DrawBuffersBlend, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Tessellation, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BlitFramebuffer, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::IndirectDrawing, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::MapBuffer, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Fences, false);
+ }
+
+
+#define ADD_UNIFORM_ENTRY(FragShader, Name, Type, ComponentSize, ExpectedRawSize) \
+ QTest::newRow(#FragShader"_"#Type) << FragShader << QStringLiteral(Name) << Type << ComponentSize << ExpectedRawSize;
+
+ void uniformsByteSize_data()
+ {
+ QTest::addColumn<QByteArray>("fragShader");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<int>("componentSize");
+ QTest::addColumn<int>("expectedByteSize");
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplier", GL_FLOAT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec2", GL_FLOAT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec3",GL_FLOAT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec4", GL_FLOAT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplier", GL_INT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec2", GL_INT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec3", GL_INT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec4", GL_INT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m2", GL_FLOAT_MAT2, 1, 4 * 2 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m23", GL_FLOAT_MAT2x3, 1, 4 * 2 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m32", GL_FLOAT_MAT3x2, 1, 4 * 3 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m24", GL_FLOAT_MAT2x4, 1, 4 * 2 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m42", GL_FLOAT_MAT4x2, 1, 4 * 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m3", GL_FLOAT_MAT3, 1, 4 * 3 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m34", GL_FLOAT_MAT3x4, 1, 4 * 3 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m43", GL_FLOAT_MAT4x3, 1, 4 * 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m4", GL_FLOAT_MAT4, 1, 4 * 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s1", GL_SAMPLER_1D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s2", GL_SAMPLER_2D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s3", GL_SAMPLER_3D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "scube", GL_SAMPLER_CUBE, 1, 4);
+ }
+
+ void uniformsByteSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QFETCH(QByteArray, fragShader);
+ QFETCH(QString, name);
+ QFETCH(int, type);
+ QFETCH(int, componentSize);
+ QFETCH(int, expectedByteSize);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShader);
+ QVERIFY(shaderProgram.link());
+
+ GLint location = shaderProgram.uniformLocation(name);
+ // WHEN
+ const QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+ ShaderUniform matchingUniform;
+ for (const ShaderUniform &u : activeUniforms) {
+ if (u.m_location == location) {
+ matchingUniform = u;
+ break;
+ }
+ }
+
+ // THEN
+ QCOMPARE(matchingUniform.m_location, location);
+ QCOMPARE(matchingUniform.m_type, GLuint(type));
+ QCOMPARE(matchingUniform.m_size, componentSize);
+
+ // WHEN
+ const int computedRawByteSize = m_glHelper.uniformByteSize(matchingUniform);
+
+ // THEN
+ QCOMPARE(expectedByteSize, computedRawByteSize);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void useProgram()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+
+ GLint currentProg = 0;
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+
+ // WHEN
+ m_glHelper.useProgram(shaderProgram.programId());
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QCOMPARE(GLuint(currentProg), shaderProgram.programId());
+
+ // WHEN
+ m_glHelper.useProgram(0);
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+ }
+
+ void vertexAttribDivisor()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not available in 3.2
+ }
+
+ void vertexAttributePointer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+
+ const int vertexCount = 99;
+ QOpenGLBuffer positionBuffer(QOpenGLBuffer::VertexBuffer);
+ positionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ positionBuffer.create();
+ positionBuffer.bind();
+ positionBuffer.allocate(vertexCount * sizeof(QVector3D));
+
+ QOpenGLBuffer texCoordBuffer(QOpenGLBuffer::VertexBuffer);
+ texCoordBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ texCoordBuffer.create();
+ texCoordBuffer.allocate(vertexCount * sizeof(QVector2D));
+
+ // WHEN
+ shaderProgram.bind();
+ positionBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC3, positionLocation, 3, GL_FLOAT, GL_TRUE, 0, 0);
+
+ texCoordBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC2, texCoordLocation, 2, GL_FLOAT, GL_TRUE, 0, 0);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void glUniform1fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat value = 883.0f;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1fv(location, 1, &value);
+
+ // THEN
+ GLfloat setValue = 0.0f;
+ m_func->glGetUniformfv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[2] = { 383.0f, 427.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[2] = { 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[3] = { 572.0f, 1340.0f, 1584.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[3] = { 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint value = 883;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1iv(location, 1, &value);
+
+ // THEN
+ GLint setValue = 0;
+ m_func->glGetUniformiv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[2] = { 383, 427 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2iv(location, 1, values);
+
+ // THEN
+ GLint setValues[2] = { 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[3] = { 572, 1340, 1584 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3iv(location, 1, values);
+
+ // THEN
+ GLint setValues[3] = { 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[4] = { 454, 350, 883, 355 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4iv(location, 1, values);
+
+ // THEN
+ GLint setValues[4] = { 0, 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniform2uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniform3uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniform4uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("m2");
+ m_glHelper.glUniformMatrix2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[9] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f };
+ const GLint location = shaderProgram.uniformLocation("m3");
+ m_glHelper.glUniformMatrix3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 9; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[16] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f, 1603.0f, 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 327.0f };
+ const GLint location = shaderProgram.uniformLocation("m4");
+ m_glHelper.glUniformMatrix4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 16; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix3x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix2x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix4x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix3x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void glUniformMatrix4x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void blitFramebuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+#define ADD_GL_TYPE_ENTRY(Type, Expected) \
+ QTest::newRow(#Type) << Type << Expected;
+
+ void uniformTypeFromGLType_data()
+ {
+ QTest::addColumn<int>("glType");
+ QTest::addColumn<UniformType>("expected");
+
+ ADD_GL_TYPE_ENTRY(GL_FLOAT, UniformType::Float);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_INT, UniformType::Int);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC2, UniformType::IVec2);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC3, UniformType::IVec3);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC4, UniformType::IVec4);
+ ADD_GL_TYPE_ENTRY(GL_BOOL, UniformType::Bool);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC2, UniformType::BVec2);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC3, UniformType::BVec3);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC4, UniformType::BVec4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2, UniformType::Mat2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3, UniformType::Mat3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4, UniformType::Mat4);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE, UniformType::Sampler);
+ }
+
+ void uniformTypeFromGLType()
+ {
+ // GIVEN
+ QFETCH(int, glType);
+ QFETCH(UniformType, expected);
+
+ // WHEN
+ UniformType computed = m_glHelper.uniformTypeFromGLType(glType);
+
+ // THEN
+ QCOMPARE(computed, expected);
+ }
+
+ void drawBuffer()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void readBuffer()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void fenceSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void clientWaitSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void waitSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void wasSyncSignaled()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void deleteSync()
+ {
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+ // Not supported by GL2
+ }
+
+ void rasterMode()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 2.0 functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.rasterMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_POLYGON_MODE, &p);
+ QCOMPARE(p, GL_LINE);
+ }
+
+private:
+ QScopedPointer<QWindow> m_window;
+ QOpenGLContext m_glContext;
+ GraphicsHelperGL2 m_glHelper;
+ QOpenGLFunctions_2_0 *m_func = nullptr;
+ QOpenGLExtension_ARB_framebuffer_object *m_fboFuncs = nullptr;
+ bool m_initializationSuccessful = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
+
+int main(int argc, char *argv[])
+{
+#ifdef TEST_SHOULD_BE_PERFORMED
+ QGuiApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_GraphicsHelperGL2 tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+#endif
+ return 0;
+}
+
+#ifdef TEST_SHOULD_BE_PERFORMED
+#include "tst_graphicshelpergl2.moc"
+#endif
diff --git a/tests/auto/render/opengl/graphicshelpergl3_2/graphicshelpergl3_2.pro b/tests/auto/render/opengl/graphicshelpergl3_2/graphicshelpergl3_2.pro
new file mode 100644
index 000000000..a613f1279
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl3_2/graphicshelpergl3_2.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+
+TARGET = tst_graphicshelpergl3_2
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += \
+ tst_graphicshelpergl3_2.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp b/tests/auto/render/opengl/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp
new file mode 100644
index 000000000..08c30dc15
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl3_2/tst_graphicshelpergl3_2.cpp
@@ -0,0 +1,2295 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DRender/qrendertargetoutput.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <graphicshelpergl3_2_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions_3_2_Core>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QSurfaceFormat>
+
+#if !defined(QT_OPENGL_ES_2) && defined(QT_OPENGL_3_2)
+
+#define TEST_SHOULD_BE_PERFORMED 1
+
+using namespace Qt3DRender;
+using namespace Qt3DRender::Render;
+using namespace Qt3DRender::Render::OpenGL;
+
+namespace {
+
+const QByteArray vertCode = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "in vec3 vertexPosition;\n" \
+ "in vec2 vertexTexCoord;\n" \
+ "out vec2 texCoord;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray vertCodeUniformBuffer = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "in vec3 vertexPosition;\n" \
+ "in vec2 vertexTexCoord;\n" \
+ "in int vertexColorIndex;\n" \
+ "out vec2 texCoord;\n" \
+ "flat out int colorIndex;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " colorIndex = vertexColorIndex;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeFragOutputs = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "out vec2 temp;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0);\n" \
+ " temp = vec2(1.0, 0.3);\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloat = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "uniform float multiplier;\n" \
+ "uniform vec2 multiplierVec2;\n" \
+ "uniform vec3 multiplierVec3;\n" \
+ "uniform vec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " vec4 randomMult = multiplierVec4 + vec4(multiplierVec3, 0.0) + vec4(multiplierVec2, 0.0, 0.0);\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsInt = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "uniform int multiplier;\n" \
+ "uniform ivec2 multiplierVec2;\n" \
+ "uniform ivec3 multiplierVec3;\n" \
+ "uniform ivec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " ivec4 randomMult = multiplierVec4 + ivec4(multiplierVec3, 0) + ivec4(multiplierVec2, 0, 0);\n" \
+ " color = ivec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsUInt = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "uniform uint multiplier;\n" \
+ "uniform uvec2 multiplierVec2;\n" \
+ "uniform uvec3 multiplierVec3;\n" \
+ "uniform uvec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " uvec4 randomMult = multiplierVec4 + uvec4(multiplierVec3, 0) + uvec4(multiplierVec2, 0, 0);\n" \
+ " color = uvec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloatMatrices = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "uniform mat2 m2;\n" \
+ "uniform mat2x3 m23;\n" \
+ "uniform mat3x2 m32;\n" \
+ "uniform mat2x4 m24;\n" \
+ "uniform mat4x2 m42;\n" \
+ "uniform mat3 m3;\n" \
+ "uniform mat3x4 m34;\n" \
+ "uniform mat4x3 m43;\n" \
+ "uniform mat4 m4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " float lengthSum = m2[0][0] + m23[0][0] + m32[0][0] + m24[0][0] + m42[0][0] + m3[0][0] + m34[0][0] + m43[0][0] + m4[0][0];\n" \
+ " color = vec4(1, 0, 0, 1) * lengthSum;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformBuffer = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "out vec4 color;\n" \
+ "in vec2 texCoord;\n" \
+ "flat in int colorIndex;\n" \
+ "uniform ColorArray\n" \
+ "{\n" \
+ " vec4 colors[256];\n" \
+ "};\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = colors[colorIndex] + vec4(texCoord.s, texCoord.t, 0.0, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeSamplers = QByteArrayLiteral(
+ "#version 150 core\n" \
+ "in vec2 texCoord;\n" \
+ "out vec4 color;\n" \
+ "uniform sampler1D s1;\n" \
+ "uniform sampler2D s2;\n" \
+ "uniform sampler2DArray s2a;\n" \
+ "uniform sampler3D s3;\n" \
+ "uniform samplerCube scube;\n" \
+ "uniform sampler2DRect srect;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1, 0, 0, 1) *" \
+ " texture(s1, texCoord.x) *" \
+ " texture(s2, texCoord) *" \
+ " texture(s2a, vec3(texCoord, 0.0)) *" \
+ " texture(s3, vec3(texCoord, 0.0)) *" \
+ " texture(scube, vec3(texCoord, 0)) *" \
+ " texture(srect, texCoord);\n" \
+ "}\n");
+
+} // anonymous
+
+class tst_GraphicsHelperGL3_2 : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+
+ void init()
+ {
+ m_window.reset(new QWindow);
+ m_window->setSurfaceType(QWindow::OpenGLSurface);
+ m_window->setGeometry(0, 0, 10, 10);
+ m_window->create();
+
+ QSurfaceFormat format;
+ format.setVersion(3, 2);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ m_window->setFormat(format);
+ m_glContext.setFormat(format);
+
+ if (!m_glContext.create()) {
+ qWarning() << "Failed to create OpenGL context";
+ return;
+ }
+
+ if (!m_glContext.makeCurrent(m_window.data())) {
+ qWarning() << "Failed to make OpenGL context current";
+ return;
+ }
+
+ if ((m_func = m_glContext.versionFunctions<QOpenGLFunctions_3_2_Core>()) != nullptr) {
+ m_glHelper.initializeHelper(&m_glContext, m_func);
+ m_initializationSuccessful = true;
+ }
+ }
+
+ void cleanup()
+ {
+ m_glContext.doneCurrent();
+ }
+
+ void alphaTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+ // Deprecated
+ }
+
+ void bindBufferBase()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint bufferId = 0;
+ // WHEN
+ m_func->glGenBuffers(1, &bufferId);
+ // THEN
+ QVERIFY(bufferId != 0);
+
+
+ // WHEN
+ m_func->glBindBuffer(GL_UNIFORM_BUFFER, bufferId);
+ m_glHelper.bindBufferBase(GL_UNIFORM_BUFFER, 2, bufferId);
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundToPointBufferId = 0;
+ m_func->glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, 2, &boundToPointBufferId);
+ QVERIFY(boundToPointBufferId == GLint(bufferId));
+
+ // Restore to sane state
+ m_func->glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ m_func->glDeleteBuffers(1, &bufferId);
+ }
+
+ void bindFragDataLocation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // WHEN
+ QHash<QString, int> fragLocations;
+ fragLocations.insert(QStringLiteral("temp"), 2);
+ fragLocations.insert(QStringLiteral("color"), 1);
+ m_glHelper.bindFragDataLocation(shaderProgram.programId(), fragLocations);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ const GLint tempLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "temp");
+ const GLint colorLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "color");
+ QCOMPARE(tempLocation, 2);
+ QCOMPARE(colorLocation, 1);
+ }
+
+ void bindFrameBufferAttachment()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA32F);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureAttachmentId = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ &textureAttachmentId);
+ QCOMPARE(GLuint(textureAttachmentId), texture.textureId());
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+
+ // THEN
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBORead);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // Cleanup
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+ // Not supported in OpenGL 3.2
+ }
+
+ void bindUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ GLint index = m_func->glGetUniformBlockIndex(shaderProgram.programId(), "ColorArray");
+ m_glHelper.bindUniformBlock(shaderProgram.programId(), index, 1);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void blendEquation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLint equation = 0;
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_ADD);
+
+ // WHEN
+ m_glHelper.blendEquation(GL_FUNC_REVERSE_SUBTRACT);
+
+ // THEN
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_REVERSE_SUBTRACT);
+ }
+
+ void blendFunci()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+ // Not supported by OpenGL 3.2
+ }
+
+ void blendFuncSeparatei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // Not supported by OpenGL 3.2
+ }
+
+ void boundFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ // THEN
+ GLint boundBuffer = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundBuffer);
+ QCOMPARE(GLuint(boundBuffer), fboId);
+
+ // THEN
+ QCOMPARE(m_glHelper.boundFrameBufferObject(), fboId);
+
+ // Reset state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void checkFrameBufferComplete()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ QVERIFY(m_glHelper.checkFrameBufferComplete());
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void clearBufferf()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ // Create 4 attachments
+ QOpenGLTexture *textures[4];
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // Set Draw buffers
+ GLenum clearBufferEnum = GL_COLOR_ATTACHMENT3;
+ m_func->glDrawBuffers(1, &clearBufferEnum);
+
+ const GLint bufferIndex = 0; // index of the element in the draw buffers
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // WHEN
+ const QVector4D clearValue1 = QVector4D(0.5f, 0.2f, 0.4f, 0.8f);
+ m_func->glClearBufferfv(GL_COLOR, bufferIndex, reinterpret_cast<const float *>(&clearValue1));
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ QVector<QVector4D> colors(512 * 512);
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue1);
+ }
+
+ // WHEN
+ const QVector4D clearValue2 = QVector4D(0.4f, 0.5f, 0.4f, 1.0f);
+ m_glHelper.clearBufferf(bufferIndex, clearValue2);
+
+ // THEN
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue2);
+ }
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void createFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // WHEN
+ const GLuint fboId = m_glHelper.createFrameBufferObject();
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // Restore
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void depthMask()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLboolean depthWritingEnabled = false;
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+
+ // THEN
+ QVERIFY(depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_FALSE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(!depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_TRUE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(depthWritingEnabled);
+ }
+
+ void depthTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_DEPTH_TEST);
+ m_func->glDepthFunc(GL_LESS);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LEQUAL);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ GLint depthMode = 0;
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LEQUAL);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LESS);
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LESS);
+ }
+
+ void disableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_CLIP_DISTANCE0 + 5);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+
+ // WHEN
+ m_glHelper.disableClipPlane(5);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+ }
+
+ void disablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 2));
+
+ // WHEN
+ m_glHelper.disablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 2));
+ }
+
+ void disablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.disablePrimitiveRestart();
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ }
+
+ void drawBuffers()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ QOpenGLTexture *textures[4];
+
+ // Create 4 attachments
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // WHEN
+ GLenum bufferEnum = GL_COLOR_ATTACHMENT4;
+ m_func->glDrawBuffers(1, &bufferEnum);
+
+ // THEN
+ GLint enumValue = -1;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT4);
+
+ // WHEN
+ GLint newBufferEnum = 2;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // WHEN
+ newBufferEnum = 0;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void enableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_CLIP_DISTANCE0 + 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+
+ // WHEN
+ m_glHelper.enableClipPlane(4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+ }
+
+ void enablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 4));
+
+ // WHEN
+ m_glHelper.enablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 4));
+ }
+
+ void enablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.enablePrimitiveRestart(883);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ GLint restartIndex = 0;
+ m_func->glGetIntegerv(GL_PRIMITIVE_RESTART_INDEX, &restartIndex);
+ QCOMPARE(restartIndex, 883);
+
+ // Restore
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+ }
+
+ void enableVertexAttribute()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+ shaderProgram.bind();
+
+ // WHEN
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void frontFace()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glFrontFace(GL_CW);
+
+ // THEN
+ GLint face = 0;
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CW);
+
+ // WHEN
+ m_glHelper.frontFace(GL_CCW);
+
+ // THEN
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CCW);
+ }
+
+ void getRenderBufferDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint renderBufferId = 0;
+ m_func->glGenRenderbuffers(1, &renderBufferId);
+ QVERIFY(renderBufferId != 0);
+
+ // WHEN
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_func->glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8, 512, 512);
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ const QSize dimensions = m_glHelper.getRenderBufferDimensions(renderBufferId);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+
+ // Restore
+ m_func->glDeleteRenderbuffers(1, &renderBufferId);
+ }
+
+ void getTextureDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+
+ // WHEN
+ const QSize dimensions = m_glHelper.getTextureDimensions(texture.textureId(), GL_TEXTURE_2D);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+ }
+
+ void pointSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PROGRAM_POINT_SIZE);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ GLfloat size = 0;
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 1.0f);
+
+ // WHEN
+ m_glHelper.pointSize(false, 0.5f);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 0.5f);
+ }
+
+ void maxClipPlaneCount()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLint maxCount = -1;
+ m_func->glGetIntegerv(GL_MAX_CLIP_PLANES, &maxCount);
+
+ // THEN
+ QCOMPARE(maxCount, m_glHelper.maxClipPlaneCount());
+ }
+
+ void programUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ const QVector<ShaderUniformBlock> activeUniformBlocks = m_glHelper.programUniformBlocks(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniformBlocks.size(), 1);
+ const ShaderUniformBlock uniformBlock = activeUniformBlocks.first();
+
+ QCOMPARE(uniformBlock.m_activeUniformsCount, 1);
+ QCOMPARE(uniformBlock.m_name, QStringLiteral("ColorArray"));
+
+ GLint blockIndex = m_func->glGetUniformBlockIndex(shaderProgram.programId(), "ColorArray");
+ GLint blockBinding = -1;
+ m_func->glGetActiveUniformBlockiv(shaderProgram.programId(), blockIndex, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
+ QCOMPARE(blockIndex, uniformBlock.m_index);
+ QCOMPARE(blockBinding, uniformBlock.m_binding);
+ }
+
+ void programAttributesAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderAttribute> activeAttributes = m_glHelper.programAttributesAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeAttributes.size(), 2);
+ std::sort(activeAttributes.begin(), activeAttributes.end(), [] (const ShaderAttribute &a, const ShaderAttribute &b) { return a.m_name < b.m_name; });
+
+ const ShaderAttribute attribute1 = activeAttributes.at(0);
+ QCOMPARE(attribute1.m_name, QStringLiteral("vertexPosition"));
+ QCOMPARE(attribute1.m_size, 1);
+ QCOMPARE(attribute1.m_location, shaderProgram.attributeLocation("vertexPosition"));
+ QCOMPARE(attribute1.m_type, GLenum(GL_FLOAT_VEC3));
+
+ const ShaderAttribute attribute2 = activeAttributes.at(1);
+ QCOMPARE(attribute2.m_name, QStringLiteral("vertexTexCoord"));
+ QCOMPARE(attribute2.m_size, 1);
+ QCOMPARE(attribute2.m_location, shaderProgram.attributeLocation("vertexTexCoord"));
+ QCOMPARE(attribute2.m_type, GLenum(GL_FLOAT_VEC2));
+ }
+
+ void programUniformsAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniforms.size(), 4);
+ std::sort(activeUniforms.begin(), activeUniforms.end(), [] (const ShaderUniform &a, const ShaderUniform &b) { return a.m_name < b.m_name; });
+
+ const ShaderUniform uniform1 = activeUniforms.at(0);
+ QCOMPARE(uniform1.m_location, shaderProgram.uniformLocation("multiplier"));
+ QCOMPARE(uniform1.m_offset, -1);
+ QCOMPARE(uniform1.m_blockIndex, -1);
+ QCOMPARE(uniform1.m_arrayStride, -1);
+ QCOMPARE(uniform1.m_matrixStride, -1);
+ QCOMPARE(uniform1.m_size, 1);
+ QCOMPARE(uniform1.m_type, GLenum(GL_FLOAT));
+ QCOMPARE(uniform1.m_name, QStringLiteral("multiplier"));
+
+ const ShaderUniform uniform2 = activeUniforms.at(1);
+ QCOMPARE(uniform2.m_location, shaderProgram.uniformLocation("multiplierVec2"));
+ QCOMPARE(uniform2.m_offset, -1);
+ QCOMPARE(uniform2.m_blockIndex, -1);
+ QCOMPARE(uniform2.m_arrayStride, -1);
+ QCOMPARE(uniform2.m_matrixStride, -1);
+ QCOMPARE(uniform2.m_size, 1);
+ QCOMPARE(uniform2.m_type, GLenum(GL_FLOAT_VEC2));
+ QCOMPARE(uniform2.m_name, QStringLiteral("multiplierVec2"));
+
+ const ShaderUniform uniform3 = activeUniforms.at(2);
+ QCOMPARE(uniform3.m_location, shaderProgram.uniformLocation("multiplierVec3"));
+ QCOMPARE(uniform3.m_offset, -1);
+ QCOMPARE(uniform3.m_blockIndex, -1);
+ QCOMPARE(uniform3.m_arrayStride, -1);
+ QCOMPARE(uniform3.m_matrixStride, -1);
+ QCOMPARE(uniform3.m_size, 1);
+ QCOMPARE(uniform3.m_type, GLenum(GL_FLOAT_VEC3));
+ QCOMPARE(uniform3.m_name, QStringLiteral("multiplierVec3"));
+
+ const ShaderUniform uniform4 = activeUniforms.at(3);
+ QCOMPARE(uniform4.m_location, shaderProgram.uniformLocation("multiplierVec4"));
+ QCOMPARE(uniform4.m_offset, -1);
+ QCOMPARE(uniform4.m_blockIndex, -1);
+ QCOMPARE(uniform4.m_arrayStride, -1);
+ QCOMPARE(uniform4.m_matrixStride, -1);
+ QCOMPARE(uniform4.m_size, 1);
+ QCOMPARE(uniform4.m_type, GLenum(GL_FLOAT_VEC4));
+ QCOMPARE(uniform4.m_name, QStringLiteral("multiplierVec4"));
+ }
+
+ void programShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // Not supported in 3.2
+ }
+
+ void releaseFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.releaseFrameBufferObject(fboId);
+
+ // THEN
+ QVERIFY(!m_func->glIsFramebuffer(fboId));
+ }
+
+ void setMSAAEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+ }
+
+ void setAlphaCoverageEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+ }
+
+ void setClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // Deprecated in 3.3 core
+ }
+
+ void setSeamlessCubemap()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+ void setVerticesPerPatch()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+#define SUPPORTS_FEATURE(Feature, IsSupported) \
+ QVERIFY(m_glHelper.supportsFeature(Feature) == IsSupported);
+
+ void supportsFeature()
+ {
+ SUPPORTS_FEATURE(GraphicsHelperInterface::MRT, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BindableFragmentOutputs, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::PrimitiveRestart, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::RenderBufferDimensionRetrieval, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::TextureDimensionRetrieval, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::ShaderStorageObject, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Compute, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::DrawBuffersBlend, false);
+ // Tesselation could be true or false depending on extensions so not tested
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BlitFramebuffer, true);
+ }
+
+
+#define ADD_UNIFORM_ENTRY(FragShader, Name, Type, ComponentSize, ExpectedRawSize) \
+ QTest::newRow(#FragShader"_"#Type) << FragShader << QStringLiteral(Name) << Type << ComponentSize << ExpectedRawSize;
+
+ void uniformsByteSize_data()
+ {
+ QTest::addColumn<QByteArray>("fragShader");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<int>("componentSize");
+ QTest::addColumn<int>("expectedByteSize");
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplier", GL_FLOAT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec2", GL_FLOAT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec3",GL_FLOAT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec4", GL_FLOAT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplier", GL_INT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec2", GL_INT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec3", GL_INT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec4", GL_INT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplier", GL_UNSIGNED_INT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec2", GL_UNSIGNED_INT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec3", GL_UNSIGNED_INT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec4", GL_UNSIGNED_INT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m2", GL_FLOAT_MAT2, 1, 4 * 2 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m23", GL_FLOAT_MAT2x3, 1, 4 * 2 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m32", GL_FLOAT_MAT3x2, 1, 4 * 3 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m24", GL_FLOAT_MAT2x4, 1, 4 * 2 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m42", GL_FLOAT_MAT4x2, 1, 4 * 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m3", GL_FLOAT_MAT3, 1, 4 * 3 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m34", GL_FLOAT_MAT3x4, 1, 4 * 3 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m43", GL_FLOAT_MAT4x3, 1, 4 * 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m4", GL_FLOAT_MAT4, 1, 4 * 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s1", GL_SAMPLER_1D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s2", GL_SAMPLER_2D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s2a", GL_SAMPLER_2D_ARRAY, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s3", GL_SAMPLER_3D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "scube", GL_SAMPLER_CUBE, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "srect", GL_SAMPLER_2D_RECT, 1, 4);
+ }
+
+ void uniformsByteSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QFETCH(QByteArray, fragShader);
+ QFETCH(QString, name);
+ QFETCH(int, type);
+ QFETCH(int, componentSize);
+ QFETCH(int, expectedByteSize);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShader);
+ QVERIFY(shaderProgram.link());
+
+ GLint location = shaderProgram.uniformLocation(name);
+ // WHEN
+ const QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+ ShaderUniform matchingUniform;
+ for (const ShaderUniform &u : activeUniforms) {
+ if (u.m_location == location) {
+ matchingUniform = u;
+ break;
+ }
+ }
+
+
+ // THEN
+ QCOMPARE(matchingUniform.m_location, location);
+ QCOMPARE(matchingUniform.m_type, GLuint(type));
+ QCOMPARE(matchingUniform.m_size, componentSize);
+
+ // WHEN
+ const int computedRawByteSize = m_glHelper.uniformByteSize(matchingUniform);
+
+ // THEN
+ QCOMPARE(expectedByteSize, computedRawByteSize);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void useProgram()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+
+ GLint currentProg = 0;
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+
+ // WHEN
+ m_glHelper.useProgram(shaderProgram.programId());
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QCOMPARE(GLuint(currentProg), shaderProgram.programId());
+
+ // WHEN
+ m_glHelper.useProgram(0);
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+ }
+
+ void vertexAttribDivisor()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+ // Not available in 3.2
+ }
+
+ void vertexAttributePointer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+
+ const int vertexCount = 99;
+ QOpenGLBuffer positionBuffer(QOpenGLBuffer::VertexBuffer);
+ positionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ positionBuffer.create();
+ positionBuffer.bind();
+ positionBuffer.allocate(vertexCount * sizeof(QVector3D));
+
+ QOpenGLBuffer texCoordBuffer(QOpenGLBuffer::VertexBuffer);
+ texCoordBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ texCoordBuffer.create();
+ texCoordBuffer.allocate(vertexCount * sizeof(QVector2D));
+
+ QOpenGLBuffer colorIndexBuffer(QOpenGLBuffer::VertexBuffer);
+ colorIndexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ colorIndexBuffer.create();
+ colorIndexBuffer.allocate(vertexCount * sizeof(int));
+
+ // WHEN
+ shaderProgram.bind();
+ positionBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC3, positionLocation, 3, GL_FLOAT, GL_TRUE, 0, 0);
+
+ texCoordBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC2, texCoordLocation, 2, GL_FLOAT, GL_TRUE, 0, 0);
+
+ colorIndexBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+ m_glHelper.vertexAttributePointer(GL_INT, colorIndexLocation, 1, GL_INT, GL_TRUE, 0, 0);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void glUniform1fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat value = 883.0f;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1fv(location, 1, &value);
+
+ // THEN
+ GLfloat setValue = 0.0f;
+ m_func->glGetUniformfv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[2] = { 383.0f, 427.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[2] = { 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[3] = { 572.0f, 1340.0f, 1584.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[3] = { 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint value = 883;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1iv(location, 1, &value);
+
+ // THEN
+ GLint setValue = 0;
+ m_func->glGetUniformiv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[2] = { 383, 427 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2iv(location, 1, values);
+
+ // THEN
+ GLint setValues[2] = { 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[3] = { 572, 1340, 1584 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3iv(location, 1, values);
+
+ // THEN
+ GLint setValues[3] = { 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[4] = { 454, 350, 883, 355 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4iv(location, 1, values);
+
+ // THEN
+ GLint setValues[4] = { 0, 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint value = 883U;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1uiv(location, 1, &value);
+
+ // THEN
+ GLuint setValue = 0U;
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[2] = { 383U, 427U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[2] = { 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[3] = { 572U, 1340U, 1584U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[3] = { 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[4] = { 454U, 350U, 883U, 355U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[4] = { 0U, 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("m2");
+ m_glHelper.glUniformMatrix2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[9] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f };
+ const GLint location = shaderProgram.uniformLocation("m3");
+ m_glHelper.glUniformMatrix3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 9; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[16] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f, 1603.0f, 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 327.0f };
+ const GLint location = shaderProgram.uniformLocation("m4");
+ m_glHelper.glUniformMatrix4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 16; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m23");
+ m_glHelper.glUniformMatrix2x3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m32");
+ m_glHelper.glUniformMatrix3x2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m24");
+ m_glHelper.glUniformMatrix2x4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m42");
+ m_glHelper.glUniformMatrix4x2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f};
+ const GLint location = shaderProgram.uniformLocation("m34");
+ m_glHelper.glUniformMatrix3x4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m43");
+ m_glHelper.glUniformMatrix4x3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void blitFramebuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ GLint maxSamples;
+ m_func->glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ if (maxSamples < 1)
+ QSKIP("This test requires an implementation that supports multisampled textures");
+
+ // GIVEN
+ GLuint fbos[2];
+ GLuint fboTextures[2];
+
+ m_func->glGenFramebuffers(2, fbos);
+ m_func->glGenTextures(2, fboTextures);
+
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, fboTextures[0]);
+ m_func->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, maxSamples, GL_RGBA8, 10, 10, true);
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+
+ m_func->glBindTexture(GL_TEXTURE_2D, fboTextures[1]);
+ m_func->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ m_func->glBindTexture(GL_TEXTURE_2D, 0);
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[1], 0);
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[0], 0);
+
+ status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ m_func->glEnable(GL_MULTISAMPLE);
+ m_func->glClearColor(0.2f, 0.2f, 0.2f, 0.2f);
+ m_func->glClear(GL_COLOR_BUFFER_BIT);
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+
+ // WHEN
+ m_glHelper.blitFramebuffer(0,0,10,10,0,0,10,10, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[1]);
+
+ GLuint result[10*10];
+ m_func->glReadPixels(0,0,10,10,GL_RGBA, GL_UNSIGNED_BYTE, result);
+
+ // THEN
+ GLuint v = (0.2f) * 255;
+ v = v | (v<<8) | (v<<16) | (v<<24);
+ for (GLuint value : result) {
+ QCOMPARE(value, v);
+ }
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+
+ m_func->glDeleteFramebuffers(2, fbos);
+ m_func->glDeleteTextures(2, fboTextures);
+ }
+
+#define ADD_GL_TYPE_ENTRY(Type, Expected) \
+ QTest::newRow(#Type) << Type << Expected;
+
+ void uniformTypeFromGLType_data()
+ {
+ QTest::addColumn<int>("glType");
+ QTest::addColumn<UniformType>("expected");
+
+ ADD_GL_TYPE_ENTRY(GL_FLOAT, UniformType::Float);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_INT, UniformType::Int);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC2, UniformType::IVec2);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC3, UniformType::IVec3);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC4, UniformType::IVec4);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT, UniformType::UInt);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC2, UniformType::UIVec2);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC3, UniformType::UIVec3);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC4, UniformType::UIVec4);
+ ADD_GL_TYPE_ENTRY(GL_BOOL, UniformType::Bool);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC2, UniformType::BVec2);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC3, UniformType::BVec3);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC4, UniformType::BVec4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2, UniformType::Mat2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3, UniformType::Mat3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4, UniformType::Mat4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x3, UniformType::Mat2x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x4, UniformType::Mat2x4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x2, UniformType::Mat3x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x2, UniformType::Mat4x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x3, UniformType::Mat4x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x4, UniformType::Mat3x4);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_RECT, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_CUBE, UniformType::Sampler);
+ }
+
+ void uniformTypeFromGLType()
+ {
+ // GIVEN
+ QFETCH(int, glType);
+ QFETCH(UniformType, expected);
+
+ // WHEN
+ UniformType computed = m_glHelper.uniformTypeFromGLType(glType);
+
+ // THEN
+ QCOMPARE(computed, expected);
+ }
+
+ void drawBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.drawBuffer(GL_FRONT);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ GLint p;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void readBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.readBuffer(GL_FRONT);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_READ_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
+ void rasterMode()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.2 functions not supported");
+
+ m_func->glGetError();
+
+ m_glHelper.rasterMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_POLYGON_MODE, &p);
+ QCOMPARE(p, GL_LINE);
+ }
+
+private:
+ QScopedPointer<QWindow> m_window;
+ QOpenGLContext m_glContext;
+ GraphicsHelperGL3_2 m_glHelper;
+ QOpenGLFunctions_3_2_Core *m_func = nullptr;
+ bool m_initializationSuccessful = false;
+};
+
+#endif
+
+int main(int argc, char *argv[])
+{
+#ifdef TEST_SHOULD_BE_PERFORMED
+ QGuiApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_GraphicsHelperGL3_2 tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+#endif
+ return 0;
+}
+
+#ifdef TEST_SHOULD_BE_PERFORMED
+#include "tst_graphicshelpergl3_2.moc"
+#endif
diff --git a/tests/auto/render/opengl/graphicshelpergl3_3/graphicshelpergl3_3.pro b/tests/auto/render/opengl/graphicshelpergl3_3/graphicshelpergl3_3.pro
new file mode 100644
index 000000000..5ef0b6806
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl3_3/graphicshelpergl3_3.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+
+TARGET = tst_graphicshelpergl3_3
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += \
+ tst_graphicshelpergl3_3.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp b/tests/auto/render/opengl/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp
new file mode 100644
index 000000000..b463e6236
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl3_3/tst_graphicshelpergl3_3.cpp
@@ -0,0 +1,2394 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DRender/qrendertargetoutput.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <graphicshelpergl3_3_p.h>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions_3_3_Core>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+
+#if !defined(QT_OPENGL_ES_2) && defined(QT_OPENGL_3_2)
+
+#define TEST_SHOULD_BE_PERFORMED 1
+
+using namespace Qt3DRender;
+using namespace Qt3DRender::Render;
+using namespace Qt3DRender::Render::OpenGL;
+
+namespace {
+
+const QByteArray vertCode = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "in vec3 vertexPosition;\n" \
+ "in vec2 vertexTexCoord;\n" \
+ "out vec2 texCoord;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray vertCodeUniformBuffer = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "in vec3 vertexPosition;\n" \
+ "in vec2 vertexTexCoord;\n" \
+ "in int vertexColorIndex;\n" \
+ "out vec2 texCoord;\n" \
+ "flat out int colorIndex;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " colorIndex = vertexColorIndex;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeFragOutputs = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out vec4 color;\n" \
+ "out vec2 temp;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0);\n" \
+ " temp = vec2(1.0, 0.3);\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloat = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out vec4 color;\n" \
+ "uniform float multiplier;\n" \
+ "uniform vec2 multiplierVec2;\n" \
+ "uniform vec3 multiplierVec3;\n" \
+ "uniform vec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " vec4 randomMult = multiplierVec4 + vec4(multiplierVec3, 0.0) + vec4(multiplierVec2, 0.0, 0.0);\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsInt = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out ivec4 color;\n" \
+ "uniform int multiplier;\n" \
+ "uniform ivec2 multiplierVec2;\n" \
+ "uniform ivec3 multiplierVec3;\n" \
+ "uniform ivec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " ivec4 randomMult = multiplierVec4 + ivec4(multiplierVec3, 0) + ivec4(multiplierVec2, 0, 0);\n" \
+ " color = ivec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsUInt = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out uvec4 color;\n" \
+ "uniform uint multiplier;\n" \
+ "uniform uvec2 multiplierVec2;\n" \
+ "uniform uvec3 multiplierVec3;\n" \
+ "uniform uvec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " uvec4 randomMult = multiplierVec4 + uvec4(multiplierVec3, 0) + uvec4(multiplierVec2, 0, 0);\n" \
+ " color = uvec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloatMatrices = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out vec4 color;\n" \
+ "uniform mat2 m2;\n" \
+ "uniform mat2x3 m23;\n" \
+ "uniform mat3x2 m32;\n" \
+ "uniform mat2x4 m24;\n" \
+ "uniform mat4x2 m42;\n" \
+ "uniform mat3 m3;\n" \
+ "uniform mat3x4 m34;\n" \
+ "uniform mat4x3 m43;\n" \
+ "uniform mat4 m4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " float lengthSum = m2[0][0] + m23[0][0] + m32[0][0] + m24[0][0] + m42[0][0] + m3[0][0] + m34[0][0] + m43[0][0] + m4[0][0];\n" \
+ " color = vec4(1, 0, 0, 1) * lengthSum;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformBuffer = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "out vec4 color;\n" \
+ "in vec2 texCoord;\n" \
+ "flat in int colorIndex;\n" \
+ "uniform ColorArray\n" \
+ "{\n" \
+ " vec4 colors[256];\n" \
+ "};\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = colors[colorIndex] + vec4(texCoord.s, texCoord.t, 0.0, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeSamplers = QByteArrayLiteral(
+ "#version 330 core\n" \
+ "in vec2 texCoord;\n" \
+ "out vec4 color;\n" \
+ "uniform sampler1D s1;\n" \
+ "uniform sampler2D s2;\n" \
+ "uniform sampler2DArray s2a;\n" \
+ "uniform sampler3D s3;\n" \
+ "uniform samplerCube scube;\n" \
+ "uniform sampler2DRect srect;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1, 0, 0, 1) *" \
+ " texture(s1, texCoord.x) *" \
+ " texture(s2, texCoord) *" \
+ " texture(s2a, vec3(texCoord, 0.0)) *" \
+ " texture(s3, vec3(texCoord, 0.0)) *" \
+ " texture(scube, vec3(texCoord, 0)) *" \
+ " texture(srect, texCoord);\n" \
+ "}\n");
+
+} // anonymous
+
+class tst_GraphicsHelperGL3_3 : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+
+ void init()
+ {
+ m_window.reset(new QWindow);
+ m_window->setSurfaceType(QWindow::OpenGLSurface);
+ m_window->setGeometry(0, 0, 10, 10);
+
+ QSurfaceFormat format;
+ format.setVersion(3, 3);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ m_window->setFormat(format);
+ m_glContext.setFormat(format);
+
+ m_window->create();
+
+ if (!m_glContext.create()) {
+ qWarning() << "Failed to create OpenGL context";
+ return;
+ }
+
+ if (!m_glContext.makeCurrent(m_window.data())) {
+ qWarning() << "Failed to maed OpenGL context current";
+ return;
+ }
+
+ if ((m_func = m_glContext.versionFunctions<QOpenGLFunctions_3_3_Core>()) != nullptr) {
+ m_glHelper.initializeHelper(&m_glContext, m_func);
+ m_initializationSuccessful = true;
+ }
+ }
+
+ void cleanup()
+ {
+ m_glContext.doneCurrent();
+ }
+
+ void alphaTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+ // Deprecated
+ }
+
+ void bindBufferBase()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint bufferId = 0;
+ // WHEN
+ m_func->glGenBuffers(1, &bufferId);
+ // THEN
+ QVERIFY(bufferId != 0);
+
+
+ // WHEN
+ m_func->glBindBuffer(GL_UNIFORM_BUFFER, bufferId);
+ m_glHelper.bindBufferBase(GL_UNIFORM_BUFFER, 2, bufferId);
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundToPointBufferId = 0;
+ m_func->glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, 2, &boundToPointBufferId);
+ QVERIFY(boundToPointBufferId == GLint(bufferId));
+
+ // Restore to sane state
+ m_func->glBindBuffer(GL_UNIFORM_BUFFER, 0);
+ m_func->glDeleteBuffers(1, &bufferId);
+ }
+
+ void bindFragDataLocation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // WHEN
+ QHash<QString, int> fragLocations;
+ fragLocations.insert(QStringLiteral("temp"), 2);
+ fragLocations.insert(QStringLiteral("color"), 1);
+ m_glHelper.bindFragDataLocation(shaderProgram.programId(), fragLocations);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ const GLint tempLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "temp");
+ const GLint colorLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "color");
+ QCOMPARE(tempLocation, 2);
+ QCOMPARE(colorLocation, 1);
+ }
+
+ void bindFrameBufferAttachment()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ {
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ GLint maxAttachmentsCount = 0;
+ m_func->glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxAttachmentsCount);
+
+ // THEN
+ QVERIFY(fboId != 0);
+ QVERIFY(maxAttachmentsCount >= 3);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QCOMPARE(int(status), GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureAttachmentId = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ &textureAttachmentId);
+ QCOMPARE(GLuint(textureAttachmentId), texture.textureId());
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ {
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::TargetCubeMap);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA32F);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ { // Check All Faces
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+ attachment.m_face = Qt3DRender::QAbstractTexture::AllFaces;
+
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureIsLayered = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_LAYERED,
+ &textureIsLayered);
+ QCOMPARE(textureIsLayered, GL_TRUE);
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ { // Check Specific Faces
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+ attachment.m_face = Qt3DRender::QAbstractTexture::CubeMapNegativeZ;
+
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureFace = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
+ &textureFace);
+ QCOMPARE(textureFace, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+ GLint textureIsLayered = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_LAYERED,
+ &textureIsLayered);
+ QCOMPARE(textureIsLayered, GL_FALSE);
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ }
+ }
+
+ void bindFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+
+ // THEN
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBORead);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // Cleanup
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+ // Not supported in OpenGL 3.3
+ }
+
+ void bindUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ GLint index = m_func->glGetUniformBlockIndex(shaderProgram.programId(), "ColorArray");
+ m_glHelper.bindUniformBlock(shaderProgram.programId(), index, 1);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void blendEquation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLint equation = 0;
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_ADD);
+
+ // WHEN
+ m_glHelper.blendEquation(GL_FUNC_REVERSE_SUBTRACT);
+
+ // THEN
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_REVERSE_SUBTRACT);
+ }
+
+ void blendFunci()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+ // Not supported by OpenGL 3.3
+ }
+
+ void blendFuncSeparatei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // Not supported by OpenGL 3.3
+ }
+
+ void boundFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ // THEN
+ GLint boundBuffer = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundBuffer);
+ QCOMPARE(GLuint(boundBuffer), fboId);
+
+ // THEN
+ QCOMPARE(m_glHelper.boundFrameBufferObject(), fboId);
+
+ // Reset state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void checkFrameBufferComplete()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ QVERIFY(m_glHelper.checkFrameBufferComplete());
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void clearBufferf()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ // Create 4 attachments
+ QOpenGLTexture *textures[4];
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // Set Draw buffers
+ GLenum clearBufferEnum = GL_COLOR_ATTACHMENT3;
+ m_func->glDrawBuffers(1, &clearBufferEnum);
+
+ const GLint bufferIndex = 0; // index of the element in the draw buffers
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // WHEN
+ const QVector4D clearValue1 = QVector4D(0.5f, 0.2f, 0.4f, 0.8f);
+ m_func->glClearBufferfv(GL_COLOR, bufferIndex, reinterpret_cast<const float *>(&clearValue1));
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ QVector<QVector4D> colors(512 * 512);
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue1);
+ }
+
+ // WHEN
+ const QVector4D clearValue2 = QVector4D(0.4f, 0.5f, 0.4f, 1.0f);
+ m_glHelper.clearBufferf(bufferIndex, clearValue2);
+
+ // THEN
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue2);
+ }
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void createFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // WHEN
+ const GLuint fboId = m_glHelper.createFrameBufferObject();
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // Restore
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void depthMask()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLboolean depthWritingEnabled = false;
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+
+ // THEN
+ QVERIFY(depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_FALSE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(!depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_TRUE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(depthWritingEnabled);
+ }
+
+ void depthTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_DEPTH_TEST);
+ m_func->glDepthFunc(GL_LESS);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LEQUAL);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ GLint depthMode = 0;
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LEQUAL);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LESS);
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LESS);
+ }
+
+ void disableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_CLIP_DISTANCE0 + 5);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+
+ // WHEN
+ m_glHelper.disableClipPlane(5);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+ }
+
+ void disablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 2));
+
+ // WHEN
+ m_glHelper.disablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 2));
+ }
+
+ void disablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.disablePrimitiveRestart();
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ }
+
+ void drawBuffers()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ QOpenGLTexture *textures[4];
+
+ // Create 4 attachments
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // WHEN
+ GLenum bufferEnum = GL_COLOR_ATTACHMENT4;
+ m_func->glDrawBuffers(1, &bufferEnum);
+
+ // THEN
+ GLint enumValue = -1;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT4);
+
+ // WHEN
+ GLint newBufferEnum = 2;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // WHEN
+ newBufferEnum = 0;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void enableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_CLIP_DISTANCE0 + 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+
+ // WHEN
+ m_glHelper.enableClipPlane(4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+ }
+
+ void enablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 4));
+
+ // WHEN
+ m_glHelper.enablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 4));
+ }
+
+ void enablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.enablePrimitiveRestart(883);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ GLint restartIndex = 0;
+ m_func->glGetIntegerv(GL_PRIMITIVE_RESTART_INDEX, &restartIndex);
+ QCOMPARE(restartIndex, 883);
+
+ // Restore
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+ }
+
+ void enableVertexAttribute()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+ shaderProgram.bind();
+
+ // WHEN
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void frontFace()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glFrontFace(GL_CW);
+
+ // THEN
+ GLint face = 0;
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CW);
+
+ // WHEN
+ m_glHelper.frontFace(GL_CCW);
+
+ // THEN
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CCW);
+ }
+
+ void getRenderBufferDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint renderBufferId = 0;
+ m_func->glGenRenderbuffers(1, &renderBufferId);
+ QVERIFY(renderBufferId != 0);
+
+ // WHEN
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_func->glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8, 512, 512);
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ const QSize dimensions = m_glHelper.getRenderBufferDimensions(renderBufferId);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+
+ // Restore
+ m_func->glDeleteRenderbuffers(1, &renderBufferId);
+ }
+
+ void getTextureDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+
+ // WHEN
+ const QSize dimensions = m_glHelper.getTextureDimensions(texture.textureId(), GL_TEXTURE_2D);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+ }
+
+ void pointSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PROGRAM_POINT_SIZE);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ GLfloat size = 0;
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 1.0f);
+
+ // WHEN
+ m_glHelper.pointSize(false, 0.5f);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 0.5f);
+ }
+
+ void maxClipPlaneCount()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLint maxCount = -1;
+ m_func->glGetIntegerv(GL_MAX_CLIP_PLANES, &maxCount);
+
+ // THEN
+ QCOMPARE(maxCount, m_glHelper.maxClipPlaneCount());
+ }
+
+ void programUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ const QVector<ShaderUniformBlock> activeUniformBlocks = m_glHelper.programUniformBlocks(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniformBlocks.size(), 1);
+ const ShaderUniformBlock uniformBlock = activeUniformBlocks.first();
+
+ QCOMPARE(uniformBlock.m_activeUniformsCount, 1);
+ QCOMPARE(uniformBlock.m_name, QStringLiteral("ColorArray"));
+
+ GLint blockIndex = m_func->glGetUniformBlockIndex(shaderProgram.programId(), "ColorArray");
+ GLint blockBinding = -1;
+ m_func->glGetActiveUniformBlockiv(shaderProgram.programId(), blockIndex, GL_UNIFORM_BLOCK_BINDING, &blockBinding);
+ QCOMPARE(blockIndex, uniformBlock.m_index);
+ QCOMPARE(blockBinding, uniformBlock.m_binding);
+ }
+
+ void programAttributesAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderAttribute> activeAttributes = m_glHelper.programAttributesAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeAttributes.size(), 2);
+ std::sort(activeAttributes.begin(), activeAttributes.end(), [] (const ShaderAttribute &a, const ShaderAttribute &b) { return a.m_name < b.m_name; });
+
+ const ShaderAttribute attribute1 = activeAttributes.at(0);
+ QCOMPARE(attribute1.m_name, QStringLiteral("vertexPosition"));
+ QCOMPARE(attribute1.m_size, 1);
+ QCOMPARE(attribute1.m_location, shaderProgram.attributeLocation("vertexPosition"));
+ QCOMPARE(attribute1.m_type, GLenum(GL_FLOAT_VEC3));
+
+ const ShaderAttribute attribute2 = activeAttributes.at(1);
+ QCOMPARE(attribute2.m_name, QStringLiteral("vertexTexCoord"));
+ QCOMPARE(attribute2.m_size, 1);
+ QCOMPARE(attribute2.m_location, shaderProgram.attributeLocation("vertexTexCoord"));
+ QCOMPARE(attribute2.m_type, GLenum(GL_FLOAT_VEC2));
+ }
+
+ void programUniformsAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniforms.size(), 4);
+ std::sort(activeUniforms.begin(), activeUniforms.end(), [] (const ShaderUniform &a, const ShaderUniform &b) { return a.m_name < b.m_name; });
+
+ const ShaderUniform uniform1 = activeUniforms.at(0);
+ QCOMPARE(uniform1.m_location, shaderProgram.uniformLocation("multiplier"));
+ QCOMPARE(uniform1.m_offset, -1);
+ QCOMPARE(uniform1.m_blockIndex, -1);
+ QCOMPARE(uniform1.m_arrayStride, -1);
+ QCOMPARE(uniform1.m_matrixStride, -1);
+ QCOMPARE(uniform1.m_size, 1);
+ QCOMPARE(uniform1.m_type, GLenum(GL_FLOAT));
+ QCOMPARE(uniform1.m_name, QStringLiteral("multiplier"));
+
+ const ShaderUniform uniform2 = activeUniforms.at(1);
+ QCOMPARE(uniform2.m_location, shaderProgram.uniformLocation("multiplierVec2"));
+ QCOMPARE(uniform2.m_offset, -1);
+ QCOMPARE(uniform2.m_blockIndex, -1);
+ QCOMPARE(uniform2.m_arrayStride, -1);
+ QCOMPARE(uniform2.m_matrixStride, -1);
+ QCOMPARE(uniform2.m_size, 1);
+ QCOMPARE(uniform2.m_type, GLenum(GL_FLOAT_VEC2));
+ QCOMPARE(uniform2.m_name, QStringLiteral("multiplierVec2"));
+
+ const ShaderUniform uniform3 = activeUniforms.at(2);
+ QCOMPARE(uniform3.m_location, shaderProgram.uniformLocation("multiplierVec3"));
+ QCOMPARE(uniform3.m_offset, -1);
+ QCOMPARE(uniform3.m_blockIndex, -1);
+ QCOMPARE(uniform3.m_arrayStride, -1);
+ QCOMPARE(uniform3.m_matrixStride, -1);
+ QCOMPARE(uniform3.m_size, 1);
+ QCOMPARE(uniform3.m_type, GLenum(GL_FLOAT_VEC3));
+ QCOMPARE(uniform3.m_name, QStringLiteral("multiplierVec3"));
+
+ const ShaderUniform uniform4 = activeUniforms.at(3);
+ QCOMPARE(uniform4.m_location, shaderProgram.uniformLocation("multiplierVec4"));
+ QCOMPARE(uniform4.m_offset, -1);
+ QCOMPARE(uniform4.m_blockIndex, -1);
+ QCOMPARE(uniform4.m_arrayStride, -1);
+ QCOMPARE(uniform4.m_matrixStride, -1);
+ QCOMPARE(uniform4.m_size, 1);
+ QCOMPARE(uniform4.m_type, GLenum(GL_FLOAT_VEC4));
+ QCOMPARE(uniform4.m_name, QStringLiteral("multiplierVec4"));
+ }
+
+ void programShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // Not supported in 3.3
+ }
+
+ void releaseFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.releaseFrameBufferObject(fboId);
+
+ // THEN
+ QVERIFY(!m_func->glIsFramebuffer(fboId));
+ }
+
+ void setMSAAEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+ }
+
+ void setAlphaCoverageEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+ }
+
+ void setClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // Deprecated in 3.3 core
+ }
+
+ void setSeamlessCubemap()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+ void setVerticesPerPatch()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+#define SUPPORTS_FEATURE(Feature, IsSupported) \
+ QVERIFY(m_glHelper.supportsFeature(Feature) == IsSupported);
+
+ void supportsFeature()
+ {
+ SUPPORTS_FEATURE(GraphicsHelperInterface::MRT, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BindableFragmentOutputs, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::PrimitiveRestart, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::RenderBufferDimensionRetrieval, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::TextureDimensionRetrieval, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::UniformBufferObject, true);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::ShaderStorageObject, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::Compute, false);
+ SUPPORTS_FEATURE(GraphicsHelperInterface::DrawBuffersBlend, false);
+ // Tesselation could be true or false depending on extensions so not tested
+ SUPPORTS_FEATURE(GraphicsHelperInterface::BlitFramebuffer, true);
+ }
+
+
+#define ADD_UNIFORM_ENTRY(FragShader, Name, Type, ComponentSize, ExpectedRawSize) \
+ QTest::newRow(#FragShader"_"#Type) << FragShader << QStringLiteral(Name) << Type << ComponentSize << ExpectedRawSize;
+
+ void uniformsByteSize_data()
+ {
+ QTest::addColumn<QByteArray>("fragShader");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<int>("componentSize");
+ QTest::addColumn<int>("expectedByteSize");
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplier", GL_FLOAT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec2", GL_FLOAT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec3",GL_FLOAT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, "multiplierVec4", GL_FLOAT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplier", GL_INT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec2", GL_INT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec3", GL_INT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, "multiplierVec4", GL_INT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplier", GL_UNSIGNED_INT, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec2", GL_UNSIGNED_INT_VEC2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec3", GL_UNSIGNED_INT_VEC3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, "multiplierVec4", GL_UNSIGNED_INT_VEC4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m2", GL_FLOAT_MAT2, 1, 4 * 2 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m23", GL_FLOAT_MAT2x3, 1, 4 * 2 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m32", GL_FLOAT_MAT3x2, 1, 4 * 3 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m24", GL_FLOAT_MAT2x4, 1, 4 * 2 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m42", GL_FLOAT_MAT4x2, 1, 4 * 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m3", GL_FLOAT_MAT3, 1, 4 * 3 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m34", GL_FLOAT_MAT3x4, 1, 4 * 3 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m43", GL_FLOAT_MAT4x3, 1, 4 * 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, "m4", GL_FLOAT_MAT4, 1, 4 * 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s1", GL_SAMPLER_1D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s2", GL_SAMPLER_2D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s2a", GL_SAMPLER_2D_ARRAY, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "s3", GL_SAMPLER_3D, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "scube", GL_SAMPLER_CUBE, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, "srect", GL_SAMPLER_2D_RECT, 1, 4);
+ }
+
+ void uniformsByteSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QFETCH(QByteArray, fragShader);
+ QFETCH(QString, name);
+ QFETCH(int, type);
+ QFETCH(int, componentSize);
+ QFETCH(int, expectedByteSize);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShader);
+ QVERIFY(shaderProgram.link());
+
+ GLint location = shaderProgram.uniformLocation(name);
+ // WHEN
+ const QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+ ShaderUniform matchingUniform;
+ for (const ShaderUniform &u : activeUniforms) {
+ if (u.m_location == location) {
+ matchingUniform = u;
+ break;
+ }
+ }
+
+
+ // THEN
+ QCOMPARE(matchingUniform.m_location, location);
+ QCOMPARE(matchingUniform.m_type, GLuint(type));
+ QCOMPARE(matchingUniform.m_size, componentSize);
+
+ // WHEN
+ const int computedRawByteSize = m_glHelper.uniformByteSize(matchingUniform);
+
+ // THEN
+ QCOMPARE(expectedByteSize, computedRawByteSize);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+
+ void useProgram()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+
+ GLint currentProg = 0;
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+
+ // WHEN
+ m_glHelper.useProgram(shaderProgram.programId());
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QCOMPARE(GLuint(currentProg), shaderProgram.programId());
+
+ // WHEN
+ m_glHelper.useProgram(0);
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+ }
+
+ void vertexAttribDivisor()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+ }
+
+ void vertexAttributePointer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+
+ const int vertexCount = 99;
+ QOpenGLBuffer positionBuffer(QOpenGLBuffer::VertexBuffer);
+ positionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ positionBuffer.create();
+ positionBuffer.bind();
+ positionBuffer.allocate(vertexCount * sizeof(QVector3D));
+
+ QOpenGLBuffer texCoordBuffer(QOpenGLBuffer::VertexBuffer);
+ texCoordBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ texCoordBuffer.create();
+ texCoordBuffer.allocate(vertexCount * sizeof(QVector2D));
+
+ QOpenGLBuffer colorIndexBuffer(QOpenGLBuffer::VertexBuffer);
+ colorIndexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ colorIndexBuffer.create();
+ colorIndexBuffer.allocate(vertexCount * sizeof(int));
+
+ // WHEN
+ shaderProgram.bind();
+ positionBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC3, positionLocation, 3, GL_FLOAT, GL_TRUE, 0, 0);
+
+ texCoordBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC2, texCoordLocation, 2, GL_FLOAT, GL_TRUE, 0, 0);
+
+ colorIndexBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+ m_glHelper.vertexAttributePointer(GL_INT, colorIndexLocation, 1, GL_INT, GL_TRUE, 0, 0);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void glUniform1fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat value = 883.0f;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1fv(location, 1, &value);
+
+ // THEN
+ GLfloat setValue = 0.0f;
+ m_func->glGetUniformfv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[2] = { 383.0f, 427.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[2] = { 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[3] = { 572.0f, 1340.0f, 1584.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[3] = { 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint value = 883;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1iv(location, 1, &value);
+
+ // THEN
+ GLint setValue = 0;
+ m_func->glGetUniformiv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[2] = { 383, 427 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2iv(location, 1, values);
+
+ // THEN
+ GLint setValues[2] = { 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[3] = { 572, 1340, 1584 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3iv(location, 1, values);
+
+ // THEN
+ GLint setValues[3] = { 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[4] = { 454, 350, 883, 355 };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4iv(location, 1, values);
+
+ // THEN
+ GLint setValues[4] = { 0, 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint value = 883U;
+ const GLint location = shaderProgram.uniformLocation("multiplier");
+ m_glHelper.glUniform1uiv(location, 1, &value);
+
+ // THEN
+ GLuint setValue = 0U;
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[2] = { 383U, 427U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec2");
+ m_glHelper.glUniform2uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[2] = { 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[3] = { 572U, 1340U, 1584U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec3");
+ m_glHelper.glUniform3uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[3] = { 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[4] = { 454U, 350U, 883U, 355U };
+ const GLint location = shaderProgram.uniformLocation("multiplierVec4");
+ m_glHelper.glUniform4uiv(location, 1, values);
+
+ // THEN
+ GLuint setValues[4] = { 0U, 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ const GLint location = shaderProgram.uniformLocation("m2");
+ m_glHelper.glUniformMatrix2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[9] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f };
+ const GLint location = shaderProgram.uniformLocation("m3");
+ m_glHelper.glUniformMatrix3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 9; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[16] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f, 1603.0f, 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 327.0f };
+ const GLint location = shaderProgram.uniformLocation("m4");
+ m_glHelper.glUniformMatrix4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 16; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m23");
+ m_glHelper.glUniformMatrix2x3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m32");
+ m_glHelper.glUniformMatrix3x2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m24");
+ m_glHelper.glUniformMatrix2x4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m42");
+ m_glHelper.glUniformMatrix4x2fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f};
+ const GLint location = shaderProgram.uniformLocation("m34");
+ m_glHelper.glUniformMatrix3x4fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ const GLint location = shaderProgram.uniformLocation("m43");
+ m_glHelper.glUniformMatrix4x3fv(location, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), location, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+
+ void blitFramebuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ GLint maxSamples;
+ m_func->glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
+ if (maxSamples < 1)
+ QSKIP("This test requires an implementation that supports multisampled textures");
+
+ // GIVEN
+ GLuint fbos[2];
+ GLuint fboTextures[2];
+
+ m_func->glGenFramebuffers(2, fbos);
+ m_func->glGenTextures(2, fboTextures);
+
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, fboTextures[0]);
+ m_func->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, maxSamples, GL_RGBA8, 10, 10, true);
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+
+ m_func->glBindTexture(GL_TEXTURE_2D, fboTextures[1]);
+ m_func->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ m_func->glBindTexture(GL_TEXTURE_2D, 0);
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[1], 0);
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QCOMPARE(status, GLenum(GL_FRAMEBUFFER_COMPLETE));
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[0], 0);
+
+ status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QCOMPARE(status, GLenum(GL_FRAMEBUFFER_COMPLETE));
+
+ m_func->glEnable(GL_MULTISAMPLE);
+ m_func->glClearColor(0.2f, 0.2f, 0.2f, 0.2f);
+ m_func->glClear(GL_COLOR_BUFFER_BIT);
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+
+ // WHEN
+ m_glHelper.blitFramebuffer(0,0,10,10,0,0,10,10, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[1]);
+
+ GLuint result[10*10];
+ m_func->glReadPixels(0,0,10,10,GL_RGBA, GL_UNSIGNED_BYTE, result);
+
+ // THEN
+ GLuint v = (0.2f) * 255;
+ v = v | (v<<8) | (v<<16) | (v<<24);
+ for (GLuint value : result) {
+ QCOMPARE(value, v);
+ }
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+
+ m_func->glDeleteFramebuffers(2, fbos);
+ m_func->glDeleteTextures(2, fboTextures);
+ }
+
+#define ADD_GL_TYPE_ENTRY(Type, Expected) \
+ QTest::newRow(#Type) << Type << Expected;
+
+ void uniformTypeFromGLType_data()
+ {
+ QTest::addColumn<int>("glType");
+ QTest::addColumn<UniformType>("expected");
+
+ ADD_GL_TYPE_ENTRY(GL_FLOAT, UniformType::Float);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_INT, UniformType::Int);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC2, UniformType::IVec2);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC3, UniformType::IVec3);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC4, UniformType::IVec4);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT, UniformType::UInt);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC2, UniformType::UIVec2);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC3, UniformType::UIVec3);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC4, UniformType::UIVec4);
+ ADD_GL_TYPE_ENTRY(GL_BOOL, UniformType::Bool);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC2, UniformType::BVec2);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC3, UniformType::BVec3);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC4, UniformType::BVec4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2, UniformType::Mat2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3, UniformType::Mat3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4, UniformType::Mat4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x3, UniformType::Mat2x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x4, UniformType::Mat2x4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x2, UniformType::Mat3x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x2, UniformType::Mat4x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x3, UniformType::Mat4x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x4, UniformType::Mat3x4);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_RECT, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_CUBE, UniformType::Sampler);
+ }
+
+ void uniformTypeFromGLType()
+ {
+ // GIVEN
+ QFETCH(int, glType);
+ QFETCH(UniformType, expected);
+
+ // WHEN
+ UniformType computed = m_glHelper.uniformTypeFromGLType(glType);
+
+ // THEN
+ QCOMPARE(computed, expected);
+ }
+
+ void drawBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.drawBuffer(GL_FRONT);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ GLint p;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void readBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.readBuffer(GL_FRONT);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_READ_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
+ void rasterMode()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 3.3 functions not supported");
+
+ m_func->glGetError();
+ m_glHelper.rasterMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_POLYGON_MODE, &p);
+ QCOMPARE(p, GL_LINE);
+ }
+
+private:
+ QScopedPointer<QWindow> m_window;
+ QOpenGLContext m_glContext;
+ GraphicsHelperGL3_3 m_glHelper;
+ QOpenGLFunctions_3_3_Core *m_func = nullptr;
+ bool m_initializationSuccessful = false;
+};
+
+#endif
+
+int main(int argc, char *argv[])
+{
+#ifdef TEST_SHOULD_BE_PERFORMED
+ QGuiApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_GraphicsHelperGL3_3 tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+#endif
+ return 0;
+}
+
+#ifdef TEST_SHOULD_BE_PERFORMED
+#include "tst_graphicshelpergl3_3.moc"
+#endif
diff --git a/tests/auto/render/opengl/graphicshelpergl4/graphicshelpergl4.pro b/tests/auto/render/opengl/graphicshelpergl4/graphicshelpergl4.pro
new file mode 100644
index 000000000..5ea881208
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl4/graphicshelpergl4.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+
+TARGET = tst_graphicshelpergl4
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += \
+ tst_graphicshelpergl4.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/graphicshelpergl4/tst_graphicshelpergl4.cpp b/tests/auto/render/opengl/graphicshelpergl4/tst_graphicshelpergl4.cpp
new file mode 100644
index 000000000..8f86f38ce
--- /dev/null
+++ b/tests/auto/render/opengl/graphicshelpergl4/tst_graphicshelpergl4.cpp
@@ -0,0 +1,2584 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DRender/qrendertargetoutput.h>
+#include <Qt3DRender/private/uniform_p.h>
+#include <Qt3DRender/private/attachmentpack_p.h>
+#include <graphicshelpergl4_p.h>
+#include <QOpenGLBuffer>
+#include <QOpenGLFunctions_4_3_Core>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLVertexArrayObject>
+#include <QSurfaceFormat>
+
+#if !defined(QT_OPENGL_ES_2) && defined(QT_OPENGL_4_3)
+
+#define TEST_SHOULD_BE_PERFORMED 1
+
+using namespace Qt3DRender;
+using namespace Qt3DRender::Render;
+using namespace Qt3DRender::Render::OpenGL;
+
+namespace {
+
+const QByteArray vertCode = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "layout(location = 1) in vec3 vertexPosition;\n" \
+ "layout(location = 2) in vec2 vertexTexCoord;\n" \
+ "out vec2 texCoord;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vertexTexCoord;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray vertCodeUniformBuffer = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "layout(location = 1) in vec3 vertexPosition;\n" \
+ "layout(location = 2) in vec2 vertexTexCoord;\n" \
+ "layout(location = 3) in int vertexColorIndex;\n" \
+ "layout(location = 4) in double vertexTexCoordScale;\n" \
+ "out vec2 texCoord;\n" \
+ "flat out int colorIndex;\n" \
+ "void main()\n" \
+ "{\n" \
+ " texCoord = vec2(vertexTexCoordScale * vertexTexCoord);\n" \
+ " colorIndex = vertexColorIndex;\n" \
+ " gl_Position = vec4(vertexPosition, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeFragOutputs = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out vec4 color;\n" \
+ "out vec2 temp;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0);\n" \
+ " temp = vec2(1.0, 0.3);\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloat = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out vec4 color;\n" \
+ "layout(location = 1) uniform float multiplier;\n" \
+ "layout(location = 2) uniform vec2 multiplierVec2;\n" \
+ "layout(location = 3) uniform vec3 multiplierVec3;\n" \
+ "layout(location = 4) uniform vec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " vec4 randomMult = multiplierVec4 + vec4(multiplierVec3, 0.0) + vec4(multiplierVec2, 0.0, 0.0);\n" \
+ " color = vec4(1.0, 0.0, 0.0, 1.0) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsInt = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out ivec4 color;\n" \
+ "layout(location = 1) uniform int multiplier;\n" \
+ "layout(location = 2) uniform ivec2 multiplierVec2;\n" \
+ "layout(location = 3) uniform ivec3 multiplierVec3;\n" \
+ "layout(location = 4) uniform ivec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " ivec4 randomMult = multiplierVec4 + ivec4(multiplierVec3, 0) + ivec4(multiplierVec2, 0, 0);\n" \
+ " color = ivec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsUInt = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out uvec4 color;\n" \
+ "layout(location = 1) uniform uint multiplier;\n" \
+ "layout(location = 2) uniform uvec2 multiplierVec2;\n" \
+ "layout(location = 3) uniform uvec3 multiplierVec3;\n" \
+ "layout(location = 4) uniform uvec4 multiplierVec4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " uvec4 randomMult = multiplierVec4 + uvec4(multiplierVec3, 0) + uvec4(multiplierVec2, 0, 0);\n" \
+ " color = uvec4(1, 0, 0, 1) * randomMult * multiplier;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformsFloatMatrices = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out vec4 color;\n" \
+ "layout(location = 1) uniform mat2 m2;\n" \
+ "layout(location = 2) uniform mat2x3 m23;\n" \
+ "layout(location = 3) uniform mat3x2 m32;\n" \
+ "layout(location = 4) uniform mat2x4 m24;\n" \
+ "layout(location = 5) uniform mat4x2 m42;\n" \
+ "layout(location = 6) uniform mat3 m3;\n" \
+ "layout(location = 7) uniform mat3x4 m34;\n" \
+ "layout(location = 8) uniform mat4x3 m43;\n" \
+ "layout(location = 9) uniform mat4 m4;\n" \
+ "void main()\n" \
+ "{\n" \
+ " float lengthSum = m2[0][0] + m23[0][0] + m32[0][0] + m24[0][0] + m42[0][0] + m3[0][0] + m34[0][0] + m43[0][0] + m4[0][0];\n" \
+ " color = vec4(1, 0, 0, 1) * lengthSum;\n" \
+ "}\n");
+
+const QByteArray fragCodeUniformBuffer = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "out vec4 color;\n" \
+ "in vec2 texCoord;\n" \
+ "flat in int colorIndex;\n" \
+ "layout(binding = 2, std140) uniform ColorArray\n" \
+ "{\n" \
+ " vec4 colors[256];\n" \
+ "};\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = colors[colorIndex] + vec4(texCoord.s, texCoord.t, 0.0, 1.0);\n" \
+ "}\n");
+
+const QByteArray fragCodeSamplers = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "in vec2 texCoord;\n" \
+ "out vec4 color;\n" \
+ "layout(location = 1) uniform sampler1D s1;\n" \
+ "layout(location = 2) uniform sampler2D s2;\n" \
+ "layout(location = 3) uniform sampler2DArray s2a;\n" \
+ "layout(location = 4) uniform sampler3D s3;\n" \
+ "layout(location = 5) uniform samplerCube scube;\n" \
+ "layout(location = 6) uniform sampler2DRect srect;\n" \
+ "void main()\n" \
+ "{\n" \
+ " color = vec4(1, 0, 0, 1) *" \
+ " texture(s1, texCoord.x) *" \
+ " texture(s2, texCoord) *" \
+ " texture(s2a, vec3(texCoord, 0.0)) *" \
+ " texture(s3, vec3(texCoord, 0.0)) *" \
+ " texture(scube, vec3(texCoord, 0)) *" \
+ " texture(srect, texCoord);\n" \
+ "}\n");
+
+const QByteArray fragCodeImages = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "in vec2 texCoord;\n" \
+ "out vec4 color;\n" \
+ "layout(location = 1, rgba32f) readonly uniform image1D s1;\n" \
+ "layout(location = 2, rg16f) readonly uniform image2D s2;\n" \
+ "layout(location = 3, r16f) readonly uniform image2DArray s2a;\n" \
+ "layout(location = 4, rg8) readonly uniform image3D s3;\n" \
+ "layout(location = 5, rgba16_snorm) readonly uniform imageCube scube;\n" \
+ "layout(location = 6, rg16) readonly uniform image2DRect srect;\n" \
+ "void main()\n" \
+ "{\n" \
+ " ivec2 coords = ivec2(texCoord);\n"\
+ " color = vec4(1, 0, 0, 1) *" \
+ " imageLoad(s1, coords.x) *" \
+ " imageLoad(s2, coords) *" \
+ " imageLoad(s2a, ivec3(coords, 0)) *" \
+ " imageLoad(s3, ivec3(coords, 0)) *" \
+ " imageLoad(scube, ivec3(coords, 0)) *" \
+ " imageLoad(srect, coords);\n" \
+ "}\n");
+
+const QByteArray computeShader = QByteArrayLiteral(
+ "#version 430 core\n" \
+ "uniform float particleStep;\n" \
+ "uniform float finalCollisionFactor;\n" \
+ "layout (local_size_x = 1024) in;\n" \
+ "struct ParticleData\n" \
+ "{\n" \
+ " vec4 position;\n" \
+ " vec4 direction;\n" \
+ " vec4 color;\n" \
+ "};\n" \
+ "layout (std140, binding = 6) coherent buffer Particles\n" \
+ "{\n" \
+ " ParticleData particles[];\n" \
+ "} data;\n" \
+ "void main(void)\n" \
+ "{\n" \
+ " uint globalId = gl_GlobalInvocationID.x;\n" \
+ " ParticleData currentParticle = data.particles[globalId];\n" \
+ " currentParticle.position = currentParticle.position + currentParticle.direction * particleStep;\n" \
+ " vec4 acceleration = normalize(vec4(0.0) - currentParticle.position) * finalCollisionFactor;\n" \
+ " currentParticle.direction = currentParticle.direction + acceleration * particleStep;\n" \
+ " data.particles[globalId] = currentParticle;\n" \
+ "}");
+
+} // anonymous
+
+class tst_GraphicsHelperGL4 : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void init()
+ {
+ m_window.reset(new QWindow);
+ m_window->setSurfaceType(QWindow::OpenGLSurface);
+ m_window->setGeometry(0, 0, 10, 10);
+
+ QSurfaceFormat format;
+ format.setVersion(4, 3);
+ format.setProfile(QSurfaceFormat::CoreProfile);
+ format.setDepthBufferSize(24);
+ format.setSamples(4);
+ format.setStencilBufferSize(8);
+ m_window->setFormat(format);
+ m_glContext.setFormat(format);
+
+ m_window->create();
+
+ if (!m_glContext.create()) {
+ qWarning() << "Failed to create OpenGL context";
+ return;
+ }
+
+ if (!m_glContext.makeCurrent(m_window.data())) {
+ qWarning() << "Failed to make OpenGL context current";
+ return;
+ }
+
+ if ((m_func = m_glContext.versionFunctions<QOpenGLFunctions_4_3_Core>()) != nullptr) {
+ m_glHelper.initializeHelper(&m_glContext, m_func);
+ m_initializationSuccessful = true;
+ }
+ }
+
+ void cleanup()
+ {
+ m_glContext.doneCurrent();
+ }
+
+ void alphaTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+ // Deprecated
+ }
+
+ void bindBufferBase()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint bufferId = 0;
+ // WHEN
+ m_func->glGenBuffers(1, &bufferId);
+ // THEN
+ QVERIFY(bufferId != 0);
+
+
+ // WHEN
+ m_func->glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferId);
+ m_glHelper.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, bufferId);
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundToPointBufferId = 0;
+ m_func->glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_BINDING, 2, &boundToPointBufferId);
+ QVERIFY(boundToPointBufferId == GLint(bufferId));
+
+ // Restore to sane state
+ m_func->glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ m_func->glDeleteBuffers(1, &bufferId);
+ }
+
+ void bindFragDataLocation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // WHEN
+ QHash<QString, int> fragLocations;
+ fragLocations.insert(QStringLiteral("temp"), 2);
+ fragLocations.insert(QStringLiteral("color"), 1);
+ m_glHelper.bindFragDataLocation(shaderProgram.programId(), fragLocations);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ const GLint tempLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "temp");
+ const GLint colorLocation = m_func->glGetFragDataLocation(shaderProgram.programId(), "color");
+ QCOMPARE(tempLocation, 2);
+ QCOMPARE(colorLocation, 1);
+ }
+
+ void bindFrameBufferAttachment()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ {
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color2;
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA32F);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureAttachmentId = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0 + 2,
+ GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME,
+ &textureAttachmentId);
+ QCOMPARE(GLuint(textureAttachmentId), texture.textureId());
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ {
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::TargetCubeMap);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA32F);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture.create())
+ qWarning() << "Texture creation failed";
+ texture.allocateStorage();
+ QVERIFY(texture.isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ { // Check All Faces
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+ attachment.m_face = Qt3DRender::QAbstractTexture::AllFaces;
+
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureIsLayered = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_LAYERED,
+ &textureIsLayered);
+ QCOMPARE(textureIsLayered, GL_TRUE);
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ { // Check Specific Faces
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color0;
+ attachment.m_face = Qt3DRender::QAbstractTexture::CubeMapNegativeZ;
+
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint textureFace = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE,
+ &textureFace);
+ QCOMPARE(textureFace, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
+ GLint textureIsLayered = 0;
+ m_func->glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_FRAMEBUFFER_ATTACHMENT_LAYERED,
+ &textureIsLayered);
+ QCOMPARE(textureIsLayered, GL_FALSE);
+
+ // Restore state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+ }
+ }
+
+ void bindFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBODraw);
+
+ // THEN
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBORead);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // WHEN
+ m_glHelper.bindFrameBufferObject(fboId, GraphicsHelperInterface::FBOReadAndDraw);
+
+ // THEN
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+ boundindFBOId = 0;
+ m_func->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &boundindFBOId);
+ QVERIFY(GLuint(boundindFBOId) == fboId);
+
+ // Cleanup
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void bindImageTexture()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+
+ // THEN
+ QVERIFY(texture.textureId() != 0 && texture.isCreated() && texture.isStorageAllocated());
+
+ // WHEN
+ m_glHelper.bindImageTexture(0,
+ texture.textureId(),
+ 0,
+ GL_FALSE,
+ 0,
+ GL_READ_WRITE,
+ GL_RGBA8UI);
+
+ // THEN
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void bindShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Compute, computeShader);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ GLint index = m_func->glGetProgramResourceIndex(shaderProgram.programId(),
+ GL_SHADER_STORAGE_BLOCK,
+ "Particles");
+ // THEN
+ GLint binding = -1;
+ GLenum prop = GL_BUFFER_BINDING;
+ m_func->glGetProgramResourceiv(shaderProgram.programId(),
+ GL_SHADER_STORAGE_BLOCK,
+ index,
+ 1, &prop,
+ 4, NULL, &binding);
+ QCOMPARE(binding, 6);
+
+ // WHEN
+ m_glHelper.bindShaderStorageBlock(shaderProgram.programId(), index, 1);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ m_func->glGetProgramResourceiv(shaderProgram.programId(),
+ GL_SHADER_STORAGE_BLOCK,
+ index,
+ 1, &prop,
+ 4, NULL, &binding);
+ QCOMPARE(binding, 1);
+ }
+
+ void bindUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ GLint index = m_func->glGetUniformBlockIndex(shaderProgram.programId(), "ColorArray");
+ m_glHelper.bindUniformBlock(shaderProgram.programId(), index, 1);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void blendEquation()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLint equation = 0;
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_ADD);
+
+ // WHEN
+ m_glHelper.blendEquation(GL_FUNC_REVERSE_SUBTRACT);
+
+ // THEN
+ m_func->glGetIntegerv(GL_BLEND_EQUATION_RGB, &equation);
+ QCOMPARE(equation, GL_FUNC_REVERSE_SUBTRACT);
+ }
+
+ void blendFunci()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLint destinationRgb = 0;
+ GLint destinationAlpha = 0;
+ GLint sourceRgb = 0;
+ GLint sourceAlpha = 0;
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_RGB, 4, &sourceRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_RGB, 4, &destinationRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_ALPHA, 4, &sourceAlpha);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_ALPHA, 4, &destinationAlpha);
+
+ // THEN
+ QCOMPARE(destinationAlpha, GL_ZERO);
+ QCOMPARE(destinationRgb, GL_ZERO);
+ QCOMPARE(sourceRgb, GL_ONE);
+ QCOMPARE(sourceAlpha, GL_ONE);
+
+ // WHEN
+ m_glHelper.blendFunci(4, GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
+
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_RGB, 4, &sourceRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_RGB, 4, &destinationRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_ALPHA, 4, &sourceAlpha);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_ALPHA, 4, &destinationAlpha);
+
+ // THEN
+ QCOMPARE(destinationAlpha, GL_ONE_MINUS_SRC_ALPHA);
+ QCOMPARE(destinationRgb, GL_ONE_MINUS_SRC_ALPHA);
+ QCOMPARE(sourceRgb, GL_SRC_COLOR);
+ QCOMPARE(sourceAlpha, GL_SRC_COLOR);
+
+ // Reset default
+ m_glHelper.blendFunci(4, GL_ONE, GL_ZERO);
+ }
+
+ void blendFuncSeparatei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLint destinationRgb = 0;
+ GLint destinationAlpha = 0;
+ GLint sourceRgb = 0;
+ GLint sourceAlpha = 0;
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_RGB, 2, &sourceRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_RGB, 2, &destinationRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_ALPHA, 2, &sourceAlpha);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_ALPHA, 2, &destinationAlpha);
+
+ // THEN
+ QCOMPARE(destinationAlpha, GL_ZERO);
+ QCOMPARE(destinationRgb, GL_ZERO);
+ QCOMPARE(sourceRgb, GL_ONE);
+ QCOMPARE(sourceAlpha, GL_ONE);
+
+ // WHEN
+ m_glHelper.blendFuncSeparatei(2, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_RGB, 2, &sourceRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_RGB, 2, &destinationRgb);
+ m_func->glGetIntegeri_v(GL_BLEND_SRC_ALPHA, 2, &sourceAlpha);
+ m_func->glGetIntegeri_v(GL_BLEND_DST_ALPHA, 2, &destinationAlpha);
+
+ // THEN
+ QCOMPARE(destinationAlpha, GL_ONE_MINUS_SRC_ALPHA);
+ QCOMPARE(destinationRgb, GL_ONE_MINUS_SRC_COLOR);
+ QCOMPARE(sourceRgb, GL_SRC_COLOR);
+ QCOMPARE(sourceAlpha, GL_SRC_ALPHA);
+
+ // Reset default
+ m_glHelper.blendFunci(4, GL_ONE, GL_ZERO);
+ }
+
+ void boundFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ // THEN
+ GLint boundBuffer = 0;
+ m_func->glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &boundBuffer);
+ QCOMPARE(GLuint(boundBuffer), fboId);
+
+ // THEN
+ QCOMPARE(m_glHelper.boundFrameBufferObject(), fboId);
+
+ // Reset state
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void checkFrameBufferComplete()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ Attachment attachment;
+ attachment.m_point = QRenderTargetOutput::Color1;
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+ m_glHelper.bindFrameBufferAttachment(&texture, attachment);
+
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ QVERIFY(m_glHelper.checkFrameBufferComplete());
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void clearBufferf()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ // Create 4 attachments
+ QOpenGLTexture *textures[4];
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // Set Draw buffers
+ GLenum clearBufferEnum = GL_COLOR_ATTACHMENT3;
+ m_func->glDrawBuffers(1, &clearBufferEnum);
+
+ const GLint bufferIndex = 0; // index of the element in the draw buffers
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // WHEN
+ const QVector4D clearValue1 = QVector4D(0.5f, 0.2f, 0.4f, 0.8f);
+ m_func->glClearBufferfv(GL_COLOR, bufferIndex, reinterpret_cast<const float *>(&clearValue1));
+ error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ QVector<QVector4D> colors(512 * 512);
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue1);
+ }
+
+ // WHEN
+ const QVector4D clearValue2 = QVector4D(0.4f, 0.5f, 0.4f, 1.0f);
+ m_glHelper.clearBufferf(bufferIndex, clearValue2);
+
+ // THEN
+ textures[3]->bind();
+ m_func->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, colors.data());
+ textures[3]->release();
+ for (const QVector4D c : colors) {
+ QVERIFY(c == clearValue2);
+ }
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void createFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // WHEN
+ const GLuint fboId = m_glHelper.createFrameBufferObject();
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // Restore
+ m_func->glDeleteFramebuffers(1, &fboId);
+ }
+
+ void depthMask()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLboolean depthWritingEnabled = false;
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+
+ // THEN
+ QVERIFY(depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_FALSE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(!depthWritingEnabled);
+
+ // WHEN
+ m_glHelper.depthMask(GL_TRUE);
+
+ // THEN
+ m_func->glGetBooleanv(GL_DEPTH_WRITEMASK, &depthWritingEnabled);
+ QVERIFY(depthWritingEnabled);
+ }
+
+ void depthTest()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_DEPTH_TEST);
+ m_func->glDepthFunc(GL_LESS);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LEQUAL);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ GLint depthMode = 0;
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LEQUAL);
+
+ // WHEN
+ m_glHelper.depthTest(GL_LESS);
+ QVERIFY(m_func->glIsEnabled(GL_DEPTH_TEST));
+ m_func->glGetIntegerv(GL_DEPTH_FUNC, &depthMode);
+ QCOMPARE(depthMode, GL_LESS);
+ }
+
+ void disableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_CLIP_DISTANCE0 + 5);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+
+ // WHEN
+ m_glHelper.disableClipPlane(5);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 5));
+ }
+
+ void disablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 2));
+
+ // WHEN
+ m_glHelper.disablei(GL_BLEND, 2);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 2));
+ }
+
+ void disablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.disablePrimitiveRestart();
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ }
+
+ void drawBuffers()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);
+ QOpenGLTexture *textures[4];
+
+ // Create 4 attachments
+ for (int i = 0; i < 4; ++i) {
+ Attachment attachment;
+ attachment.m_point = static_cast<QRenderTargetOutput::AttachmentPoint>(i);
+
+ QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+ textures[i] = texture;
+ texture->setSize(512, 512);
+ texture->setFormat(QOpenGLTexture::RGBA32F);
+ texture->setMinificationFilter(QOpenGLTexture::Linear);
+ texture->setMagnificationFilter(QOpenGLTexture::Linear);
+ texture->setWrapMode(QOpenGLTexture::ClampToEdge);
+ if (!texture->create())
+ qWarning() << "Texture creation failed";
+ texture->allocateStorage();
+ QVERIFY(texture->isStorageAllocated());
+ GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ m_glHelper.bindFrameBufferAttachment(texture, attachment);
+ }
+ // THEN
+ GLenum status = m_func->glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ // WHEN
+ GLenum bufferEnum = GL_COLOR_ATTACHMENT4;
+ m_func->glDrawBuffers(1, &bufferEnum);
+
+ // THEN
+ GLint enumValue = -1;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT4);
+
+ // WHEN
+ GLint newBufferEnum = 2;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // WHEN
+ newBufferEnum = 0;
+ m_glHelper.drawBuffers(1, &newBufferEnum);
+
+ // THEN
+ m_func->glGetIntegerv(GL_DRAW_BUFFER0, &enumValue);
+ QCOMPARE(enumValue, GL_COLOR_ATTACHMENT0 + newBufferEnum);
+
+ // Restore
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glDeleteFramebuffers(1, &fboId);
+ for (int i = 0; i < 4; ++i)
+ delete textures[i];
+ }
+
+ void enableClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_CLIP_DISTANCE0 + 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+
+ // WHEN
+ m_glHelper.enableClipPlane(4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_CLIP_DISTANCE0 + 4));
+ }
+
+ void enablei()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabledi(GL_BLEND, 4));
+
+ // WHEN
+ m_glHelper.enablei(GL_BLEND, 4);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabledi(GL_BLEND, 4));
+ }
+
+ void enablePrimitiveRestart()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+
+ // WHEN
+ m_glHelper.enablePrimitiveRestart(883);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PRIMITIVE_RESTART));
+ GLint restartIndex = 0;
+ m_func->glGetIntegerv(GL_PRIMITIVE_RESTART_INDEX, &restartIndex);
+ QCOMPARE(restartIndex, 883);
+
+ // Restore
+ m_func->glDisable(GL_PRIMITIVE_RESTART);
+ }
+
+ void enableVertexAttribute()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+ shaderProgram.bind();
+
+ // WHEN
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void frontFace()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glFrontFace(GL_CW);
+
+ // THEN
+ GLint face = 0;
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CW);
+
+ // WHEN
+ m_glHelper.frontFace(GL_CCW);
+
+ // THEN
+ m_func->glGetIntegerv(GL_FRONT_FACE, &face);
+ QCOMPARE(face, GL_CCW);
+ }
+
+ void getRenderBufferDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint renderBufferId = 0;
+ m_func->glGenRenderbuffers(1, &renderBufferId);
+ QVERIFY(renderBufferId != 0);
+
+ // WHEN
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, renderBufferId);
+ m_func->glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8, 512, 512);
+ m_func->glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ const QSize dimensions = m_glHelper.getRenderBufferDimensions(renderBufferId);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+
+ // Restore
+ m_func->glDeleteRenderbuffers(1, &renderBufferId);
+ }
+
+ void getTextureDimensions()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLTexture texture(QOpenGLTexture::Target2D);
+ texture.setSize(512, 512);
+ texture.setFormat(QOpenGLTexture::RGBA8U);
+ texture.setMinificationFilter(QOpenGLTexture::Linear);
+ texture.setMagnificationFilter(QOpenGLTexture::Linear);
+ texture.create();
+ texture.allocateStorage();
+
+ // WHEN
+ const QSize dimensions = m_glHelper.getTextureDimensions(texture.textureId(), GL_TEXTURE_2D);
+
+ // THEN
+ QCOMPARE(dimensions, QSize(512, 512));
+ }
+
+ void pointSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glEnable(GL_PROGRAM_POINT_SIZE);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ GLfloat size = 0;
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 1.0f);
+
+ // WHEN
+ m_glHelper.pointSize(false, 0.5f);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_PROGRAM_POINT_SIZE));
+ m_func->glGetFloatv(GL_POINT_SIZE, &size);
+ QCOMPARE(size, 0.5f);
+ }
+
+ void maxClipPlaneCount()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLint maxCount = -1;
+ m_func->glGetIntegerv(GL_MAX_CLIP_PLANES, &maxCount);
+
+ // THEN
+ QCOMPARE(maxCount, m_glHelper.maxClipPlaneCount());
+ }
+
+ void programUniformBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ const QVector<ShaderUniformBlock> activeUniformBlocks = m_glHelper.programUniformBlocks(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniformBlocks.size(), 1);
+ const ShaderUniformBlock uniformBlock = activeUniformBlocks.first();
+
+ QCOMPARE(uniformBlock.m_activeUniformsCount, 1);
+ QCOMPARE(uniformBlock.m_name, QStringLiteral("ColorArray"));
+ QCOMPARE(uniformBlock.m_binding, 2);
+ }
+
+ void programAttributesAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeSamplers);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderAttribute> activeAttributes = m_glHelper.programAttributesAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeAttributes.size(), 2);
+ std::sort(activeAttributes.begin(), activeAttributes.end(), [] (const ShaderAttribute &a, const ShaderAttribute &b) { return a.m_location < b.m_location; });
+
+ const ShaderAttribute attribute1 = activeAttributes.at(0);
+ QCOMPARE(attribute1.m_name, QStringLiteral("vertexPosition"));
+ QCOMPARE(attribute1.m_size, 1);
+ QCOMPARE(attribute1.m_location, 1);
+ QCOMPARE(attribute1.m_type, GLenum(GL_FLOAT_VEC3));
+
+ const ShaderAttribute attribute2 = activeAttributes.at(1);
+ QCOMPARE(attribute2.m_name, QStringLiteral("vertexTexCoord"));
+ QCOMPARE(attribute2.m_size, 1);
+ QCOMPARE(attribute2.m_location, 2);
+ QCOMPARE(attribute2.m_type, GLenum(GL_FLOAT_VEC2));
+ }
+
+ void programUniformsAndLocations()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+
+ // THEN
+ QCOMPARE(activeUniforms.size(), 4);
+ std::sort(activeUniforms.begin(), activeUniforms.end(), [] (const ShaderUniform &a, const ShaderUniform &b) { return a.m_location < b.m_location; });
+
+ const ShaderUniform uniform1 = activeUniforms.at(0);
+ QCOMPARE(uniform1.m_location, 1);
+ QCOMPARE(uniform1.m_offset, -1);
+ QCOMPARE(uniform1.m_blockIndex, -1);
+ QCOMPARE(uniform1.m_arrayStride, -1);
+ QCOMPARE(uniform1.m_matrixStride, -1);
+ QCOMPARE(uniform1.m_size, 1);
+ QCOMPARE(uniform1.m_type, GLenum(GL_FLOAT));
+ QCOMPARE(uniform1.m_name, QStringLiteral("multiplier"));
+
+ const ShaderUniform uniform2 = activeUniforms.at(1);
+ QCOMPARE(uniform2.m_location, 2);
+ QCOMPARE(uniform2.m_offset, -1);
+ QCOMPARE(uniform2.m_blockIndex, -1);
+ QCOMPARE(uniform2.m_arrayStride, -1);
+ QCOMPARE(uniform2.m_matrixStride, -1);
+ QCOMPARE(uniform2.m_size, 1);
+ QCOMPARE(uniform2.m_type, GLenum(GL_FLOAT_VEC2));
+ QCOMPARE(uniform2.m_name, QStringLiteral("multiplierVec2"));
+
+ const ShaderUniform uniform3 = activeUniforms.at(2);
+ QCOMPARE(uniform3.m_location, 3);
+ QCOMPARE(uniform3.m_offset, -1);
+ QCOMPARE(uniform3.m_blockIndex, -1);
+ QCOMPARE(uniform3.m_arrayStride, -1);
+ QCOMPARE(uniform3.m_matrixStride, -1);
+ QCOMPARE(uniform3.m_size, 1);
+ QCOMPARE(uniform3.m_type, GLenum(GL_FLOAT_VEC3));
+ QCOMPARE(uniform3.m_name, QStringLiteral("multiplierVec3"));
+
+ const ShaderUniform uniform4 = activeUniforms.at(3);
+ QCOMPARE(uniform4.m_location, 4);
+ QCOMPARE(uniform4.m_offset, -1);
+ QCOMPARE(uniform4.m_blockIndex, -1);
+ QCOMPARE(uniform4.m_arrayStride, -1);
+ QCOMPARE(uniform4.m_matrixStride, -1);
+ QCOMPARE(uniform4.m_size, 1);
+ QCOMPARE(uniform4.m_type, GLenum(GL_FLOAT_VEC4));
+ QCOMPARE(uniform4.m_name, QStringLiteral("multiplierVec4"));
+ }
+
+ void programShaderStorageBlock()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Compute, computeShader);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ const QVector<ShaderStorageBlock> activeShaderStorageBlocks = m_glHelper.programShaderStorageBlocks(shaderProgram.programId());
+
+ // THEN
+ QVERIFY(activeShaderStorageBlocks.size() == 1);
+ ShaderStorageBlock block = activeShaderStorageBlocks.first();
+ QCOMPARE(block.m_name, QStringLiteral("Particles"));
+ QCOMPARE(block.m_activeVariablesCount, 3);
+ QCOMPARE(block.m_index, 0);
+ QCOMPARE(block.m_binding, 6);
+ QCOMPARE(block.m_size, (4 + 4 + 4) * 4);
+ }
+
+ void releaseFrameBufferObject()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fboId;
+ m_func->glGenFramebuffers(1, &fboId);
+
+ // THEN
+ QVERIFY(fboId != 0);
+
+ // WHEN
+ m_glHelper.releaseFrameBufferObject(fboId);
+
+ // THEN
+ QVERIFY(!m_func->glIsFramebuffer(fboId));
+ }
+
+ void setMSAAEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_MULTISAMPLE));
+
+ // WHEN
+ m_glHelper.setMSAAEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_MULTISAMPLE));
+ }
+
+ void setAlphaCoverageEnabled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+
+ // WHEN
+ m_glHelper.setAlphaCoverageEnabled(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE));
+ }
+
+ void setClipPlane()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+ // Deprecated in GL 4
+ }
+
+ void setSeamlessCubemap()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+ void setVerticesPerPatch()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ m_func->glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(true);
+
+ // THEN
+ QVERIFY(m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+
+ // WHEN
+ m_glHelper.setSeamlessCubemap(false);
+
+ // THEN
+ QVERIFY(!m_func->glIsEnabled(GL_TEXTURE_CUBE_MAP_SEAMLESS));
+ }
+
+ void supportsFeature()
+ {
+ for (int i = 0; i <= GraphicsHelperInterface::Fences; ++i)
+ QVERIFY(m_glHelper.supportsFeature(static_cast<GraphicsHelperInterface::Feature>(i)));
+ }
+
+
+#define ADD_UNIFORM_ENTRY(FragShader, Type, Location, ComponentSize, ExpectedRawSize) \
+ QTest::newRow(#FragShader"_"#Type) << FragShader << Type << Location << ComponentSize << ExpectedRawSize;
+
+ void uniformsByteSize_data()
+ {
+ QTest::addColumn<QByteArray>("fragShader");
+ QTest::addColumn<int>("type");
+ QTest::addColumn<int>("location");
+ QTest::addColumn<int>("componentSize");
+ QTest::addColumn<int>("expectedByteSize");
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, GL_FLOAT, 1, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, GL_FLOAT_VEC2, 2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, GL_FLOAT_VEC3, 3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloat, GL_FLOAT_VEC4, 4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, GL_INT, 1, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, GL_INT_VEC2, 2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, GL_INT_VEC3, 3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsInt, GL_INT_VEC4, 4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, GL_UNSIGNED_INT, 1, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, GL_UNSIGNED_INT_VEC2, 2, 1, 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, GL_UNSIGNED_INT_VEC3, 3, 1, 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsUInt, GL_UNSIGNED_INT_VEC4, 4, 1, 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT2, 1, 1, 4 * 2 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT2x3, 2, 1, 4 * 2 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT3x2, 3, 1, 4 * 3 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT2x4, 4, 1, 4 * 2 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT4x2, 5, 1, 4 * 4 * 2);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT3, 6, 1, 4 * 3 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT3x4, 7, 1, 4 * 3 * 4);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT4x3, 8, 1, 4 * 4 * 3);
+ ADD_UNIFORM_ENTRY(fragCodeUniformsFloatMatrices, GL_FLOAT_MAT4, 9, 1, 4 * 4 * 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_1D, 1, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_2D, 2, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_2D_ARRAY, 3, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_3D, 4, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_CUBE, 5, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeSamplers, GL_SAMPLER_2D_RECT, 6, 1, 4);
+
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_1D, 1, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_2D, 2, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_2D_ARRAY, 3, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_3D, 4, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_CUBE, 5, 1, 4);
+ ADD_UNIFORM_ENTRY(fragCodeImages, GL_IMAGE_2D_RECT, 6, 1, 4);
+ }
+
+ void uniformsByteSize()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QFETCH(QByteArray, fragShader);
+ QFETCH(int, type);
+ QFETCH(int, location);
+ QFETCH(int, componentSize);
+ QFETCH(int, expectedByteSize);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragShader);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ const QVector<ShaderUniform> activeUniforms = m_glHelper.programUniformsAndLocations(shaderProgram.programId());
+ ShaderUniform matchingUniform;
+ for (const ShaderUniform &u : activeUniforms) {
+ if (u.m_location == location) {
+ matchingUniform = u;
+ break;
+ }
+ }
+
+ // THEN
+ QCOMPARE(matchingUniform.m_location, location);
+ QCOMPARE(matchingUniform.m_type, GLuint(type));
+ QCOMPARE(matchingUniform.m_size, componentSize);
+
+ // WHEN
+ const int computedRawByteSize = m_glHelper.uniformByteSize(matchingUniform);
+
+ // THEN
+ QCOMPARE(expectedByteSize, computedRawByteSize);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void useProgram()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeFragOutputs);
+
+ // THEN
+ QVERIFY(shaderProgram.link());
+
+ GLint currentProg = 0;
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+
+ // WHEN
+ m_glHelper.useProgram(shaderProgram.programId());
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QCOMPARE(GLuint(currentProg), shaderProgram.programId());
+
+ // WHEN
+ m_glHelper.useProgram(0);
+
+ // THEN
+ m_func->glGetIntegerv(GL_CURRENT_PROGRAM, &currentProg);
+ QVERIFY(currentProg == 0);
+ }
+
+ void vertexAttribDivisor()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+ }
+
+ void vertexAttributePointer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLVertexArrayObject vao;
+ vao.create();
+ QOpenGLVertexArrayObject::Binder binder(&vao);
+
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCodeUniformBuffer);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformBuffer);
+ QVERIFY(shaderProgram.link());
+
+ GLint positionLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexPosition");
+ GLint texCoordLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoord");
+ GLint colorIndexLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexColorIndex");
+ GLint texCoordScaleLocation = m_func->glGetAttribLocation(shaderProgram.programId(), "vertexTexCoordScale");
+
+ const int vertexCount = 99;
+ QOpenGLBuffer positionBuffer(QOpenGLBuffer::VertexBuffer);
+ positionBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ positionBuffer.create();
+ positionBuffer.bind();
+ positionBuffer.allocate(vertexCount * sizeof(QVector3D));
+
+ QOpenGLBuffer texCoordBuffer(QOpenGLBuffer::VertexBuffer);
+ texCoordBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ texCoordBuffer.create();
+ texCoordBuffer.allocate(vertexCount * sizeof(QVector2D));
+
+ QOpenGLBuffer colorIndexBuffer(QOpenGLBuffer::VertexBuffer);
+ colorIndexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ colorIndexBuffer.create();
+ colorIndexBuffer.allocate(vertexCount * sizeof(int));
+
+ QOpenGLBuffer scaleBuffer(QOpenGLBuffer::VertexBuffer);
+ scaleBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
+ scaleBuffer.create();
+ scaleBuffer.allocate(vertexCount * sizeof(double));
+
+ // WHEN
+ shaderProgram.bind();
+ positionBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(positionLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC3, positionLocation, 3, GL_FLOAT, GL_TRUE, 0, 0);
+
+ texCoordBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(texCoordLocation);
+ m_glHelper.vertexAttributePointer(GL_FLOAT_VEC2, texCoordLocation, 2, GL_FLOAT, GL_TRUE, 0, 0);
+
+ colorIndexBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+ m_glHelper.vertexAttributePointer(GL_INT, colorIndexLocation, 1, GL_INT, GL_TRUE, 0, 0);
+
+ scaleBuffer.bind();
+ m_glHelper.enableVertexAttributeArray(colorIndexLocation);
+ m_glHelper.vertexAttributePointer(GL_DOUBLE, texCoordScaleLocation, 1, GL_DOUBLE, GL_TRUE, 0, 0);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void glUniform1fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat value = 883.0f;
+ m_glHelper.glUniform1fv(1, 1, &value);
+
+ // THEN
+ GLfloat setValue = 0.0f;
+ m_func->glGetUniformfv(shaderProgram.programId(), 1, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[2] = { 383.0f, 427.0f };
+ m_glHelper.glUniform2fv(2, 1, values);
+
+ // THEN
+ GLfloat setValues[2] = { 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 2, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[3] = { 572.0f, 1340.0f, 1584.0f };
+ m_glHelper.glUniform3fv(3, 1, values);
+
+ // THEN
+ GLfloat setValues[3] = { 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 3, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloat);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ m_glHelper.glUniform4fv(4, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 4, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(setValues[i], values[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint value = 883;
+ m_glHelper.glUniform1iv(1, 1, &value);
+
+ // THEN
+ GLint setValue = 0;
+ m_func->glGetUniformiv(shaderProgram.programId(), 1, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[2] = { 383, 427 };
+ m_glHelper.glUniform2iv(2, 1, values);
+
+ // THEN
+ GLint setValues[2] = { 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), 2, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[3] = { 572, 1340, 1584 };
+ m_glHelper.glUniform3iv(3, 1, values);
+
+ // THEN
+ GLint setValues[3] = { 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), 3, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4iv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLint values[4] = { 454, 350, 883, 355 };
+ m_glHelper.glUniform4iv(4, 1, values);
+
+ // THEN
+ GLint setValues[4] = { 0, 0, 0, 0 };
+ m_func->glGetUniformiv(shaderProgram.programId(), 4, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform1uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint value = 883U;
+ m_glHelper.glUniform1uiv(1, 1, &value);
+
+ // THEN
+ GLuint setValue = 0U;
+ m_func->glGetUniformuiv(shaderProgram.programId(), 1, &setValue);
+ QCOMPARE(value, setValue);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform2uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[2] = { 383U, 427U };
+ m_glHelper.glUniform2uiv(2, 1, values);
+
+ // THEN
+ GLuint setValues[2] = { 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), 2, setValues);
+ for (int i = 0; i < 2; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform3uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[3] = { 572U, 1340U, 1584U };
+ m_glHelper.glUniform3uiv(3, 1, values);
+
+ // THEN
+ GLuint setValues[3] = { 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), 3, setValues);
+ for (int i = 0; i < 3; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniform4uiv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsUInt);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLuint values[4] = { 454U, 350U, 883U, 355U };
+ m_glHelper.glUniform4uiv(4, 1, values);
+
+ // THEN
+ GLuint setValues[4] = { 0U, 0U, 0U, 0U };
+ m_func->glGetUniformuiv(shaderProgram.programId(), 4, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[4] = { 454.0f, 350.0f, 883.0f, 355.0f };
+ m_glHelper.glUniformMatrix2fv(1, 1, values);
+
+ // THEN
+ GLfloat setValues[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 1, setValues);
+ for (int i = 0; i < 4; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[9] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f };
+ m_glHelper.glUniformMatrix3fv(6, 1, values);
+
+ // THEN
+ GLfloat setValues[9] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 6, setValues);
+ for (int i = 0; i < 9; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[16] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f, 1200.0f, 427.0f, 396.0f, 1603.0f, 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 327.0f };
+ m_glHelper.glUniformMatrix4fv(9, 1, values);
+
+ // THEN
+ GLfloat setValues[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 9, setValues);
+ for (int i = 0; i < 16; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ m_glHelper.glUniformMatrix2x3fv(2, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 2, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[6] = { 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ m_glHelper.glUniformMatrix3x2fv(3, 1, values);
+
+ // THEN
+ GLfloat setValues[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 3, setValues);
+ for (int i = 0; i < 6; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix2x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ m_glHelper.glUniformMatrix2x4fv(4, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 4, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x2fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[8] = { 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ m_glHelper.glUniformMatrix4x2fv(5, 1, values);
+
+ // THEN
+ GLfloat setValues[8] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 5, setValues);
+ for (int i = 0; i < 8; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix3x4fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 5.3f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f,};
+ m_glHelper.glUniformMatrix3x4fv(7, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 7, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+ void glUniformMatrix4x3fv()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ QOpenGLShaderProgram shaderProgram;
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertCode);
+ shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragCodeUniformsFloatMatrices);
+ QVERIFY(shaderProgram.link());
+
+ // WHEN
+ m_func->glUseProgram(shaderProgram.programId());
+ GLfloat values[12] = { 55.0f, 5.7f, 383.0f, 6.2f, 383.0f, 427.0f, 454.0f, 350.0f, 883.0f, 355.0f, 1340.0f, 1584.0f};
+ m_glHelper.glUniformMatrix4x3fv(8, 1, values);
+
+ // THEN
+ GLfloat setValues[12] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+ m_func->glGetUniformfv(shaderProgram.programId(), 8, setValues);
+ for (int i = 0; i < 12; ++i)
+ QCOMPARE(values[i], setValues[i]);
+
+ // Restore
+ m_func->glUseProgram(0);
+ }
+
+
+ void blitFramebuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ // GIVEN
+ GLuint fbos[2];
+ GLuint fboTextures[2];
+
+ m_func->glGenFramebuffers(2, fbos);
+ m_func->glGenTextures(2, fboTextures);
+
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, fboTextures[0]);
+ m_func->glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, 10, 10, true);
+ m_func->glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
+
+ m_func->glBindTexture(GL_TEXTURE_2D, fboTextures[1]);
+ m_func->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 10, 10, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ m_func->glBindTexture(GL_TEXTURE_2D, 0);
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[1]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[1], 0);
+
+ GLenum status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ m_func->glBindFramebuffer(GL_FRAMEBUFFER, fbos[0]);
+ m_func->glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fboTextures[0], 0);
+
+ status = m_func->glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ QVERIFY(status == GL_FRAMEBUFFER_COMPLETE);
+
+ m_func->glEnable(GL_MULTISAMPLE);
+ m_func->glClearColor(0.2f, 0.2f, 0.2f, 0.2f);
+ m_func->glClear(GL_COLOR_BUFFER_BIT);
+ m_func->glDisable(GL_MULTISAMPLE);
+
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]);
+
+ // WHEN
+ m_glHelper.blitFramebuffer(0,0,10,10,0,0,10,10, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[1]);
+
+ GLuint result[10*10];
+ m_func->glReadPixels(0,0,10,10,GL_RGBA, GL_UNSIGNED_BYTE, result);
+
+ // THEN
+ GLuint v = (0.2f) * 255;
+ v = v | (v<<8) | (v<<16) | (v<<24);
+ for (GLuint value : result) {
+ QCOMPARE(value, v);
+ }
+ m_func->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ m_func->glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+
+ m_func->glDeleteFramebuffers(2, fbos);
+ m_func->glDeleteTextures(2, fboTextures);
+ }
+
+#define ADD_GL_TYPE_ENTRY(Type, Expected) \
+ QTest::newRow(#Type) << Type << Expected;
+
+ void uniformTypeFromGLType_data()
+ {
+ QTest::addColumn<int>("glType");
+ QTest::addColumn<UniformType>("expected");
+
+ ADD_GL_TYPE_ENTRY(GL_FLOAT, UniformType::Float);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC2, UniformType::Vec2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_VEC3, UniformType::Vec3);
+ ADD_GL_TYPE_ENTRY(GL_INT, UniformType::Int);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC2, UniformType::IVec2);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC3, UniformType::IVec3);
+ ADD_GL_TYPE_ENTRY(GL_INT_VEC4, UniformType::IVec4);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT, UniformType::UInt);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC2, UniformType::UIVec2);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC3, UniformType::UIVec3);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_VEC4, UniformType::UIVec4);
+ ADD_GL_TYPE_ENTRY(GL_BOOL, UniformType::Bool);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC2, UniformType::BVec2);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC3, UniformType::BVec3);
+ ADD_GL_TYPE_ENTRY(GL_BOOL_VEC4, UniformType::BVec4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2, UniformType::Mat2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3, UniformType::Mat3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4, UniformType::Mat4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x3, UniformType::Mat2x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT2x4, UniformType::Mat2x4);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x2, UniformType::Mat3x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x2, UniformType::Mat4x2);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT4x3, UniformType::Mat4x3);
+ ADD_GL_TYPE_ENTRY(GL_FLOAT_MAT3x4, UniformType::Mat3x4);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_1D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_RECT, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_2D_ARRAY_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE_MAP_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_INT_SAMPLER_CUBE_MAP_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_1D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_3D, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_BUFFER, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_CUBE, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY, UniformType::Sampler);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_1D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_2D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_3D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_2D_RECT, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_CUBE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_BUFFER, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_1D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_2D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_CUBE_MAP_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_2D_MULTISAMPLE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_IMAGE_2D_MULTISAMPLE_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_1D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_2D, UniformType::Image); ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_3D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_2D_RECT, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_CUBE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_BUFFER, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_1D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_2D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_CUBE_MAP_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_2D_MULTISAMPLE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_1D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_2D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_3D, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_2D_RECT, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_CUBE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_BUFFER, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_1D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_2D_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE, UniformType::Image);
+ ADD_GL_TYPE_ENTRY(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY, UniformType::Image);
+ }
+
+ void uniformTypeFromGLType()
+ {
+ // GIVEN
+ QFETCH(int, glType);
+ QFETCH(UniformType, expected);
+
+ // WHEN
+ UniformType computed = m_glHelper.uniformTypeFromGLType(glType);
+
+ // THEN
+ QCOMPARE(computed, expected);
+ }
+
+ void drawBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.drawBuffer(GL_FRONT);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // THEN
+ GLint p;
+ m_func->glGetIntegerv(GL_DRAW_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void readBuffer()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ m_glHelper.readBuffer(GL_FRONT);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_READ_BUFFER, &p);
+ QCOMPARE(p, GL_FRONT);
+ }
+
+ void fenceSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ // THEN
+ QVERIFY(sync != nullptr);
+ QCOMPARE(m_func->glIsSync(sync), GL_TRUE);
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void clientWaitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ QElapsedTimer t;
+ t.start();
+
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+
+ m_glHelper.clientWaitSync(sync, 1000000);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ qDebug() << t.nsecsElapsed();
+ }
+
+ void waitSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ }
+
+ void wasSyncSignaled()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_func->glFlush();
+ m_glHelper.waitSync(sync);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+
+ // Shouldn't loop forever
+ while (!m_glHelper.wasSyncSignaled(sync))
+ ;
+ }
+
+ void deleteSync()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 Core functions not supported");
+
+ m_func->glGetError();
+
+ // WHEN
+ GLsync sync = reinterpret_cast<GLsync>(m_glHelper.fenceSync());
+ m_glHelper.clientWaitSync(sync, GLuint64(-1));
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ QVERIFY(m_glHelper.wasSyncSignaled(sync) == true);
+
+ // WHEN
+ m_glHelper.deleteSync(sync);
+
+ // THEN
+ QCOMPARE(m_func->glIsSync(sync), GL_FALSE);
+ }
+
+ void rasterMode()
+ {
+ if (!m_initializationSuccessful)
+ QSKIP("Initialization failed, OpenGL 4.3 functions not supported");
+
+ m_func->glGetError();
+ m_glHelper.rasterMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ // THEN
+ const GLint error = m_func->glGetError();
+ QVERIFY(error == 0);
+ GLint p;
+ m_func->glGetIntegerv(GL_POLYGON_MODE, &p);
+ QCOMPARE(p, GL_LINE);
+ }
+
+private:
+ QScopedPointer<QWindow> m_window;
+ QOpenGLContext m_glContext;
+ GraphicsHelperGL4 m_glHelper;
+ QOpenGLFunctions_4_3_Core *m_func = nullptr;
+ bool m_initializationSuccessful = false;
+};
+
+#endif
+
+int main(int argc, char *argv[])
+{
+#ifdef TEST_SHOULD_BE_PERFORMED
+ QGuiApplication app(argc, argv);
+ app.setAttribute(Qt::AA_Use96Dpi, true);
+ tst_GraphicsHelperGL4 tc;
+ QTEST_SET_MAIN_SOURCE_PATH
+ return QTest::qExec(&tc, argc, argv);
+#endif
+ return 0;
+}
+
+#ifdef TEST_SHOULD_BE_PERFORMED
+#include "tst_graphicshelpergl4.moc"
+#endif
diff --git a/tests/auto/render/opengl/materialparametergathererjob/materialparametergathererjob.pro b/tests/auto/render/opengl/materialparametergathererjob/materialparametergathererjob.pro
new file mode 100644
index 000000000..106e7a263
--- /dev/null
+++ b/tests/auto/render/opengl/materialparametergathererjob/materialparametergathererjob.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+TARGET = tst_materialparametergathererjob
+
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_materialparametergathererjob.cpp
+
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/materialparametergathererjob/tst_materialparametergathererjob.cpp b/tests/auto/render/opengl/materialparametergathererjob/tst_materialparametergathererjob.cpp
new file mode 100644
index 000000000..efd2c6603
--- /dev/null
+++ b/tests/auto/render/opengl/materialparametergathererjob/tst_materialparametergathererjob.cpp
@@ -0,0 +1,801 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DCore/qentity.h>
+#include <Qt3DCore/qtransform.h>
+#include <Qt3DCore/private/qaspectjobmanager_p.h>
+#include <Qt3DCore/private/qnodevisitor_p.h>
+#include <Qt3DCore/private/qnode_p.h>
+
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/entity_p.h>
+#include <materialparametergathererjob_p.h>
+#include <Qt3DRender/private/techniquefilternode_p.h>
+#include <Qt3DRender/private/technique_p.h>
+#include <Qt3DRender/private/techniquemanager_p.h>
+#include <Qt3DRender/private/renderpassfilternode_p.h>
+#include <Qt3DRender/qrendersettings.h>
+#include <Qt3DRender/qrenderaspect.h>
+#include <Qt3DRender/qeffect.h>
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qparameter.h>
+#include <Qt3DRender/qtechniquefilter.h>
+#include <Qt3DRender/qtechnique.h>
+#include <Qt3DRender/qrenderpassfilter.h>
+#include <Qt3DRender/qrenderpass.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <Qt3DRender/qviewport.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class TestAspect : public Qt3DRender::QRenderAspect
+{
+public:
+ TestAspect(Qt3DCore::QNode *root)
+ : Qt3DRender::QRenderAspect(Qt3DRender::QRenderAspect::Synchronous)
+ , m_jobManager(new Qt3DCore::QAspectJobManager())
+ {
+ Qt3DCore::QAbstractAspectPrivate::get(this)->m_jobManager = m_jobManager.data();
+ QRenderAspect::onRegistered();
+
+ QVector<Qt3DCore::QNode *> nodes;
+ Qt3DCore::QNodeVisitor v;
+ v.traverse(root, [&nodes](Qt3DCore::QNode *node) {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(node);
+ d->m_typeInfo = const_cast<QMetaObject*>(Qt3DCore::QNodePrivate::findStaticMetaObject(node->metaObject()));
+ d->m_hasBackendNode = true;
+ nodes << node;
+ });
+
+ for (const auto node: nodes)
+ d_func()->createBackendNode({
+ node->id(),
+ Qt3DCore::QNodePrivate::get(node)->m_typeInfo,
+ Qt3DCore::NodeTreeChange::Added,
+ node
+ });
+
+ const auto handles = nodeManagers()->techniqueManager()->activeHandles();
+ for (const auto &handle: handles) {
+ Render::Technique *technique = nodeManagers()->techniqueManager()->data(handle);
+ technique->setCompatibleWithRenderer(true);
+ }
+ }
+
+ ~TestAspect()
+ {
+ QRenderAspect::onUnregistered();
+ }
+
+ Qt3DRender::Render::NodeManagers *nodeManagers() const
+ {
+ return d_func()->m_renderer->nodeManagers();
+ }
+
+ void initializeRenderer()
+ {
+ d_func()->m_renderer->initialize();
+ }
+
+ Render::OpenGL::MaterialParameterGathererJobPtr materialGathererJob() const
+ {
+ Render::OpenGL::MaterialParameterGathererJobPtr job = Render::OpenGL::MaterialParameterGathererJobPtr::create();
+ job->setNodeManagers(nodeManagers());
+ return job;
+ }
+
+ void onRegistered() { QRenderAspect::onRegistered(); }
+ void onUnregistered() { QRenderAspect::onUnregistered(); }
+
+private:
+ QScopedPointer<Qt3DCore::QAspectJobManager> m_jobManager;
+};
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+namespace {
+
+class TestMaterial : public Qt3DRender::QMaterial
+{
+ Q_OBJECT
+public:
+ explicit TestMaterial(Qt3DCore::QNode *parent = nullptr)
+ : Qt3DRender::QMaterial(parent)
+ , m_effect(new Qt3DRender::QEffect())
+ , m_gl3Technique(new Qt3DRender::QTechnique())
+ , m_gl2Technique(new Qt3DRender::QTechnique())
+ , m_es2Technique(new Qt3DRender::QTechnique())
+ , m_gl3Pass(new Qt3DRender::QRenderPass())
+ , m_gl2Pass(new Qt3DRender::QRenderPass())
+ , m_es2Pass(new Qt3DRender::QRenderPass())
+ , m_gl3Program(new Qt3DRender::QShaderProgram())
+ , m_gl2es2Program(new Qt3DRender::QShaderProgram())
+ {
+ m_gl3Pass->setShaderProgram(m_gl3Program);
+ m_gl2Pass->setShaderProgram(m_gl2es2Program);
+ m_es2Pass->setShaderProgram(m_gl2es2Program);
+
+ m_gl3Technique->addRenderPass(m_gl3Pass);
+ m_gl2Technique->addRenderPass(m_gl2Pass);
+ m_es2Technique->addRenderPass(m_es2Pass);
+
+ m_effect->addTechnique(m_gl3Technique);
+ m_effect->addTechnique(m_gl2Technique);
+ m_effect->addTechnique(m_es2Technique);
+
+ setEffect(m_effect);
+ }
+
+ Qt3DRender::QTechnique *gl3Technique() const { return m_gl3Technique; }
+ Qt3DRender::QTechnique *gl2Technique() const { return m_gl2Technique; }
+ Qt3DRender::QTechnique *es2Technique() const { return m_es2Technique; }
+
+ Qt3DRender::QRenderPass *gl3Pass() const { return m_gl3Pass; }
+ Qt3DRender::QRenderPass *gl2Pass() const { return m_gl2Pass; }
+ Qt3DRender::QRenderPass *es2Pass() const { return m_es2Pass; }
+
+ Qt3DRender::QShaderProgram *gl3Shader() const { return m_gl3Program; }
+ Qt3DRender::QShaderProgram *gl2shader() const { return m_gl2es2Program; }
+ Qt3DRender::QShaderProgram *es2Shader() const { return m_gl2es2Program; }
+
+private:
+ Qt3DRender::QEffect *m_effect;
+ Qt3DRender::QTechnique *m_gl3Technique;
+ Qt3DRender::QTechnique *m_gl2Technique;
+ Qt3DRender::QTechnique *m_es2Technique;
+ Qt3DRender::QRenderPass *m_gl3Pass;
+ Qt3DRender::QRenderPass *m_gl2Pass;
+ Qt3DRender::QRenderPass *m_es2Pass;
+ Qt3DRender::QShaderProgram *m_gl3Program;
+ Qt3DRender::QShaderProgram *m_gl2es2Program;
+};
+
+Qt3DRender::QViewport *viewportFrameGraph()
+{
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ return viewport;
+}
+
+Qt3DRender::QTechniqueFilter *techniqueFilterFrameGraph()
+{
+ Qt3DRender::QTechniqueFilter *techniqueFilter = new Qt3DRender::QTechniqueFilter();
+ return techniqueFilter;
+}
+
+Qt3DRender::QRenderPassFilter *renderPassFilter()
+{
+ Qt3DRender::QRenderPassFilter *passFilter = new Qt3DRender::QRenderPassFilter();
+ return passFilter;
+}
+
+Qt3DCore::QEntity *buildScene(Qt3DRender::QFrameGraphNode *frameGraph, Qt3DRender::QMaterial *material = nullptr)
+{
+ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
+
+ // FrameGraph
+ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
+ renderSettings->setActiveFrameGraph(frameGraph);
+ root->addComponent(renderSettings);
+
+ // Scene
+ for (int i = 0; i < 10; i++) {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ if (material != nullptr)
+ e->addComponent(material);
+ e->setParent(root);
+ }
+
+ return root;
+}
+
+} // anonymous
+
+class tst_MaterialParameterGatherer : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+
+ void checkRunNoHandlesNoTechniqueFilterNoPassFilter()
+ {
+ // GIVEN
+ Qt3DCore::QEntity *sceneRoot = buildScene(viewportFrameGraph());
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 0);
+
+ // WHEN
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 0);
+ }
+
+ void checkRunSelectAllNoTechniqueFilterNoPassFilter()
+ {
+ // GIVEN
+ TestMaterial material;
+ Qt3DCore::QEntity *sceneRoot = buildScene(viewportFrameGraph(), &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+ }
+
+ void checkRunDisabledMaterial()
+ {
+ // GIVEN
+ TestMaterial material;
+ material.setEnabled(false);
+ Qt3DCore::QEntity *sceneRoot = buildScene(viewportFrameGraph(), &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 0);
+ }
+
+ void checkRunSelectAllTechniqueFilterWithNoFilterNoPassFilter()
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *frameGraphFilter = techniqueFilterFrameGraph();
+
+ TestMaterial material;
+
+ Qt3DRender::QFilterKey techniqueFilterKey;
+ techniqueFilterKey.setName(QStringLiteral("renderingStyle"));
+ techniqueFilterKey.setValue(QVariant(QStringLiteral("backward")));
+
+ material.gl2Technique()->addFilterKey(&techniqueFilterKey);
+ material.gl3Technique()->addFilterKey(&techniqueFilterKey);
+ material.es2Technique()->addFilterKey(&techniqueFilterKey);
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(frameGraphFilter, &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(frameGraphFilter->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+ }
+
+ void checkRunSelectAllTechniqueFilterWithIncompatibleFilterNoPassFilter()
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *frameGraphFilter = techniqueFilterFrameGraph();
+ TestMaterial material;
+
+ Qt3DRender::QFilterKey techniqueFilterFilterKey;
+ techniqueFilterFilterKey.setName(QStringLiteral("renderingStyle"));
+ techniqueFilterFilterKey.setValue(QVariant(QStringLiteral("forward")));
+
+ Qt3DRender::QFilterKey techniqueFilterKey;
+ techniqueFilterKey.setName(QStringLiteral("renderingStyle"));
+ techniqueFilterKey.setValue(QVariant(QStringLiteral("backward")));
+
+ frameGraphFilter->addMatch(&techniqueFilterFilterKey);
+
+ material.gl2Technique()->addFilterKey(&techniqueFilterKey);
+ material.gl3Technique()->addFilterKey(&techniqueFilterKey);
+ material.es2Technique()->addFilterKey(&techniqueFilterKey);
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(frameGraphFilter, &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(frameGraphFilter->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 0);
+ }
+
+ void checkRunSelectAllTechniqueFilterWithCompatibleFilterNoPassFilter()
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *frameGraphFilter = techniqueFilterFrameGraph();
+ TestMaterial material;
+
+ Qt3DRender::QFilterKey techniqueFilterKey;
+ techniqueFilterKey.setName(QStringLiteral("renderingStyle"));
+ techniqueFilterKey.setValue(QVariant(QStringLiteral("backward")));
+
+ frameGraphFilter->addMatch(&techniqueFilterKey);
+
+ material.gl2Technique()->addFilterKey(&techniqueFilterKey);
+ material.gl3Technique()->addFilterKey(&techniqueFilterKey);
+ material.es2Technique()->addFilterKey(&techniqueFilterKey);
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(frameGraphFilter, &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(frameGraphFilter->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+ }
+
+ void checkRunSelectAllNoTechniqueFilterPassFilterWithNoFilter()
+ {
+ // GIVEN
+ Qt3DRender::QRenderPassFilter *frameGraphFilter = renderPassFilter();
+ TestMaterial material;
+
+ Qt3DRender::QFilterKey passFilterKey;
+ passFilterKey.setName(QStringLiteral("renderingStyle"));
+ passFilterKey.setValue(QVariant(QStringLiteral("backward")));
+
+ material.gl3Pass()->addFilterKey(&passFilterKey);
+ material.gl2Pass()->addFilterKey(&passFilterKey);
+ material.es2Pass()->addFilterKey(&passFilterKey);
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(frameGraphFilter, &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::RenderPassFilter *backendPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(frameGraphFilter->id()));
+ QVERIFY(backendPassFilter != nullptr);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendPassFilter);
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+ }
+
+ void checkRunSelectAllNoTechniqueFilterPassFilterWithIncompatibleFilter()
+ {
+ // GIVEN
+ Qt3DRender::QRenderPassFilter *frameGraphFilter = renderPassFilter();
+ TestMaterial material;
+
+ Qt3DRender::QFilterKey passFilterFilterKey;
+ passFilterFilterKey.setName(QStringLiteral("renderingStyle"));
+ passFilterFilterKey.setValue(QVariant(QStringLiteral("forward")));
+
+ Qt3DRender::QFilterKey passFilterKey;
+ passFilterKey.setName(QStringLiteral("renderingStyle"));
+ passFilterKey.setValue(QVariant(QStringLiteral("backward")));
+
+ frameGraphFilter->addMatch(&passFilterFilterKey);
+
+ material.gl3Pass()->addFilterKey(&passFilterKey);
+ material.gl2Pass()->addFilterKey(&passFilterKey);
+ material.es2Pass()->addFilterKey(&passFilterKey);
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(frameGraphFilter, &material);
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+
+ testAspect.initializeRenderer();
+
+ // THEN
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::RenderPassFilter *backendPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(frameGraphFilter->id()));
+ QVERIFY(backendPassFilter != nullptr);
+
+ // WHEN
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendPassFilter);
+ gatherer->run();
+
+ // THEN
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 0);
+ }
+
+ void checkParameterPriorityGathering()
+ {
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ techniqueFilterFG->addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::red))));
+
+ auto renderPassParameter = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::cyan)));
+
+ renderPassFG->addParameter(renderPassParameter);
+ material.addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::green))));
+ material.effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::blue))));
+
+ auto techniqueParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::white)));
+
+ material.gl3Technique()->addParameter(techniqueParam);
+ material.gl2Technique()->addParameter(techniqueParam);
+ material.es2Technique()->addParameter(techniqueParam);
+
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> RenderPassFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(renderPassParameter->id()));
+ }
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ auto techniqueFilterParameter = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::cyan)));
+ techniqueFilterFG->addParameter(techniqueFilterParameter);
+
+ material.addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::green))));
+ material.effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::blue))));
+
+ auto techniqueParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::white)));
+
+ material.gl3Technique()->addParameter(techniqueParam);
+ material.gl2Technique()->addParameter(techniqueParam);
+ material.es2Technique()->addParameter(techniqueParam);
+
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> TechniqueFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(techniqueFilterParameter->id()));
+ }
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ auto materialParameter = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::cyan)));
+
+ material.addParameter(materialParameter);
+ material.effect()->addParameter(new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::blue))));
+
+ auto techniqueParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::white)));
+
+ material.gl3Technique()->addParameter(techniqueParam);
+ material.gl2Technique()->addParameter(techniqueParam);
+ material.es2Technique()->addParameter(techniqueParam);
+
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> TechniqueFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(materialParameter->id()));
+ }
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ auto effectParameter = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::cyan)));
+
+ material.effect()->addParameter(effectParameter);
+
+ auto techniqueParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::white)));
+
+ material.gl3Technique()->addParameter(techniqueParam);
+ material.gl2Technique()->addParameter(techniqueParam);
+ material.es2Technique()->addParameter(techniqueParam);
+
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> TechniqueFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(effectParameter->id()));
+ }
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ auto techniqueParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::white)));
+
+ material.gl3Technique()->addParameter(techniqueParam);
+ material.gl2Technique()->addParameter(techniqueParam);
+ material.es2Technique()->addParameter(techniqueParam);
+
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> TechniqueFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(techniqueParam->id()));
+ }
+ {
+ // GIVEN
+ Qt3DRender::QTechniqueFilter *techniqueFilterFG = techniqueFilterFrameGraph();
+ Qt3DRender::QRenderPassFilter *renderPassFG = renderPassFilter();
+
+ renderPassFG->setParent(techniqueFilterFG);
+
+ TestMaterial material;
+
+ Qt3DCore::QEntity *sceneRoot = buildScene(techniqueFilterFG, &material);
+
+ // WHEN
+ auto passParam = new Qt3DRender::QParameter(QStringLiteral("color"), QVariant(QColor(Qt::gray)));
+
+ material.gl3Pass()->addParameter(passParam);
+ material.gl2Pass()->addParameter(passParam);
+ material.es2Pass()->addParameter(passParam);
+
+ Qt3DRender::TestAspect testAspect(sceneRoot);
+ Qt3DRender::Render::OpenGL::MaterialParameterGathererJobPtr gatherer = testAspect.materialGathererJob();
+ testAspect.initializeRenderer();
+
+ QCOMPARE(testAspect.nodeManagers()->materialManager()->activeHandles().size(), 1);
+ Qt3DRender::Render::TechniqueFilter *backendTechniqueFilter = static_cast<Qt3DRender::Render::TechniqueFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(techniqueFilterFG->id()));
+ Qt3DRender::Render::RenderPassFilter *backendRenderPassFilter = static_cast<Qt3DRender::Render::RenderPassFilter *>(testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFG->id()));
+ QVERIFY(backendTechniqueFilter != nullptr);
+ QVERIFY(backendRenderPassFilter != nullptr);
+
+ gatherer->setHandles(testAspect.nodeManagers()->materialManager()->activeHandles());
+ gatherer->setRenderPassFilter(backendRenderPassFilter);
+ gatherer->setTechniqueFilter(backendTechniqueFilter);
+ gatherer->run();
+
+ // THEN -> TechniqueFilter wins
+ QCOMPARE(gatherer->materialToPassAndParameter().size(), 1);
+
+ const QVector<Qt3DRender::Render::OpenGL::RenderPassParameterData> passParameterData = gatherer->materialToPassAndParameter().begin().value();
+ QCOMPARE(passParameterData.size(), 1);
+
+ const Qt3DRender::Render::OpenGL::RenderPassParameterData data = passParameterData.first();
+
+ QCOMPARE(data.parameterInfo.size(), 1);
+ QCOMPARE(data.parameterInfo.first().handle, testAspect.nodeManagers()->parameterManager()->lookupHandle(passParam->id()));
+ }
+ }
+};
+
+QTEST_MAIN(tst_MaterialParameterGatherer)
+
+#include "tst_materialparametergathererjob.moc"
diff --git a/tests/auto/render/opengl/opengl.pro b/tests/auto/render/opengl/opengl.pro
new file mode 100644
index 000000000..5299ebd36
--- /dev/null
+++ b/tests/auto/render/opengl/opengl.pro
@@ -0,0 +1,19 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ filtercompatibletechniquejob \
+ graphicshelpergl3_3 \
+ graphicshelpergl3_2 \
+ graphicshelpergl2 \
+ glshadermanager \
+ materialparametergathererjob \
+ textures \
+ renderer \
+ renderviewutils \
+ renderviews \
+ renderqueue \
+ renderviewbuilder \
+ qgraphicsutils \
+ computecommand
+
+!macos: SUBDIRS += graphicshelpergl4
diff --git a/tests/auto/render/opengl/opengl_render_plugin.pri b/tests/auto/render/opengl/opengl_render_plugin.pri
new file mode 100644
index 000000000..50fade878
--- /dev/null
+++ b/tests/auto/render/opengl/opengl_render_plugin.pri
@@ -0,0 +1,18 @@
+# Found no way of having the test runner include the correct
+# library path as runtime
+
+#PLUGIN_SRC_PATH = $$PWD/../../../../src/plugins/renderers/opengl
+
+#INCLUDEPATH += \
+# $$PLUGIN_SRC_PATH/graphicshelpers \
+# $$PLUGIN_SRC_PATH/io \
+# $$PLUGIN_SRC_PATH/jobs \
+# $$PLUGIN_SRC_PATH/managers \
+# $$PLUGIN_SRC_PATH/renderer \
+# $$PLUGIN_SRC_PATH/renderstates \
+# $$PLUGIN_SRC_PATH/textures
+
+#LIBS += -L$$[QT_INSTALL_PLUGINS]/renderers/ -lopenglrenderer
+
+# Instead we link against the sources directly
+include(../../../../src/plugins/renderers/opengl/opengl.pri)
diff --git a/tests/auto/render/opengl/qgraphicsutils/qgraphicsutils.pro b/tests/auto/render/opengl/qgraphicsutils/qgraphicsutils.pro
new file mode 100644
index 000000000..8c936c527
--- /dev/null
+++ b/tests/auto/render/opengl/qgraphicsutils/qgraphicsutils.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_qgraphicsutils
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_qgraphicsutils.cpp
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/qgraphicsutils/tst_qgraphicsutils.cpp b/tests/auto/render/opengl/qgraphicsutils/tst_qgraphicsutils.cpp
new file mode 100644
index 000000000..bd4772045
--- /dev/null
+++ b/tests/auto/render/opengl/qgraphicsutils/tst_qgraphicsutils.cpp
@@ -0,0 +1,355 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <qgraphicsutils_p.h>
+
+class tst_QGraphicsUtils : public QObject
+{
+ Q_OBJECT
+private slots:
+ void fillScalarInDataArray();
+ void fillArray();
+ void fillScalarWithOffsets();
+ void fillMatrix4x4();
+ void fillMatrix3x4();
+ void fillMatrix4x3();
+ void fillMatrixArray();
+};
+
+void tst_QGraphicsUtils::fillScalarInDataArray()
+{
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 1;
+ description.m_offset = 0;
+ description.m_arrayStride = 10;
+
+ QVector4D testVector(8.0f, 8.0f, 3.0f, 1.0f);
+ const GLfloat *vectorData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(testVector, 1, 4);
+
+ for (int i = 0; i < 4; i++) {
+ if (i == 0)
+ QVERIFY(vectorData[i] == testVector.x());
+ else if (i == 1)
+ QVERIFY(vectorData[i] == testVector.y());
+ else if (i == 2)
+ QVERIFY(vectorData[i] == testVector.z());
+ else if (i == 3)
+ QVERIFY(vectorData[i] == testVector.w());
+ }
+
+ QByteArray data(description.m_size * 4 * sizeof(GLfloat), 0);
+ char *innerData = data.data();
+
+ // Checked that we are not overflowing
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData, vectorData, description, 2);
+ for (int i = 0; i < 4; ++i) {
+ if (i < 2)
+ QVERIFY(vectorData[i] == ((GLfloat*)innerData)[i]);
+ else
+ QVERIFY(((GLfloat*)innerData)[i] == 0.0f);
+ }
+
+ // Check that all values are copied
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData, vectorData, description, 4);
+ for (int i = 0; i < 4; ++i)
+ QVERIFY(vectorData[i] == ((GLfloat*)innerData)[i]);
+
+ // check that offsetting works
+ description.m_offset = 16;
+ data = QByteArray(description.m_size * 8 * sizeof(GLfloat), 0);
+ innerData = data.data();
+
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData, vectorData, description, 4);
+ for (int i = 0; i < 8; ++i) {
+ if (i < 4)
+ QVERIFY(((GLfloat*)innerData)[i] == 0.0f);
+ else
+ QVERIFY(vectorData[i - 4] == ((GLfloat*)innerData)[i]);
+ }
+}
+
+void tst_QGraphicsUtils::fillArray()
+{
+ QVector4D testVector(8.0f, 8.0f, 3.0f, 1.0f);
+ QVector4D testVector2(3.0f, 5.0f, 0.0f, 7.0f);
+ QVector4D testVector3(4.0f, 5.0f, 4.0f, 2.0f);
+
+ QVariantList variantList = QVariantList() << testVector << testVector2 << testVector3;
+ const GLfloat *vectorData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(QVariant(variantList), 3, 4);
+
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 3;
+ description.m_offset = 16;
+ description.m_arrayStride = 16;
+
+ QByteArray data(description.m_size * (4 + description.m_arrayStride) * sizeof(GLfloat) + description.m_offset, 0);
+ char *innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData, vectorData, description, 4);
+
+ int offset = description.m_offset / sizeof(GLfloat);
+ int stride = description.m_arrayStride / sizeof(GLfloat);
+
+ GLfloat *innerDataFloat = (GLfloat*)innerData;
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ int idx = i * 4 + j;
+ QVERIFY(innerDataFloat[offset + j] == vectorData[idx]);
+ }
+ offset += stride;
+ }
+}
+
+void tst_QGraphicsUtils::fillScalarWithOffsets()
+{
+ // Simulates Uniform Block
+
+ // uniform Block {
+ // vec3 position; // Offset 0 - 12 bytes
+ // vec3 direction; // Offset 16 - 12 bytes
+ // vec4 color; // Offset 32 - 16 bytes
+ // float intensity; // Offset 48 - bytes
+ // } // total size 64 bytes
+
+ QVector3D position(8.0f, 8.0f, 3.0f);
+ QVector3D direction(3.0f, 5.0f, 2.0f);
+ QVector4D color(4.0f, 5.0f, 4.0f, 1.0f);
+ float intensity = 1.0f;
+
+ Qt3DRender::Render::OpenGL::ShaderUniform posUniform;
+ posUniform.m_size = 1;
+ posUniform.m_arrayStride = 0;
+ posUniform.m_matrixStride = 0;
+ posUniform.m_offset = 0;
+
+ Qt3DRender::Render::OpenGL::ShaderUniform dirUniform;
+ dirUniform.m_size = 1;
+ dirUniform.m_arrayStride = 0;
+ dirUniform.m_matrixStride = 0;
+ dirUniform.m_offset = 16;
+
+ Qt3DRender::Render::OpenGL::ShaderUniform colUniform;
+ colUniform.m_size = 1;
+ colUniform.m_arrayStride = 0;
+ colUniform.m_matrixStride = 0;
+ colUniform.m_offset = 32;
+
+ Qt3DRender::Render::OpenGL::ShaderUniform intUniform;
+ intUniform.m_size = 1;
+ intUniform.m_arrayStride = 0;
+ intUniform.m_matrixStride = 0;
+ intUniform.m_offset = 48;
+
+ QVector<GLfloat> data(16);
+ void *innerData = data.data();
+
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData,
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(position, 1, 3),
+ posUniform, 3);
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData,
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(direction, 1, 3),
+ dirUniform, 3);
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData,
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(color, 1, 4),
+ colUniform, 4);
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataArray(innerData,
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(intensity, 1, 1),
+ intUniform, 1);
+
+ GLfloat *floatData = (GLfloat*)innerData;
+
+ // Check first 16 bytes - position
+ QVERIFY(floatData[0] == position.x());
+ QVERIFY(floatData[1] == position.y());
+ QVERIFY(floatData[2] == position.z());
+ QVERIFY(floatData[3] == 0.0f);
+ // Check 16 - 32 bytes - direction
+ QVERIFY(floatData[4] == direction.x());
+ QVERIFY(floatData[5] == direction.y());
+ QVERIFY(floatData[6] == direction.z());
+ QVERIFY(floatData[7] == 0.0f);
+ // Check 32 - 48 bytes - color
+ QVERIFY(floatData[8] == color.x());
+ QVERIFY(floatData[9] == color.y());
+ QVERIFY(floatData[10] == color.z());
+ QVERIFY(floatData[11] == color.w());
+ // Check 48 - 64 bytes - intensity
+ QVERIFY(floatData[12] == intensity);
+ QVERIFY(floatData[13] == 0.0f);
+ QVERIFY(floatData[14] == 0.0f);
+ QVERIFY(floatData[15] == 0.0f);
+}
+
+void tst_QGraphicsUtils::fillMatrix4x4()
+{
+ // row major
+ QMatrix4x4 mat(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f);
+
+ // column major
+ const GLfloat *matData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(mat, 1, 16);
+
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 1;
+ description.m_offset = 0;
+ description.m_arrayStride = 0;
+ description.m_matrixStride = 16;
+
+
+ QByteArray data(description.m_size * 16 * sizeof(GLfloat), 0);
+ char *innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 4, 4);
+ // Check for no offset/no stride
+ for (int i = 0; i < 16; ++i)
+ QVERIFY((((GLfloat *)innerData)[i]) == matData[i]);
+
+ description.m_offset = 12;
+ data = QByteArray((description.m_size * 16 + description.m_offset) * sizeof(GLfloat), 0);
+ innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 4, 4);
+ // Check with 12 offset/no stride
+ for (int i = 0; i < 16; ++i)
+ QVERIFY((((GLfloat *)innerData)[3 + i]) == matData[i]);
+
+ description.m_matrixStride = 16;
+ data = QByteArray((description.m_size * 16 + 4 * description.m_matrixStride + description.m_offset) * sizeof(GLfloat), 0);
+ innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 4, 4);
+ // Check with 10 offset/ 16 stride
+ int offset = description.m_offset / sizeof(GLfloat);
+ int matrixStride = description.m_matrixStride / sizeof(GLfloat);
+
+ for (int col = 0; col < 4; ++col) {
+ for (int row = 0; row < 4; ++row)
+ QVERIFY((((GLfloat *)innerData)[offset + row]) == matData[col * 4 + row]);
+ offset += matrixStride;
+ }
+}
+
+void tst_QGraphicsUtils::fillMatrix3x4()
+{
+ QMatrix3x4 mat;
+
+ mat.fill(6.0f);
+ const GLfloat *matData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(QVariant::fromValue(mat), 1, 12);
+
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 1;
+ description.m_offset = 16;
+ description.m_arrayStride = 0;
+ description.m_matrixStride = 12;
+
+ QByteArray data((description.m_size * 12 + 3 * description.m_matrixStride + description.m_offset) * sizeof(GLfloat), 0);
+ char *innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 3, 4);
+ // Check with 16 offset/ 12 stride
+ int offset = description.m_offset / sizeof(GLfloat);
+ int matrixStride = description.m_matrixStride / sizeof(GLfloat);
+
+ for (int col = 0; col < 3; ++col) {
+ for (int row = 0; row < 4; ++row)
+ QVERIFY((((GLfloat *)innerData)[offset + row]) == matData[col * 4 + row]);
+ offset += matrixStride;
+ }
+}
+
+void tst_QGraphicsUtils::fillMatrix4x3()
+{
+ QMatrix4x3 mat;
+
+ mat.fill(6.0f);
+ const GLfloat *matData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(QVariant::fromValue(mat), 1, 12);
+
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 1;
+ description.m_offset = 16;
+ description.m_arrayStride = 0;
+ description.m_matrixStride = 16;
+
+ QByteArray data((description.m_size * 12 + 4 * description.m_matrixStride + description.m_offset) * sizeof(GLfloat), 0);
+ char *innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 4, 3);
+ // Check with 16 offset/ 16 stride
+ int offset = description.m_offset / sizeof(GLfloat);
+ int matrixStride = description.m_matrixStride / sizeof(GLfloat);
+
+ for (int col = 0; col < 4; ++col) {
+ for (int row = 0; row < 3; ++row)
+ QVERIFY((((GLfloat *)innerData)[offset + row]) == matData[col * 3 + row]);
+ offset += matrixStride;
+ }
+}
+
+void tst_QGraphicsUtils::fillMatrixArray()
+{
+ QMatrix4x3 mat1;
+ QMatrix4x3 mat2;
+ QMatrix4x3 mat3;
+ mat1.fill(6.0f);
+ mat2.fill(2.0f);
+ mat3.fill(7.0f);
+
+ QVariantList matrices = QVariantList() << QVariant::fromValue(mat1) << QVariant::fromValue(mat2) << QVariant::fromValue(mat3);
+
+ const GLfloat *matData = Qt3DRender::Render::OpenGL::QGraphicsUtils::valueArrayFromVariant<GLfloat>(QVariant::fromValue(matrices), 3, 12);
+
+ Qt3DRender::Render::OpenGL::ShaderUniform description;
+
+ description.m_size = 3;
+ description.m_offset = 12;
+ description.m_arrayStride = 4;
+ description.m_matrixStride = 16;
+
+ QByteArray data((description.m_size * (12 + 4 * description.m_matrixStride + description.m_arrayStride) + description.m_offset) * sizeof(GLfloat), 0);
+ char *innerData = data.data();
+ Qt3DRender::Render::OpenGL::QGraphicsUtils::fillDataMatrixArray(innerData, matData, description, 4, 3);
+ // Check with 12 offset/ 4 array stride / 16 matrix stride
+ int offset = description.m_offset / sizeof(GLfloat);
+ int matrixStride = description.m_matrixStride / sizeof(GLfloat);
+ int arrayStride = description.m_arrayStride / sizeof(GLfloat);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int col = 0; col < 4; ++col) {
+ for (int row = 0; row < 3; ++row) {
+ int idx = i * 4 * 3 + col * 3 + row;
+ QVERIFY((((GLfloat *)innerData)[offset + row]) == matData[idx]);
+ }
+ offset += matrixStride;
+ }
+ offset += arrayStride;
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_QGraphicsUtils)
+
+#include "tst_qgraphicsutils.moc"
diff --git a/tests/auto/render/opengl/renderer/renderer.pro b/tests/auto/render/opengl/renderer/renderer.pro
new file mode 100644
index 000000000..d481c7b9e
--- /dev/null
+++ b/tests/auto/render/opengl/renderer/renderer.pro
@@ -0,0 +1,12 @@
+TEMPLATE = app
+
+TARGET = tst_renderer
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_renderer.cpp
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/renderer/tst_renderer.cpp b/tests/auto/render/opengl/renderer/tst_renderer.cpp
new file mode 100644
index 000000000..db7a37af1
--- /dev/null
+++ b/tests/auto/render/opengl/renderer/tst_renderer.cpp
@@ -0,0 +1,348 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QThread>
+#include <Qt3DRender/private/viewportnode_p.h>
+#include <renderer_p.h>
+#include <renderview_p.h>
+#include <renderviewbuilder_p.h>
+#include <Qt3DRender/private/offscreensurfacehelper_p.h>
+#include <renderqueue_p.h>
+
+class tst_Renderer : public QObject
+{
+ Q_OBJECT
+public :
+ tst_Renderer() {}
+ ~tst_Renderer() {}
+
+private Q_SLOTS:
+
+ void checkPreRenderBinJobs()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer);
+ Qt3DRender::Render::RenderSettings settings;
+ // owned by FG manager
+ Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode();
+ const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId();
+
+ nodeManagers.frameGraphManager()->appendNode(fgRootId, fgRoot);
+ settings.setActiveFrameGraphId(fgRootId);
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderer.setSettings(&settings);
+ renderer.setOffscreenSurfaceHelper(&offscreenHelper);
+ renderer.initialize();
+
+ // Ensure invoke calls are performed
+ QCoreApplication::processEvents();
+
+ // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt)
+ QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1); // RayCastingJob
+
+ // WHEN
+ renderer.m_sendBufferCaptureJob->addRequest({Qt3DCore::QNodeId(), {}});
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1); // SendBufferCaptureJob
+ // Note: pending render buffer captures are only cleared when the job is run
+
+ // WHEN
+ renderer.m_updatedSetFences.push_back({Qt3DCore::QNodeId(), nullptr});
+ jobs = renderer.preRenderingJobs();
+
+ // THEN
+ QCOMPARE(jobs.size(),
+ 1 + // PickBoundingVolumeJob
+ 1 + // RayCastingJob
+ 1 + // SendBufferCaptureJob
+ 1); // SendSetFenceHandlesJob
+ // Note: pending set fence handles are only cleared when the job is run
+
+ // Properly shutdown command thread
+ renderer.shutdown();
+ }
+
+ void checkRenderBinJobs()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::OpenGL::RenderQueue *renderQueue = renderer.renderQueue();
+ Qt3DRender::Render::OffscreenSurfaceHelper offscreenHelper(&renderer);
+ Qt3DRender::Render::RenderSettings settings;
+ // owned by FG manager
+ Qt3DRender::Render::ViewportNode *fgRoot = new Qt3DRender::Render::ViewportNode();
+ const Qt3DCore::QNodeId fgRootId = Qt3DCore::QNodeId::createId();
+
+ nodeManagers.frameGraphManager()->appendNode(fgRootId, fgRoot);
+ settings.setActiveFrameGraphId(fgRootId);
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderer.setSettings(&settings);
+ renderer.setOffscreenSurfaceHelper(&offscreenHelper);
+ renderer.initialize();
+
+ // Ensure invoke calls are performed
+ QCoreApplication::processEvents();
+
+ // NOTE: FilterCompatibleTechniqueJob and ShaderGathererJob cannot run because the context
+ // is not initialized in this test
+
+ const int renderViewBuilderMaterialCacheJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+ // syncMaterialGathererJob
+ // n * materialGathererJob
+ const int layerCacheJobCount = 2;
+ // filterEntityByLayerJob,
+ // syncFilterEntityByLayerJob
+
+ const int singleRenderViewCommandRebuildJobCount = 1 + Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+
+ const int singleRenderViewJobCount = 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount();
+ // RenderViewBuilder renderViewJob,
+ // syncRenderViewInitializationJob,
+ // syncFrustumCullingJob,
+ // filterProximityJob,
+ // setClearDrawBufferIndexJob,
+ // frustumCullingJob,
+ // syncRenderCommandUpdateJob,
+ // syncRenderViewCommandPostUpdateJob
+ // n * (RenderViewCommandBuildJobs)
+
+ // WHEN
+ QVector<Qt3DCore::QAspectJobPtr> jobs = renderer.renderBinJobs();
+
+ // THEN -> AllDirty
+ // (Renderer is not initialized so FilterCompatibleTechniqueJob
+ // and ShaderGathererJob are not added here)
+ QCOMPARE(jobs.size(),
+ 1 + // EntityEnabledDirty
+ 1 + // WorldTransformJob
+ 1 + // UpdateWorldBoundingVolume
+ 1 + // UpdateShaderDataTransform
+ 1 + // ExpandBoundingVolumeJob
+ 1 + // CalculateBoundingVolumeJob
+ 1 + // UpdateMeshTriangleListJob
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // BufferGathererJob
+ 1 + // TexturesGathererJob
+ 1 + // UpdateEntityLayersJob
+ 1 + // LightGathererJob
+ 1 + // CacheLightJob
+ 1 + // RenderableEntityFilterJob
+ 1 + // CacheRenderableEntitiesJob
+ 1 + // ComputableEntityFilterJob
+ 1 + // CacheComputableEntitiesJob
+ singleRenderViewJobCount +
+ singleRenderViewCommandRebuildJobCount +
+ layerCacheJobCount +
+ renderViewBuilderMaterialCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt) (Nothing in cache)
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ singleRenderViewJobCount +
+ singleRenderViewCommandRebuildJobCount +
+ renderViewBuilderMaterialCacheJobCount +
+ layerCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN (nothing dirty, no buffers, no layers to be rebuilt, no materials to be rebuilt) (RV leaf in cache)
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::FrameGraphDirty, nullptr);
+ renderer.cache()->leafNodeCache[renderer.m_frameGraphLeaves.first()] = {};
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ singleRenderViewJobCount +
+ singleRenderViewCommandRebuildJobCount +
+ renderViewBuilderMaterialCacheJobCount +
+ layerCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::EntityEnabledDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ 1 + // EntityEnabledDirty
+ singleRenderViewJobCount +
+ layerCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TransformDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // WorldTransformJob
+ 1 + // UpdateWorldBoundingVolume
+ 1 + // UpdateShaderDataTransform
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ 1 + // ExpandBoundingVolumeJob
+ singleRenderViewJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::MaterialDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ singleRenderViewJobCount +
+ singleRenderViewCommandRebuildJobCount +
+ renderViewBuilderMaterialCacheJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::GeometryDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // CalculateBoundingVolumeJob
+ 1 + // UpdateMeshTriangleListJob
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ 1 + // ExpandBoundingVolumeJob
+ 1 + // RenderableEntityFilterPtr
+ 1 + // SyncRenderableEntities
+ singleRenderViewCommandRebuildJobCount +
+ singleRenderViewJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::BuffersDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncLoadingJobs
+ 1 + // CalculateBoundingVolumeJob
+ 1 + // UpdateMeshTriangleListJob
+ 1 + // BufferGathererJob
+ singleRenderViewJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // WHEN
+ renderer.markDirty(Qt3DRender::Render::AbstractRenderer::TexturesDirty, nullptr);
+ jobs = renderer.renderBinJobs();
+
+ // THEN (level
+ QCOMPARE(jobs.size(),
+ 1 + // updateLevelOfDetailJob
+ 1 + // cleanupJob
+ 1 + // VAOGatherer
+ 1 + // TexturesGathererJob
+ 1 + // updateSkinningPaletteJob
+ 1 + // SyncTexturesGathererJob
+ singleRenderViewJobCount);
+
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderQueue->reset();
+
+ // Properly shutdown command thread
+ renderer.shutdown();
+ }
+};
+
+QTEST_MAIN(tst_Renderer)
+
+#include "tst_renderer.moc"
diff --git a/tests/auto/render/opengl/renderqueue/renderqueue.pro b/tests/auto/render/opengl/renderqueue/renderqueue.pro
new file mode 100644
index 000000000..19106ba92
--- /dev/null
+++ b/tests/auto/render/opengl/renderqueue/renderqueue.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+
+TARGET = tst_renderqueue
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_renderqueue.cpp
+
+include(../../../core/common/common.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/renderqueue/tst_renderqueue.cpp b/tests/auto/render/opengl/renderqueue/tst_renderqueue.cpp
new file mode 100644
index 000000000..728648a3c
--- /dev/null
+++ b/tests/auto/render/opengl/renderqueue/tst_renderqueue.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QThread>
+#include <renderqueue_p.h>
+#include <renderview_p.h>
+
+class tst_RenderQueue : public QObject
+{
+ Q_OBJECT
+public :
+ tst_RenderQueue() {}
+ ~tst_RenderQueue() {}
+
+private Q_SLOTS:
+ void setRenderViewCount();
+ void circleQueues();
+ void checkOrder();
+ void checkTimeToSubmit();
+ void concurrentQueueAccess();
+ void resetQueue();
+};
+
+
+void tst_RenderQueue::setRenderViewCount()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue renderQueue;
+
+ // THEN
+ QCOMPARE(renderQueue.wasReset(), true);
+
+ // WHEN
+ renderQueue.setTargetRenderViewCount(7);
+
+ // THEN
+ QCOMPARE(renderQueue.wasReset(), false);
+ QVERIFY(renderQueue.targetRenderViewCount() == 7);
+ QVERIFY(renderQueue.currentRenderViewCount()== 0);
+}
+
+void tst_RenderQueue::circleQueues()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue renderQueue;
+ renderQueue.setTargetRenderViewCount(7);
+
+ // WHEN
+ for (int j = 0; j < 10; j++) {
+
+ // WHEN
+ renderQueue.reset();
+ renderQueue.setTargetRenderViewCount(7);
+
+ // THEN
+ QVERIFY(!renderQueue.isFrameQueueComplete());
+ QCOMPARE(renderQueue.currentRenderViewCount(), 0);
+
+ // WHEN
+ QList<Qt3DRender::Render::OpenGL::RenderView *> renderViews;
+ for (int i = 0; i < 7; i++) {
+ renderViews << new Qt3DRender::Render::OpenGL::RenderView();
+ renderQueue.queueRenderView(renderViews.at(i), i);
+ }
+
+ // THEN
+ QVERIFY(renderQueue.isFrameQueueComplete());
+ }
+}
+
+void tst_RenderQueue::checkOrder()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue renderQueue;
+ renderQueue.setTargetRenderViewCount(7);
+ QVector<Qt3DRender::Render::OpenGL::RenderView *> renderViews(7);
+
+ // WHEN
+ for (int i = 0; i < 7; ++i) {
+ int processingOrder = (i % 2 == 0) ? (6 - i) : i;
+ renderViews[processingOrder] = new Qt3DRender::Render::OpenGL::RenderView();
+ renderQueue.queueRenderView(renderViews[processingOrder], processingOrder);
+ }
+
+ // THEN
+ QVector<Qt3DRender::Render::OpenGL::RenderView *> frame = renderQueue.nextFrameQueue();
+ for (int i = 0; i < 7; ++i) {
+ QVERIFY(frame[i] == renderViews[i]);
+ }
+}
+
+void tst_RenderQueue::checkTimeToSubmit()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue renderQueue;
+ renderQueue.setTargetRenderViewCount(7);
+ QVector<Qt3DRender::Render::OpenGL::RenderView *> renderViews(7);
+
+ // WHEN
+ for (int i = 0; i < 7; i++) {
+ int processingOrder = (i % 2 == 0) ? (6 - i) : i;
+ renderViews[processingOrder] = new Qt3DRender::Render::OpenGL::RenderView();
+ renderQueue.queueRenderView(renderViews[processingOrder], processingOrder);
+
+ // THEN
+ if (i < 6)
+ QVERIFY(!renderQueue.isFrameQueueComplete());
+ else
+ QVERIFY(renderQueue.isFrameQueueComplete());
+ }
+}
+
+class SimpleWorker : public QThread
+{
+ Q_OBJECT
+public:
+ QSemaphore m_waitSubmit;
+ QSemaphore m_waitQueue;
+ Qt3DRender::Render::OpenGL::RenderQueue *m_renderQueues;
+
+public Q_SLOTS:
+
+ void run() final // In Thread
+ {
+ for (int i = 0; i < 5; i++) {
+ m_waitQueue.acquire();
+
+ QVERIFY(m_renderQueues->currentRenderViewCount() == 0);
+ QVERIFY(!m_renderQueues->isFrameQueueComplete());
+
+ for (int j = 0; j < 7; ++j) {
+ // THEN
+ QCOMPARE(m_renderQueues->currentRenderViewCount(), j);
+ // WHEN
+ m_renderQueues->queueRenderView(new Qt3DRender::Render::OpenGL::RenderView(), j);
+ // THEN
+ QVERIFY(m_renderQueues->targetRenderViewCount() == 7);
+ QCOMPARE(m_renderQueues->currentRenderViewCount(), j + 1);
+ QVERIFY(m_renderQueues->isFrameQueueComplete() == (j == 6));
+ QThread::msleep(20); // Simulates business
+ }
+
+ QVERIFY(m_renderQueues->isFrameQueueComplete());
+ m_waitSubmit.release();
+ }
+ }
+};
+
+
+void tst_RenderQueue::concurrentQueueAccess()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue *renderQueue = new Qt3DRender::Render::OpenGL::RenderQueue;
+
+ SimpleWorker *jobsThread = new SimpleWorker();
+ renderQueue->setTargetRenderViewCount(7);
+ jobsThread->m_renderQueues = renderQueue;
+
+ // THEN
+ QVERIFY(jobsThread->m_renderQueues->targetRenderViewCount() == renderQueue->targetRenderViewCount());
+ QVERIFY(jobsThread->m_renderQueues->currentRenderViewCount() == renderQueue->currentRenderViewCount());
+
+ // Start thread
+ jobsThread->start();
+
+ jobsThread->m_waitQueue.release();
+
+ for (int i = 0; i < 5; ++i) {
+ jobsThread->m_waitSubmit.acquire();
+
+ // WHEN unlocked
+ // THEN
+ QVERIFY (renderQueue->isFrameQueueComplete());
+ QCOMPARE(renderQueue->nextFrameQueue().size(), renderQueue->targetRenderViewCount());
+
+ // reset queue for next frame
+ renderQueue->reset();
+ renderQueue->setTargetRenderViewCount(7);
+ jobsThread->m_waitQueue.release();
+ }
+ jobsThread->wait();
+}
+
+void tst_RenderQueue::resetQueue()
+{
+ // GIVEN
+ Qt3DRender::Render::OpenGL::RenderQueue renderQueue;
+
+ for (int j = 0; j < 5; j++) {
+ // WHEN
+ renderQueue.setTargetRenderViewCount(5);
+ // THEN
+ QCOMPARE(renderQueue.wasReset(), false);
+ QVERIFY(renderQueue.currentRenderViewCount() == 0);
+
+ // WHEN
+ QVector<Qt3DRender::Render::OpenGL::RenderView *> renderViews(5);
+ for (int i = 0; i < 5; ++i) {
+ renderQueue.queueRenderView(renderViews.at(i), i);
+ }
+ // THEN
+ QCOMPARE(renderQueue.currentRenderViewCount(), 5);
+ QVERIFY(renderQueue.isFrameQueueComplete());
+
+ // WHEN
+ renderQueue.reset();
+ QCOMPARE(renderQueue.wasReset(), true);
+ // THEN
+ QVERIFY(renderQueue.currentRenderViewCount() == 0);
+ }
+}
+
+QTEST_APPLESS_MAIN(tst_RenderQueue)
+
+#include "tst_renderqueue.moc"
diff --git a/tests/auto/render/opengl/renderviewbuilder/renderviewbuilder.pro b/tests/auto/render/opengl/renderviewbuilder/renderviewbuilder.pro
new file mode 100644
index 000000000..b7b8f5b81
--- /dev/null
+++ b/tests/auto/render/opengl/renderviewbuilder/renderviewbuilder.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_renderviewbuilder
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_renderviewbuilder.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
new file mode 100644
index 000000000..fe534e243
--- /dev/null
+++ b/tests/auto/render/opengl/renderviewbuilder/tst_renderviewbuilder.cpp
@@ -0,0 +1,670 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Paul Lemire <paul.lemire350@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <Qt3DCore/private/qaspectjobmanager_p.h>
+#include <Qt3DCore/private/qnodevisitor_p.h>
+#include <Qt3DCore/private/qnode_p.h>
+
+#include <renderviewbuilder_p.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/qviewport.h>
+#include <Qt3DRender/qclearbuffers.h>
+#include <Qt3DRender/qdispatchcompute.h>
+#include <Qt3DRender/qfrustumculling.h>
+#include <Qt3DRender/qmaterial.h>
+#include <Qt3DRender/qspotlight.h>
+#include <Qt3DRender/qpointlight.h>
+#include <Qt3DRender/qenvironmentlight.h>
+#include <Qt3DRender/qgeometryrenderer.h>
+#include <Qt3DRender/qcomputecommand.h>
+#include <Qt3DRender/qlayerfilter.h>
+#include <Qt3DRender/qrenderpassfilter.h>
+#include <Qt3DRender/qtechniquefilter.h>
+#include <Qt3DRender/qcameraselector.h>
+#include <Qt3DRender/qcamera.h>
+#include <Qt3DRender/qlayer.h>
+#include <Qt3DRender/private/qrenderaspect_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/filterentitybycomponentjob_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+class TestAspect : public QRenderAspect
+{
+public:
+ TestAspect(Qt3DCore::QNode *root)
+ : QRenderAspect(Qt3DRender::QRenderAspect::Synchronous)
+ , m_jobManager(new Qt3DCore::QAspectJobManager())
+ {
+ Qt3DCore::QAbstractAspectPrivate::get(this)->m_jobManager = m_jobManager.data();
+ QRenderAspect::onRegistered();
+
+ QVector<Qt3DCore::QNode *> nodes;
+ Qt3DCore::QNodeVisitor v;
+ v.traverse(root, [&nodes](Qt3DCore::QNode *node) {
+ Qt3DCore::QNodePrivate *d = Qt3DCore::QNodePrivate::get(node);
+ d->m_typeInfo = const_cast<QMetaObject*>(Qt3DCore::QNodePrivate::findStaticMetaObject(node->metaObject()));
+ d->m_hasBackendNode = true;
+ nodes << node;
+ });
+
+ for (const auto node: nodes)
+ d_func()->createBackendNode({
+ node->id(),
+ Qt3DCore::QNodePrivate::get(node)->m_typeInfo,
+ Qt3DCore::NodeTreeChange::Added,
+ node
+ });
+ }
+
+ ~TestAspect()
+ {
+ QRenderAspect::onUnregistered();
+ }
+
+ Qt3DRender::Render::NodeManagers *nodeManagers() const
+ {
+ return d_func()->m_renderer->nodeManagers();
+ }
+
+ Render::OpenGL::Renderer *renderer() const
+ {
+ return static_cast<Render::OpenGL::Renderer *>(d_func()->m_renderer);
+ }
+
+ Render::OpenGL::MaterialParameterGathererJobPtr materialGathererJob() const
+ {
+ Render::OpenGL::MaterialParameterGathererJobPtr job = Render::OpenGL::MaterialParameterGathererJobPtr::create();
+ job->setNodeManagers(nodeManagers());
+ return job;
+ }
+
+ void onRegistered() { QRenderAspect::onRegistered(); }
+ void onUnregistered() { QRenderAspect::onUnregistered(); }
+
+private:
+ QScopedPointer<Qt3DCore::QAspectJobManager> m_jobManager;
+};
+
+} // namespace Qt3DRender
+
+QT_END_NAMESPACE
+
+namespace {
+
+Qt3DCore::QEntity *buildSimpleScene(Qt3DRender::QFrameGraphNode *fg)
+{
+ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
+
+ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
+ renderSettings->setActiveFrameGraph(fg);
+ root->addComponent(renderSettings);
+
+ // Scene
+ {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
+ Qt3DRender::QGeometryRenderer *geometryRenderer = new Qt3DRender::QGeometryRenderer();
+ e->addComponent(material);
+ e->addComponent(geometryRenderer);
+ e->setParent(root);
+ }
+ {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
+ Qt3DRender::QComputeCommand *computeCommand = new Qt3DRender::QComputeCommand();
+ e->addComponent(material);
+ e->addComponent(computeCommand);
+ e->setParent(root);
+ }
+
+ {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight();
+ e->addComponent(light);
+ e->setParent(root);
+ }
+
+ {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QSpotLight *light = new Qt3DRender::QSpotLight();
+ e->addComponent(light);
+ e->setParent(root);
+ }
+
+ {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QEnvironmentLight *light = new Qt3DRender::QEnvironmentLight();
+ e->addComponent(light);
+ e->setParent(root);
+ }
+
+ return root;
+}
+
+Qt3DCore::QEntity *buildEntityFilterTestScene(Qt3DRender::QFrameGraphNode *fg, Qt3DRender::QLayer *layer)
+{
+ Qt3DCore::QEntity *root = new Qt3DCore::QEntity();
+
+ Qt3DRender::QRenderSettings* renderSettings = new Qt3DRender::QRenderSettings();
+ renderSettings->setActiveFrameGraph(fg);
+ root->addComponent(renderSettings);
+
+ // Scene
+ for (int i = 0; i < 200; ++i) {
+ Qt3DCore::QEntity *e = new Qt3DCore::QEntity();
+ Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial();
+ Qt3DRender::QGeometryRenderer *geometryRenderer = new Qt3DRender::QGeometryRenderer();
+ e->addComponent(material);
+ e->addComponent(geometryRenderer);
+ if (i % 2 == 0)
+ e->addComponent(layer);
+ e->setParent(root);
+ }
+
+ return root;
+}
+
+} // anonymous
+
+
+class tst_RenderViewBuilder : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkInitialState()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+
+ // THEN
+ QCOMPARE(renderViewBuilder.renderViewIndex(), 0);
+ QCOMPARE(renderViewBuilder.renderer(), testAspect.renderer());
+ QCOMPARE(renderViewBuilder.layerCacheNeedsToBeRebuilt(), false);
+ QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), false);
+ QVERIFY(!renderViewBuilder.renderViewJob().isNull());
+ QVERIFY(!renderViewBuilder.frustumCullingJob().isNull());
+ QVERIFY(!renderViewBuilder.syncPreFrustumCullingJob().isNull());
+ QVERIFY(!renderViewBuilder.setClearDrawBufferIndexJob().isNull());
+
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
+ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob().isNull());
+ QVERIFY(renderViewBuilder.syncRenderViewPostCommandUpdateJob().isNull());
+ QVERIFY(renderViewBuilder.syncRenderViewPostInitializationJob().isNull());
+
+ QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), 0);
+ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0);
+
+ // WHEN
+ renderViewBuilder.prepareJobs();
+
+ // THEN
+ QVERIFY(!renderViewBuilder.syncRenderViewPreCommandUpdateJob().isNull());
+ QVERIFY(!renderViewBuilder.syncRenderViewPostCommandUpdateJob().isNull());
+ QVERIFY(!renderViewBuilder.syncRenderViewPostInitializationJob().isNull());
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
+ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
+
+ QCOMPARE(renderViewBuilder.renderViewCommandUpdaterJobs().size(), Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), 0);
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 8 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ }
+
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.prepareJobs();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.layerCacheNeedsToBeRebuilt(), true);
+ QVERIFY(!renderViewBuilder.filterEntityByLayerJob().isNull());
+ QVERIFY(!renderViewBuilder.syncFilterEntityByLayerJob().isNull());
+
+ // mark jobs dirty and recheck
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 10 + 1 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ }
+
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.prepareJobs();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.materialGathererCacheNeedsToBeRebuilt(), true);
+ QCOMPARE(renderViewBuilder.materialGathererJobs().size(), Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ QVERIFY(!renderViewBuilder.syncMaterialGathererJob().isNull());
+
+ // mark jobs dirty and recheck
+ QCOMPARE(renderViewBuilder.buildJobHierachy().size(), 9 + 2 * Qt3DRender::Render::OpenGL::RenderViewBuilder::optimalJobCount());
+ }
+ }
+
+ void checkCheckJobDependencies()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ // THEN
+ // Step 1
+ QCOMPARE(renderViewBuilder.renderViewJob()->dependencies().size(), 1); // Depends upon skinning palette update
+
+
+ // Step 2
+ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.renderViewJob().data());
+
+ // Step 3
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob().isNull());
+ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob().isNull());
+
+ QCOMPARE(renderViewBuilder.filterProximityJob()->dependencies().size(), 2);
+ QVERIFY(renderViewBuilder.filterProximityJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.filterProximityJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
+
+ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.syncRenderViewPostInitializationJob().data());
+
+ QCOMPARE(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().size(), 3);
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateWorldTransformJob()));
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob()));
+
+ // Step 4
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->dependencies().size(), 2);
+ QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncPreFrustumCullingJob()));
+ QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
+
+
+ QCOMPARE(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().size(), renderViewBuilder.materialGathererJobs().size() + 7);
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.filterProximityJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.frustumCullingJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->introspectShadersJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->bufferGathererJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->textureGathererJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->cacheLightJob()));
+
+ // Step 5
+ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
+ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilderJob->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.syncRenderViewPreCommandUpdateJob().data());
+ }
+
+ // Step 6
+ QCOMPARE(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().size(), renderViewBuilder.renderViewCommandUpdaterJobs().size());
+ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
+ QVERIFY(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().contains(renderViewBuilderJob));
+ }
+ }
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.setMaterialGathererCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ // THEN
+ // Step 1
+ QCOMPARE(renderViewBuilder.renderViewJob()->dependencies().size(), 1); // Depends upon skinning palette update
+
+ // Step 2
+ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilder.syncRenderViewPostInitializationJob()->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.renderViewJob().data());
+
+ // Step 3
+ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->dependencies().size(), 3);
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob()->dependencies().contains(testAspect.renderer()->updateEntityLayersJob()));
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.filterEntityByLayerJob()->dependencies().contains(testAspect.renderer()->updateTreeEnabledJob()));
+
+ QCOMPARE(renderViewBuilder.syncFilterEntityByLayerJob()->dependencies().size(), 1);
+ QVERIFY(renderViewBuilder.syncFilterEntityByLayerJob()->dependencies().contains(renderViewBuilder.filterEntityByLayerJob()));
+
+ QCOMPARE(renderViewBuilder.filterProximityJob()->dependencies().size(), 2);
+ QVERIFY(renderViewBuilder.filterProximityJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.filterProximityJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
+
+ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilder.setClearDrawBufferIndexJob()->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.syncRenderViewPostInitializationJob().data());
+
+ QCOMPARE(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().size(), 3);
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateWorldTransformJob()));
+ QVERIFY(renderViewBuilder.syncPreFrustumCullingJob()->dependencies().contains(testAspect.renderer()->updateShaderDataTransformJob()));
+
+ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
+ QCOMPARE(materialGatherer->dependencies().size(), 3);
+ QVERIFY(materialGatherer->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->introspectShadersJob()));
+ QVERIFY(materialGatherer->dependencies().contains(testAspect.renderer()->filterCompatibleTechniqueJob()));
+ }
+
+ // Step 4
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->dependencies().size(), 2);
+ QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(renderViewBuilder.syncPreFrustumCullingJob()));
+ QVERIFY(renderViewBuilder.frustumCullingJob()->dependencies().contains(testAspect.renderer()->expandBoundingVolumeJob()));
+
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.syncRenderViewPostInitializationJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.syncFilterEntityByLayerJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.frustumCullingJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(renderViewBuilder.filterProximityJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->introspectShadersJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->bufferGathererJob()));
+ QVERIFY(renderViewBuilder.syncRenderViewPreCommandUpdateJob()->dependencies().contains(testAspect.renderer()->textureGathererJob()));
+
+ // Step 5
+ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
+ QCOMPARE(renderViewBuilderJob->dependencies().size(), 1);
+ QCOMPARE(renderViewBuilderJob->dependencies().constFirst().toStrongRef().data(),
+ renderViewBuilder.syncRenderViewPreCommandUpdateJob().data());
+ }
+
+ // Step 6
+ QCOMPARE(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().size(), renderViewBuilder.renderViewCommandUpdaterJobs().size());
+ for (const auto &renderViewBuilderJob : renderViewBuilder.renderViewCommandUpdaterJobs()) {
+ QVERIFY(renderViewBuilder.syncRenderViewPostCommandUpdateJob()->dependencies().contains(renderViewBuilderJob));
+ }
+ }
+ }
+
+ void checkRenderViewJobExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+ renderViewBuilder.renderViewJob()->run();
+
+ // THEN
+ QVERIFY(renderViewBuilder.renderViewJob()->renderView() != nullptr);
+ }
+
+ void checkLightGatherExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ renderer->lightGathererJob()->run();
+
+ // THEN
+ QCOMPARE(renderer->lightGathererJob()->lights().size(), 2);
+ QVERIFY(renderer->lightGathererJob()->takeEnvironmentLight() != nullptr);
+ }
+
+ void checkRenderableEntitiesFilteringExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ renderer->renderableEntityFilterJob()->run();
+
+ // THEN
+ QCOMPARE(renderer->renderableEntityFilterJob()->filteredEntities().size(), 1);
+ }
+
+ void checkComputableEntitiesFilteringExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(clearBuffer->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ renderer->computableEntityFilterJob()->run();
+
+ // THEN
+ QCOMPARE(renderer->computableEntityFilterJob()->filteredEntities().size(), 1);
+ }
+
+ void checkSyncRenderViewInitializationExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(clearBuffer);
+ Qt3DRender::QFrustumCulling *frustumCulling = new Qt3DRender::QFrustumCulling(layerFilter);
+ Qt3DRender::QTechniqueFilter *techniqueFilter = new Qt3DRender::QTechniqueFilter(frustumCulling);
+ Qt3DRender::QRenderPassFilter *renderPassFilter = new Qt3DRender::QRenderPassFilter(techniqueFilter);
+ Qt3DRender::QLayer *layer = new Qt3DRender::QLayer();
+
+ layerFilter->addLayer(layer);
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(renderPassFilter->id());
+ QVERIFY(leafNode != nullptr);
+
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false);
+ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
+ QVERIFY(materialGatherer->techniqueFilter() == nullptr);
+ QVERIFY(materialGatherer->renderPassFilter() == nullptr);
+ }
+
+ // WHEN
+ renderViewBuilder.renderViewJob()->run();
+ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true);
+ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
+ QVERIFY(materialGatherer->techniqueFilter() != nullptr);
+ QVERIFY(materialGatherer->renderPassFilter() != nullptr);
+ }
+ }
+ {
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), false);
+ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), false);
+ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 0);
+ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
+ QVERIFY(materialGatherer->techniqueFilter() == nullptr);
+ QVERIFY(materialGatherer->renderPassFilter() == nullptr);
+ }
+
+ // WHEN
+ renderViewBuilder.renderViewJob()->run();
+ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->isActive(), true);
+ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->hasLayerFilter(), true);
+ QCOMPARE(renderViewBuilder.filterEntityByLayerJob()->layerFilters().size(), 1);
+ for (const auto &materialGatherer : renderViewBuilder.materialGathererJobs()) {
+ QVERIFY(materialGatherer->techniqueFilter() != nullptr);
+ QVERIFY(materialGatherer->renderPassFilter() != nullptr);
+ }
+ }
+ }
+
+ void checkSyncFrustumCullingExecution()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::QFrustumCulling *frustumCulling = new Qt3DRender::QFrustumCulling(clearBuffer);
+ Qt3DRender::QCameraSelector *cameraSelector = new Qt3DRender::QCameraSelector(frustumCulling);
+ Qt3DRender::QCamera *camera = new Qt3DRender::QCamera();
+ cameraSelector->setCamera(camera);
+
+ Qt3DRender::TestAspect testAspect(buildSimpleScene(viewport));
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(cameraSelector->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ // THEN
+ QCOMPARE(renderViewBuilder.frustumCullingJob()->viewProjection(), Matrix4x4());
+
+ // WHEN
+ renderViewBuilder.renderViewJob()->run();
+ renderViewBuilder.syncPreFrustumCullingJob()->run();
+
+ // THEN
+ QCOMPARE(convertToQMatrix4x4(renderViewBuilder.frustumCullingJob()->viewProjection()), camera->projectionMatrix() * camera->viewMatrix());
+ }
+
+ void checkRemoveEntitiesNotInSubset()
+ {
+ // GIVEN
+ Qt3DRender::QViewport *viewport = new Qt3DRender::QViewport();
+ Qt3DRender::QClearBuffers *clearBuffer = new Qt3DRender::QClearBuffers(viewport);
+ Qt3DRender::QLayerFilter *layerFilter = new Qt3DRender::QLayerFilter(clearBuffer);
+ Qt3DRender::QLayer *layer = new Qt3DRender::QLayer();
+ layerFilter->addLayer(layer);
+ Qt3DRender::TestAspect testAspect(buildEntityFilterTestScene(viewport, layer));
+ Qt3DRender::Render::OpenGL::Renderer *renderer = testAspect.renderer();
+
+ // THEN
+ Qt3DRender::Render::FrameGraphNode *leafNode = testAspect.nodeManagers()->frameGraphManager()->lookupNode(layerFilter->id());
+ QVERIFY(leafNode != nullptr);
+
+ // WHEN
+ renderer->markDirty(Qt3DRender::Render::AbstractRenderer::AllDirty, nullptr);
+
+ Qt3DRender::Render::OpenGL::RenderViewBuilder renderViewBuilder(leafNode, 0, testAspect.renderer());
+
+ renderViewBuilder.setLayerCacheNeedsToBeRebuilt(true);
+ renderViewBuilder.prepareJobs();
+ renderViewBuilder.buildJobHierachy();
+
+ renderer->renderableEntityFilterJob()->run();
+ renderer->cacheRenderableEntitiesJob()->run();
+
+ renderViewBuilder.renderViewJob()->run();
+ renderViewBuilder.syncRenderViewPostInitializationJob()->run();
+ renderViewBuilder.filterEntityByLayerJob()->run();
+
+ QVector<Qt3DRender::Render::Entity *> renderableEntity = renderer->renderableEntityFilterJob()->filteredEntities();
+ QVector<Qt3DRender::Render::Entity *> filteredEntity = renderViewBuilder.filterEntityByLayerJob()->filteredEntities();
+
+ // THEN
+ QCOMPARE(renderableEntity.size(), 200);
+ QCOMPARE(filteredEntity.size(), 100);
+
+ std::sort(renderableEntity.begin(), renderableEntity.end());
+
+ // WHEN
+ renderableEntity = Qt3DRender::Render::OpenGL::RenderViewBuilder::entitiesInSubset(renderableEntity, filteredEntity);
+
+ // THEN
+ QCOMPARE(renderableEntity.size(), 100);
+ for (const auto entity : renderableEntity) {
+ QVERIFY(filteredEntity.contains(entity));
+ }
+ }
+
+};
+
+QTEST_MAIN(tst_RenderViewBuilder)
+
+#include "tst_renderviewbuilder.moc"
diff --git a/tests/auto/render/opengl/renderviews/renderviews.pro b/tests/auto/render/opengl/renderviews/renderviews.pro
new file mode 100644
index 000000000..7efbaec14
--- /dev/null
+++ b/tests/auto/render/opengl/renderviews/renderviews.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_renderviews
+
+QT += 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_renderviews.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/renderviews/tst_renderviews.cpp b/tests/auto/render/opengl/renderviews/tst_renderviews.cpp
new file mode 100644
index 000000000..6d01122d1
--- /dev/null
+++ b/tests/auto/render/opengl/renderviews/tst_renderviews.cpp
@@ -0,0 +1,556 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <qbackendnodetester.h>
+#include <private/memorybarrier_p.h>
+#include <testpostmanarbiter.h>
+#include <testrenderer.h>
+#include <private/shader_p.h>
+#include <Qt3DRender/qshaderprogram.h>
+#include <renderview_p.h>
+#include <renderviewjobutils_p.h>
+#include <rendercommand_p.h>
+#include <renderer_p.h>
+#include <glresourcemanagers_p.h>
+#include <private/shader_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DRender {
+
+namespace Render {
+
+namespace OpenGL {
+
+namespace {
+
+void compareShaderParameterPacks(const ShaderParameterPack &t1,
+ const ShaderParameterPack &t2)
+{
+ const PackUniformHash hash1 = t1.uniforms();
+ const PackUniformHash hash2 = t2.uniforms();
+
+ QCOMPARE(hash1.keys.size(), hash2.keys.size());
+
+ for (int i = 0, m = hash1.keys.size(); i < m; ++i) {
+ const int key = hash1.keys.at(i);
+ QCOMPARE(hash1.value(key), hash2.value(key));
+ }
+}
+
+} // anonymous
+
+class tst_RenderViews : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+
+ void checkRenderViewSizeFitsWithAllocator()
+ {
+ QSKIP("Allocated Disabled");
+ QVERIFY(sizeof(RenderView) <= 192);
+ QVERIFY(sizeof(RenderView::InnerData) <= 192);
+ }
+
+ void checkRenderViewInitialState()
+ {
+ // GIVEN
+ RenderView renderView;
+
+ // THEN
+ QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None);
+ }
+
+ void checkMemoryBarrierInitialization()
+ {
+ // GIVEN
+ RenderView renderView;
+
+ // THEN
+ QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None);
+
+ // WHEN
+ const QMemoryBarrier::Operations barriers(QMemoryBarrier::BufferUpdate|QMemoryBarrier::ShaderImageAccess);
+ renderView.setMemoryBarrier(barriers);
+
+ // THEN
+ QCOMPARE(renderView.memoryBarrier(), barriers);
+ }
+
+ void checkSetRenderViewConfig()
+ {
+ TestRenderer renderer;
+ {
+ // GIVEN
+ const QMemoryBarrier::Operations barriers(QMemoryBarrier::AtomicCounter|QMemoryBarrier::ShaderStorage);
+ Qt3DRender::QMemoryBarrier frontendBarrier;
+ FrameGraphManager frameGraphManager;
+ MemoryBarrier backendBarrier;
+ RenderView renderView;
+ // setRenderViewConfigFromFrameGraphLeafNode assumes node has a manager
+ backendBarrier.setFrameGraphManager(&frameGraphManager);
+ backendBarrier.setRenderer(&renderer);
+
+ // WHEN
+ frontendBarrier.setWaitOperations(barriers);
+ simulateInitializationSync(&frontendBarrier, &backendBarrier);
+
+ // THEN
+ QCOMPARE(renderView.memoryBarrier(), QMemoryBarrier::None);
+ QCOMPARE(backendBarrier.waitOperations(), barriers);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::setRenderViewConfigFromFrameGraphLeafNode(&renderView, &backendBarrier);
+
+ // THEN
+ QCOMPARE(backendBarrier.waitOperations(), renderView.memoryBarrier());
+ }
+ // TO DO: Complete tests for other framegraph node types
+ }
+
+ void checkRenderCommandBackToFrontSorting()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ sortTypes.push_back(QSortPolicy::BackToFront);
+
+ for (int i = 0; i < 200; ++i) {
+ RenderCommand c;
+ c.m_depth = float(i);
+ rawCommands.push_back(c);
+ }
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+ for (int j = 1; j < sortedCommands.size(); ++j)
+ QVERIFY(sortedCommands.at(j - 1).m_depth > sortedCommands.at(j).m_depth);
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
+ void checkRenderCommandMaterialSorting()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ sortTypes.push_back(QSortPolicy::Material);
+
+ GLShader *dnas[5] = {
+ reinterpret_cast<GLShader *>(0x250),
+ reinterpret_cast<GLShader *>(0x500),
+ reinterpret_cast<GLShader *>(0x1000),
+ reinterpret_cast<GLShader *>(0x1500),
+ reinterpret_cast<GLShader *>(0x2000)
+ };
+
+ for (int i = 0; i < 20; ++i) {
+ RenderCommand c;
+ c.m_glShader = dnas[i % 5];
+ rawCommands.push_back(c);
+ }
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+ GLShader *targetShader;
+
+ for (int j = 0; j < sortedCommands.size(); ++j) {
+
+ if (j % 4 == 0) {
+ targetShader = sortedCommands.at(j).m_glShader;
+ if (j > 0)
+ QVERIFY(targetShader != sortedCommands.at(j - 1).m_glShader);
+ }
+ QCOMPARE(targetShader, sortedCommands.at(j).m_glShader);
+ }
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
+ void checkRenderViewUniformMinification_data()
+ {
+ QTest::addColumn<QVector<QShaderProgram*>>("shaders");
+ QTest::addColumn<QVector<ShaderParameterPack>>("rawParameters");
+ QTest::addColumn<QVector<ShaderParameterPack>>("expectedMinimizedParameters");
+
+ Qt3DCore::QNodeId fakeTextureNodeId = Qt3DCore::QNodeId::createId();
+
+ ShaderParameterPack pack1;
+ pack1.setUniform(1, UniformValue(883));
+ pack1.setUniform(2, UniformValue(1584.0f));
+ pack1.setTexture(3, 0, fakeTextureNodeId);
+
+ QShaderProgram *shader1 = new QShaderProgram();
+ QShaderProgram *shader2 = new QShaderProgram();
+
+ shader1->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("1"));
+ shader2->setShaderCode(QShaderProgram::Vertex, QByteArrayLiteral("2"));
+
+ ShaderParameterPack minifiedPack1;
+
+ QTest::newRow("NoMinification")
+ << (QVector<QShaderProgram*>() << shader1 << shader2)
+ << (QVector<ShaderParameterPack>() << pack1 << pack1)
+ << (QVector<ShaderParameterPack>() << pack1 << pack1);
+
+ QTest::newRow("SingleShaderMinified")
+ << (QVector<QShaderProgram*>() << shader1 << shader1 << shader1)
+ << (QVector<ShaderParameterPack>() << pack1 << pack1 << pack1)
+ << (QVector<ShaderParameterPack>() << pack1 << minifiedPack1 << minifiedPack1);
+
+ QTest::newRow("MultipleShadersMinified")
+ << (QVector<QShaderProgram*>() << shader1 << shader1 << shader1 << shader2 << shader2 << shader2)
+ << (QVector<ShaderParameterPack>() << pack1 << pack1 << pack1 << pack1 << pack1 << pack1)
+ << (QVector<ShaderParameterPack>() << pack1 << minifiedPack1 << minifiedPack1 << pack1 << minifiedPack1 << minifiedPack1);
+ }
+
+ void checkRenderViewUniformMinification()
+ {
+ QFETCH(QVector<QShaderProgram*>, shaders);
+ QFETCH(QVector<ShaderParameterPack>, rawParameters);
+ QFETCH(QVector<ShaderParameterPack>, expectedMinimizedParameters);
+
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(&nodeManagers);
+
+ GLShaderManager *shaderManager = renderer.glResourceManagers()->glShaderManager();
+ for (int i = 0, m = shaders.size(); i < m; ++i) {
+ Shader* backend = new Shader();
+ backend->setRenderer(&renderer);
+ simulateInitializationSync(shaders.at(i), backend);
+ shaderManager->createOrAdoptExisting(backend);
+ }
+
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ renderView.setRenderer(&renderer);
+
+ for (int i = 0, m = shaders.size(); i < m; ++i) {
+ RenderCommand c;
+ c.m_shaderId = shaders.at(i)->id();
+ c.m_glShader = shaderManager->lookupResource(c.m_shaderId);
+ c.m_parameterPack = rawParameters.at(i);
+ rawCommands.push_back(c);
+ }
+
+ // WHEN
+ renderView.setCommands(rawCommands);
+ renderView.addSortType((QVector<QSortPolicy::SortType>() << QSortPolicy::Uniform));
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands, sortedCommands);
+
+ for (int i = 0, m = shaders.size(); i < m; ++i) {
+ const RenderCommand c = sortedCommands.at(i);
+ QCOMPARE(c.m_shaderId, shaders.at(i)->id());
+ compareShaderParameterPacks(c.m_parameterPack, expectedMinimizedParameters.at(i));
+ }
+
+ renderer.shutdown();
+ }
+
+
+ void checkRenderCommandFrontToBackSorting()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ sortTypes.push_back(QSortPolicy::FrontToBack);
+
+ for (int i = 0; i < 200; ++i) {
+ RenderCommand c;
+ c.m_depth = float(i);
+ rawCommands.push_back(c);
+ }
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+ for (int j = 1; j < sortedCommands.size(); ++j)
+ QVERIFY(sortedCommands.at(j - 1).m_depth < sortedCommands.at(j).m_depth);
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
+ void checkRenderCommandStateCostSorting()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ sortTypes.push_back(QSortPolicy::StateChangeCost);
+
+ for (int i = 0; i < 200; ++i) {
+ RenderCommand c;
+ c.m_changeCost = i;
+ rawCommands.push_back(c);
+ }
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+ for (int j = 1; j < sortedCommands.size(); ++j)
+ QVERIFY(sortedCommands.at(j - 1).m_changeCost > sortedCommands.at(j).m_changeCost);
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
+ void checkRenderCommandCombinedStateMaterialDepthSorting()
+ {
+ // GIVEN
+ Qt3DRender::Render::NodeManagers nodeManagers;
+ Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ RenderView renderView;
+ QVector<RenderCommand> rawCommands;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ renderer.setNodeManagers(&nodeManagers);
+ renderView.setRenderer(&renderer);
+
+ sortTypes.push_back(QSortPolicy::StateChangeCost);
+ sortTypes.push_back(QSortPolicy::Material);
+ sortTypes.push_back(QSortPolicy::BackToFront);
+
+ GLShader *dna[5] = {
+ reinterpret_cast<GLShader *>(0x250),
+ reinterpret_cast<GLShader *>(0x500),
+ reinterpret_cast<GLShader *>(0x1000),
+ reinterpret_cast<GLShader *>(0x1500),
+ reinterpret_cast<GLShader *>(0x2000)
+ };
+
+ float depth[3] = {
+ 10.0f,
+ 25.0f,
+ 30.0f
+ };
+
+ int stateChangeCost[2] = {
+ 100,
+ 200
+ };
+
+ auto buildRC = [] (GLShader *dna, float depth, int changeCost) {
+ RenderCommand c;
+ c.m_glShader = dna;
+ c.m_depth = depth;
+ c.m_changeCost = changeCost;
+ return c;
+ };
+
+ RenderCommand c5 = buildRC(dna[3], depth[1], stateChangeCost[1]);
+ RenderCommand c3 = buildRC(dna[3], depth[0], stateChangeCost[1]);
+ RenderCommand c4 = buildRC(dna[2], depth[1], stateChangeCost[1]);
+ RenderCommand c8 = buildRC(dna[1], depth[1], stateChangeCost[1]);
+ RenderCommand c0 = buildRC(dna[0], depth[2], stateChangeCost[1]);
+
+ RenderCommand c2 = buildRC(dna[2], depth[2], stateChangeCost[0]);
+ RenderCommand c9 = buildRC(dna[2], depth[0], stateChangeCost[0]);
+ RenderCommand c1 = buildRC(dna[1], depth[0], stateChangeCost[0]);
+ RenderCommand c7 = buildRC(dna[0], depth[2], stateChangeCost[0]);
+ RenderCommand c6 = buildRC(dna[0], depth[1], stateChangeCost[0]);
+
+ rawCommands << c0 << c1 << c2 << c3 << c4 << c5 << c6 << c7 << c8 << c9;
+
+ // WHEN
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+
+ // Ordered by higher state, higher shaderDNA and higher depth
+ QCOMPARE(c0, sortedCommands.at(4));
+ QCOMPARE(c3, sortedCommands.at(1));
+ QCOMPARE(c4, sortedCommands.at(2));
+ QCOMPARE(c5, sortedCommands.at(0));
+ QCOMPARE(c8, sortedCommands.at(3));
+
+ QCOMPARE(c1, sortedCommands.at(7));
+ QCOMPARE(c2, sortedCommands.at(5));
+ QCOMPARE(c6, sortedCommands.at(9));
+ QCOMPARE(c7, sortedCommands.at(8));
+ QCOMPARE(c9, sortedCommands.at(6));
+
+ // RenderCommands are deleted by RenderView dtor
+ renderer.shutdown();
+ }
+
+ void checkRenderCommandTextureSorting()
+ {
+ // GIVEN
+ RenderView renderView;
+ QVector<QSortPolicy::SortType> sortTypes;
+
+ sortTypes.push_back(QSortPolicy::Texture);
+
+
+ Qt3DCore::QNodeId tex1 = Qt3DCore::QNodeId::createId();
+ Qt3DCore::QNodeId tex2 = Qt3DCore::QNodeId::createId();
+ Qt3DCore::QNodeId tex3 = Qt3DCore::QNodeId::createId();
+ Qt3DCore::QNodeId tex4 = Qt3DCore::QNodeId::createId();
+
+ RenderCommand a;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(0, 0, tex1);
+ pack.setTexture(1, 0, tex3);
+ pack.setTexture(2, 0, tex4);
+ pack.setTexture(3, 0, tex2);
+ a.m_parameterPack = pack;
+ }
+ RenderCommand b;
+ RenderCommand c;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(0, 0, tex1);
+ pack.setTexture(3, 0, tex2);
+ c.m_parameterPack = pack;
+ }
+ RenderCommand d;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(1, 0, tex3);
+ pack.setTexture(2, 0, tex4);
+ d.m_parameterPack = pack;
+ }
+ RenderCommand e;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(3, 0, tex2);
+ e.m_parameterPack = pack;
+ }
+ RenderCommand f;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(3, 0, tex2);
+ f.m_parameterPack = pack;
+ }
+ RenderCommand g;
+ {
+ ShaderParameterPack pack;
+ pack.setTexture(0, 0, tex1);
+ pack.setTexture(1, 0, tex3);
+ pack.setTexture(2, 0, tex4);
+ pack.setTexture(3, 0, tex2);
+ g.m_parameterPack = pack;
+ }
+
+ // WHEN
+ QVector<RenderCommand> rawCommands = {a, b, c, d, e, f, g};
+ renderView.addSortType(sortTypes);
+ renderView.setCommands(rawCommands);
+ renderView.sort();
+
+ // THEN
+ const QVector<RenderCommand> sortedCommands = renderView.commands();
+ QCOMPARE(rawCommands.size(), sortedCommands.size());
+ QCOMPARE(sortedCommands.at(0), a);
+ QCOMPARE(sortedCommands.at(1), g);
+ QCOMPARE(sortedCommands.at(2), d);
+ QCOMPARE(sortedCommands.at(3), c);
+ QCOMPARE(sortedCommands.at(4), e);
+ QCOMPARE(sortedCommands.at(5), f);
+ QCOMPARE(sortedCommands.at(6), b);
+ // RenderCommands are deleted by RenderView dtor
+ }
+private:
+};
+
+} // OpenGL
+
+} // Render
+
+} // Qt3DRender
+
+QT_END_NAMESPACE
+
+//APPLESS_
+QTEST_MAIN(Qt3DRender::Render::OpenGL::tst_RenderViews)
+
+#include "tst_renderviews.moc"
diff --git a/tests/auto/render/opengl/renderviewutils/renderviewutils.pro b/tests/auto/render/opengl/renderviewutils/renderviewutils.pro
new file mode 100644
index 000000000..e9c9f8d51
--- /dev/null
+++ b/tests/auto/render/opengl/renderviewutils/renderviewutils.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_renderviewutils
+
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_renderviewutils.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/renderviewutils/tst_renderviewutils.cpp b/tests/auto/render/opengl/renderviewutils/tst_renderviewutils.cpp
new file mode 100644
index 000000000..6b714b9e1
--- /dev/null
+++ b/tests/auto/render/opengl/renderviewutils/tst_renderviewutils.cpp
@@ -0,0 +1,801 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QTest>
+#include <qbackendnodetester.h>
+#include <Qt3DCore/qdynamicpropertyupdatedchange.h>
+#include <renderviewjobutils_p.h>
+#include <shadervariables_p.h>
+#include <Qt3DRender/private/shaderdata_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/stringtoint_p.h>
+#include <Qt3DRender/qshaderdata.h>
+#include "testrenderer.h"
+#include "testpostmanarbiter.h"
+
+class tst_RenderViewUtils : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void topLevelScalarValueNoUniforms();
+ void topLevelScalarValue();
+ void topLevelTextureValueNoUniforms();
+ void topLevelTextureValue();
+ void topLevelArrayValue();
+ void nestedShaderDataValue();
+ void topLevelStructValue_data();
+ void topLevelStructValue();
+ void topLevelDynamicProperties();
+ void transformedProperties();
+ void shouldNotifyDynamicPropertyChanges();
+
+private:
+ void initBackendShaderData(Qt3DRender::Render::AbstractRenderer *renderer,
+ Qt3DRender::QShaderData *frontend,
+ Qt3DRender::Render::ShaderDataManager *manager)
+ {
+ // Create children first
+ for (QObject *c : frontend->children()) {
+ Qt3DRender::QShaderData *cShaderData = qobject_cast<Qt3DRender::QShaderData *>(c);
+ if (cShaderData)
+ initBackendShaderData(renderer, cShaderData, manager);
+ }
+
+ // Create backend element for frontend one
+ Qt3DRender::Render::ShaderData *backend = manager->getOrCreateResource(frontend->id());
+ // Init the backend element
+ backend->setRenderer(renderer);
+ simulateInitializationSync(frontend, backend);
+ }
+
+ void initBackendTexture(Qt3DRender::QAbstractTexture *frontend,
+ Qt3DRender::Render::TextureManager *manager)
+ {
+ // Create backend element for frontend one
+ Qt3DRender::Render::Texture *backend = manager->getOrCreateResource(frontend->id());
+ // Init the backend element
+ simulateInitialization(frontend, backend);
+ }
+};
+
+class ScalarShaderData : public Qt3DRender::QShaderData
+{
+ Q_OBJECT
+ Q_PROPERTY(float scalar READ scalar WRITE setScalar NOTIFY scalarChanged)
+
+public:
+ ScalarShaderData(Qt3DCore::QNode *parent = nullptr)
+ : Qt3DRender::QShaderData(parent)
+ , m_scalar(0.0f)
+ {
+ }
+
+ void setScalar(float scalar)
+ {
+ if (scalar != m_scalar) {
+ m_scalar = scalar;
+ emit scalarChanged();
+ }
+ }
+
+ float scalar() const
+ {
+ return m_scalar;
+ }
+
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName)
+ {
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms;
+
+ uniforms.insert(blockName + QStringLiteral(".scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+
+ return uniforms;
+ }
+
+Q_SIGNALS:
+ void scalarChanged();
+
+private:
+ float m_scalar;
+};
+
+class TextureShaderData : public Qt3DRender::QShaderData
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt3DRender::QAbstractTexture* texture READ texture WRITE setTexture NOTIFY textureChanged)
+
+public:
+ TextureShaderData()
+ : Qt3DRender::QShaderData()
+ , m_texture(nullptr)
+ {
+ }
+
+ void setTexture(Qt3DRender::QAbstractTexture *texture)
+ {
+ if (texture != m_texture) {
+ m_texture = texture;
+ emit textureChanged();
+ }
+ }
+
+ Qt3DRender::QAbstractTexture *texture() const
+ {
+ return m_texture;
+ }
+
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName)
+ {
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms;
+
+ uniforms.insert(blockName + QStringLiteral(".texture"), Qt3DRender::Render::OpenGL::ShaderUniform());
+
+ return uniforms;
+ }
+
+Q_SIGNALS:
+ void textureChanged();
+
+private:
+ Qt3DRender::QAbstractTexture *m_texture;
+};
+
+
+class ArrayShaderData : public Qt3DRender::QShaderData
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariantList array READ array WRITE setArray NOTIFY arrayChanged)
+
+public:
+ ArrayShaderData()
+ : Qt3DRender::QShaderData()
+ {
+ }
+
+ void setArray(const QVariantList &array)
+ {
+ if (array != m_array) {
+ m_array = array;
+ emit arrayChanged();
+ }
+ }
+
+ QVariantList array() const
+ {
+ return m_array;
+ }
+
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName)
+ {
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms;
+
+ uniforms.insert(blockName + QStringLiteral(".array[0]"), Qt3DRender::Render::OpenGL::ShaderUniform());
+
+ return uniforms;
+ }
+
+Q_SIGNALS:
+ void arrayChanged();
+
+private:
+ QVariantList m_array;
+};
+
+class StructShaderData : public Qt3DRender::QShaderData
+{
+ Q_OBJECT
+ Q_PROPERTY(float scalar READ scalar WRITE setScalar NOTIFY scalarChanged)
+ Q_PROPERTY(QVariantList array READ array WRITE setArray NOTIFY arrayChanged)
+
+public:
+ StructShaderData()
+ : Qt3DRender::QShaderData()
+ , m_scalar(0.0f)
+ {
+ }
+
+ void setScalar(float scalar)
+ {
+ if (scalar != m_scalar) {
+ m_scalar = scalar;
+ emit scalarChanged();
+ }
+ }
+
+ float scalar() const
+ {
+ return m_scalar;
+ }
+
+ void setArray(const QVariantList &array)
+ {
+ if (array != m_array) {
+ m_array = array;
+ emit arrayChanged();
+ }
+ }
+
+ QVariantList array() const
+ {
+ return m_array;
+ }
+
+ virtual QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName)
+ {
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms;
+
+ uniforms.insert(blockName + QStringLiteral(".scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ uniforms.insert(blockName + QStringLiteral(".array[0]"), Qt3DRender::Render::OpenGL::ShaderUniform());
+
+ return uniforms;
+ }
+
+ virtual QHash<QString, QVariant> buildUniformMapValues(const QString &blockName)
+ {
+ QHash<QString, QVariant> uniforms;
+
+ uniforms.insert(blockName + QStringLiteral(".scalar"), QVariant(scalar()));
+ uniforms.insert(blockName + QStringLiteral(".array[0]"), QVariant(array()));
+
+ return uniforms;
+ }
+
+Q_SIGNALS:
+ void scalarChanged();
+ void arrayChanged();
+
+private:
+ float m_scalar;
+ QVariantList m_array;
+};
+
+class MultiLevelStructShaderData : public StructShaderData
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt3DRender::QShaderData *inner READ inner WRITE setInner NOTIFY innerChanged)
+
+public:
+ MultiLevelStructShaderData()
+ : StructShaderData()
+ , m_inner(nullptr)
+ {
+ }
+
+ void setInner(Qt3DRender::QShaderData *inner)
+ {
+ if (inner != m_inner) {
+ m_inner = inner;
+ emit innerChanged();
+ }
+ }
+
+ Qt3DRender::QShaderData *inner() const
+ {
+ return m_inner;
+ }
+
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> buildUniformMap(const QString &blockName) override
+ {
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> innerUniforms;
+
+ StructShaderData *innerData = nullptr;
+ if ((innerData = qobject_cast<StructShaderData *>(m_inner)) != nullptr)
+ innerUniforms = innerData->buildUniformMap(QStringLiteral(".inner"));
+
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform> uniforms = StructShaderData::buildUniformMap(blockName);
+ QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform>::const_iterator it = innerUniforms.begin();
+ const QHash<QString, Qt3DRender::Render::OpenGL::ShaderUniform>::const_iterator end = innerUniforms.end();
+
+ while (it != end) {
+ uniforms.insert(blockName + it.key(), it.value());
+ ++it;
+ }
+ return uniforms;
+ }
+
+ QHash<QString, QVariant> buildUniformMapValues(const QString &blockName) override
+ {
+ QHash<QString, QVariant> innerUniformsValues;
+
+ StructShaderData *innerData = nullptr;
+ if ((innerData = qobject_cast<StructShaderData *>(m_inner)) != nullptr)
+ innerUniformsValues = innerData->buildUniformMapValues(QStringLiteral(".inner"));
+
+ QHash<QString, QVariant> uniformsValues = StructShaderData::buildUniformMapValues(blockName);
+ QHash<QString, QVariant>::const_iterator it = innerUniformsValues.begin();
+ const QHash<QString, QVariant>::const_iterator end = innerUniformsValues.end();
+
+ while (it != end) {
+ uniformsValues.insert(blockName + it.key(), it.value());
+ ++it;
+ }
+
+ return uniformsValues;
+ }
+
+Q_SIGNALS:
+ void innerChanged();
+
+private:
+ Qt3DRender::QShaderData *m_inner;
+};
+
+void tst_RenderViewUtils::topLevelScalarValueNoUniforms()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<ScalarShaderData> shaderData(new ScalarShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ shaderData->setScalar(883.0f);
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEB
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral(""));
+
+ // THEN
+ // activeUniformNamesToValue should be empty as blockBuilder.uniforms is
+ QVERIFY(blockBuilder.activeUniformNamesToValue.isEmpty());
+}
+
+void tst_RenderViewUtils::topLevelScalarValue()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<ScalarShaderData> shaderData(new ScalarShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ shaderData->setScalar(883.0f);
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock"));
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral("MyBlock"));
+
+ // THEN
+ QVERIFY(blockBuilder.uniforms.count() == 1);
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin();
+ const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end();
+
+ while (it != end) {
+ // THEN
+ QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ QCOMPARE(it.value(), QVariant(shaderData->scalar()));
+ ++it;
+ }
+}
+
+void tst_RenderViewUtils::topLevelTextureValueNoUniforms()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<TextureShaderData> shaderData(new TextureShaderData);
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager);
+ QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D);
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ shaderData->setTexture(texture.data());
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEB
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral(""));
+
+ // THEN
+ // activeUniformNamesToValue should be empty as blockBuilder.uniforms is
+ QVERIFY(blockBuilder.activeUniformNamesToValue.isEmpty());
+}
+
+void tst_RenderViewUtils::topLevelTextureValue()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<TextureShaderData> shaderData(new TextureShaderData);
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager);
+ QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D);
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ initBackendTexture(texture.data(), textureManager.data());
+ shaderData->setTexture(texture.data());
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock"));
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral("MyBlock"));
+
+ // THEN
+ QVERIFY(blockBuilder.uniforms.count() == 1);
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin();
+ const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end();
+
+ while (it != end) {
+ // THEN
+ QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ QCOMPARE(it.value(), QVariant::fromValue(shaderData->texture()->id()));
+ ++it;
+ }
+}
+
+void tst_RenderViewUtils::topLevelArrayValue()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<ArrayShaderData> shaderData(new ArrayShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ QVariantList arrayValues = QVariantList() << 454 << 350 << 383 << 427 << 552;
+ shaderData->setArray(arrayValues);
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms = shaderData->buildUniformMap(QStringLiteral("MyBlock"));
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral("MyBlock"));
+
+ // THEN
+ QVERIFY(blockBuilder.uniforms.count() == 1);
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 1);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin();
+ const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end();
+
+ while (it != end) {
+ // THEN
+ QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ QCOMPARE(it.value(), QVariant(arrayValues));
+ ++it;
+ }
+}
+
+void tst_RenderViewUtils::nestedShaderDataValue()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<ArrayShaderData> arrayShaderData(new ArrayShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ QScopedPointer<ScalarShaderData> shaderData1(new ScalarShaderData(arrayShaderData.data()));
+ QScopedPointer<ScalarShaderData> shaderData2(new ScalarShaderData(arrayShaderData.data()));
+ QScopedPointer<ScalarShaderData> shaderData3(new ScalarShaderData(arrayShaderData.data()));
+
+ shaderData1->setScalar(883.0f);
+ shaderData2->setScalar(1200.0f);
+ shaderData3->setScalar(1340.0f);
+ QHash<QString, QVariant> scalarValues;
+ scalarValues[QStringLiteral("MyBlock.array[0].scalar")] = shaderData1->scalar();
+ scalarValues[QStringLiteral("MyBlock.array[1].scalar")] = shaderData2->scalar();
+ scalarValues[QStringLiteral("MyBlock.array[2].scalar")] = shaderData3->scalar();
+
+
+ const Qt3DCore::QNodeId id1 = shaderData1->id();
+ const Qt3DCore::QNodeId id2 = shaderData2->id();
+ const Qt3DCore::QNodeId id3 = shaderData3->id();
+
+ // WHEN
+ const QVariantList arrayValues = QVariantList() << QVariant::fromValue(id1) << QVariant::fromValue(id2) << QVariant::fromValue(id3);
+ arrayShaderData->setArray(arrayValues);
+ initBackendShaderData(&renderer, arrayShaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendArrayShaderData = manager->lookupResource(arrayShaderData->id());
+ Qt3DRender::Render::ShaderData *backendShaderData1 = manager->lookupResource(id1);
+ Qt3DRender::Render::ShaderData *backendShaderData2 = manager->lookupResource(id2);
+ Qt3DRender::Render::ShaderData *backendShaderData3 = manager->lookupResource(id3);
+ QVERIFY(backendArrayShaderData != nullptr);
+ QVERIFY(backendShaderData1 != nullptr);
+ QVERIFY(backendShaderData2 != nullptr);
+ QVERIFY(backendShaderData3 != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[0].scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[1].scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[2].scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendArrayShaderData, QStringLiteral("MyBlock"));
+
+ // THEN
+ QVERIFY(blockBuilder.uniforms.count() == 3);
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 3);
+
+ // WHEN
+ auto it = blockBuilder.uniforms.cbegin();
+ const auto end = blockBuilder.uniforms.cend();
+
+ while (it != end) {
+ // THEN
+ const int nameId = Qt3DRender::Render::StringToInt::lookupId(it.key());
+ QVERIFY(blockBuilder.activeUniformNamesToValue.contains(nameId));
+ QCOMPARE(blockBuilder.activeUniformNamesToValue[nameId], scalarValues.value(it.key()));
+ ++it;
+ }
+}
+
+void tst_RenderViewUtils::topLevelStructValue_data()
+{
+ QTest::addColumn<StructShaderData*>("shaderData");
+ QTest::addColumn<QString>("blockName");
+
+ QVariantList arrayValues2 = QVariantList() << 180 << 220 << 250 << 270 << 300 << 350 << 550;
+ QVariantList arrayValues = QVariantList() << 454 << 350 << 383 << 427 << 552;
+
+ MultiLevelStructShaderData *twoLevelsNestedShaderData = new MultiLevelStructShaderData();
+ MultiLevelStructShaderData *singleLevelShaderData = new MultiLevelStructShaderData();
+ StructShaderData *shaderData = new StructShaderData();
+
+ // Don't forget to set the parent so that initBackendShaderData
+ // properly initializes nested members
+ shaderData->setParent(singleLevelShaderData);
+ shaderData->setArray(arrayValues);
+ shaderData->setScalar(1584.0f);
+
+ singleLevelShaderData->setParent(twoLevelsNestedShaderData);
+ singleLevelShaderData->setInner(shaderData);
+ singleLevelShaderData->setScalar(1200.0f);
+ singleLevelShaderData->setArray(arrayValues2);
+
+ twoLevelsNestedShaderData->setInner(singleLevelShaderData);
+ twoLevelsNestedShaderData->setArray(arrayValues + arrayValues2);
+ twoLevelsNestedShaderData->setScalar(1340.0f);
+
+ QTest::newRow("simple struct") << shaderData << QStringLiteral("Block");
+ QTest::newRow("single level inner struct") << (StructShaderData *)singleLevelShaderData << QStringLiteral("Block");
+ QTest::newRow("tow level inner struct") << (StructShaderData *)twoLevelsNestedShaderData << QStringLiteral("Block");
+}
+
+void tst_RenderViewUtils::topLevelStructValue()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QFETCH(StructShaderData *, shaderData);
+ QFETCH(QString, blockName);
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ initBackendShaderData(&renderer, shaderData, manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms = shaderData->buildUniformMap(blockName);
+ const QHash<QString, QVariant> expectedValues = shaderData->buildUniformMapValues(blockName);
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, blockName);
+
+ // THEN
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), blockBuilder.uniforms.count());
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator it = blockBuilder.activeUniformNamesToValue.begin();
+ const Qt3DRender::Render::OpenGL::UniformBlockValueBuilderHash::const_iterator end = blockBuilder.activeUniformNamesToValue.end();
+
+ while (it != end) {
+ // THEN
+ QVERIFY(blockBuilder.uniforms.contains(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ QVERIFY(expectedValues.contains(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ QCOMPARE(it.value(), expectedValues.value(Qt3DRender::Render::StringToInt::lookupString(it.key())));
+ ++it;
+ }
+}
+
+void tst_RenderViewUtils::topLevelDynamicProperties()
+{
+ // GIVEN
+ TestRenderer renderer;
+ QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D);
+ QScopedPointer<Qt3DRender::Render::TextureManager> textureManager(new Qt3DRender::Render::TextureManager());
+
+ // WHEN
+ initBackendTexture(texture.data(), textureManager.data());
+ shaderData->setProperty("scalar", 883.0f);
+ shaderData->setProperty("array", QVariantList() << 454 << 350 << 383 << 427 << 552);
+ shaderData->setProperty("texture", QVariant::fromValue(texture.data()));
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+
+ // WHEN
+ Qt3DRender::Render::OpenGL::UniformBlockValueBuilder blockBuilder;
+ blockBuilder.shaderDataManager = manager.data();
+ blockBuilder.textureManager = textureManager.data();
+ blockBuilder.updatedPropertiesOnly = false;
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.scalar"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.array[0]"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ blockBuilder.uniforms.insert(QStringLiteral("MyBlock.texture"), Qt3DRender::Render::OpenGL::ShaderUniform());
+ // build name-value map
+ blockBuilder.buildActiveUniformNameValueMapStructHelper(backendShaderData, QStringLiteral("MyBlock"));
+
+ // THEN
+ QVERIFY(blockBuilder.uniforms.count() == 3);
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.count(), 3);
+
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.scalar")),
+ shaderData->property("scalar"));
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.array[0]")),
+ shaderData->property("array"));
+ QCOMPARE(blockBuilder.activeUniformNamesToValue.value(Qt3DRender::Render::StringToInt::lookupId("MyBlock.texture")),
+ QVariant::fromValue(texture->id()));
+}
+
+void tst_RenderViewUtils::transformedProperties()
+{
+ // GIVEN
+ QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData());
+ QScopedPointer<Qt3DRender::Render::ShaderDataManager> manager(new Qt3DRender::Render::ShaderDataManager());
+ TestRenderer renderer;
+
+ // WHEN
+ const Vector3D position = Vector3D(15.0f, -5.0f, 10.0f);
+ const QVector3D positionQt = convertToQVector3D(position);
+ Matrix4x4 worldMatrix;
+ {
+ QMatrix4x4 m;
+ m.translate(-3.0f, 2.0f, 7.5f);
+ worldMatrix = Matrix4x4(m);
+ }
+ Matrix4x4 viewMatrix;
+ {
+ QMatrix4x4 m;
+ m.translate(9.0f, 6.0f, 12.0f);
+ viewMatrix = Matrix4x4(m);
+ }
+
+ shaderData->setProperty("position0", positionQt);
+ shaderData->setProperty("position1", positionQt);
+ shaderData->setProperty("position2", positionQt);
+ shaderData->setProperty("position3", positionQt);
+ shaderData->setProperty("position1Transformed", Qt3DRender::Render::ShaderData::ModelToEye);
+ shaderData->setProperty("position2Transformed", Qt3DRender::Render::ShaderData::ModelToWorld);
+ shaderData->setProperty("position3Transformed", Qt3DRender::Render::ShaderData::ModelToWorldDirection);
+ initBackendShaderData(&renderer, shaderData.data(), manager.data());
+
+ // THEN
+ Qt3DRender::Render::ShaderData *backendShaderData = manager->lookupResource(shaderData->id());
+ QVERIFY(backendShaderData != nullptr);
+ QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position0")), Qt3DRender::Render::ShaderData::NoTransform);
+ QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position1")), Qt3DRender::Render::ShaderData::ModelToEye);
+ QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position2")), Qt3DRender::Render::ShaderData::ModelToWorld);
+ QCOMPARE(backendShaderData->propertyTransformType(QStringLiteral("position3")), Qt3DRender::Render::ShaderData::ModelToWorldDirection);
+
+ // WHEN
+ backendShaderData->updateWorldTransform(worldMatrix);
+ const Vector3D position1Value = backendShaderData->getTransformedProperty(QStringLiteral("position1"), viewMatrix).value<Vector3D>();
+ const Vector3D position2Value = backendShaderData->getTransformedProperty(QStringLiteral("position2"), viewMatrix).value<Vector3D>();
+ const Vector3D position3Value = backendShaderData->getTransformedProperty(QStringLiteral("position3"), viewMatrix).value<Vector3D>();
+ const QVariant position0Value = backendShaderData->getTransformedProperty(QStringLiteral("position0"), viewMatrix);
+
+ // THEN
+ QCOMPARE(position0Value, positionQt);
+ QCOMPARE(position1Value, viewMatrix * worldMatrix * position);
+ QCOMPARE(position2Value, worldMatrix * position);
+ QCOMPARE(position3Value, Vector3D((worldMatrix * Vector4D(position, 0.0f))));
+}
+
+void tst_RenderViewUtils::shouldNotifyDynamicPropertyChanges()
+{
+ // GIVEN
+ TestArbiter arbiter;
+ QScopedPointer<Qt3DRender::QShaderData> shaderData(new Qt3DRender::QShaderData());
+ arbiter.setArbiterOnNode(shaderData.data());
+
+ // WHEN
+ shaderData->setProperty("scalar", 883.0f);
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(arbiter.dirtyNodes.size(), 1);
+ QCOMPARE(arbiter.dirtyNodes.front(), shaderData.data());
+
+ arbiter.dirtyNodes.clear();
+
+ // WHEN
+ QScopedPointer<Qt3DRender::QAbstractTexture> texture(new Qt3DRender::QTexture2D);
+ shaderData->setProperty("texture", QVariant::fromValue(texture.data()));
+
+ // THEN
+ QCOMPARE(arbiter.events.size(), 0);
+ QCOMPARE(arbiter.dirtyNodes.size(), 1);
+ QCOMPARE(arbiter.dirtyNodes.front(), shaderData.data());
+}
+
+QTEST_MAIN(tst_RenderViewUtils)
+
+#include "tst_renderviewutils.moc"
diff --git a/tests/auto/render/opengl/textures/textures.pro b/tests/auto/render/opengl/textures/textures.pro
new file mode 100644
index 000000000..a116756af
--- /dev/null
+++ b/tests/auto/render/opengl/textures/textures.pro
@@ -0,0 +1,15 @@
+TEMPLATE = app
+
+TARGET = tst_textures
+
+QT += core-private 3dcore 3dcore-private 3drender 3drender-private testlib
+
+CONFIG += testcase
+
+SOURCES += tst_textures.cpp
+
+include(../../../core/common/common.pri)
+include(../../commons/commons.pri)
+
+# Link Against OpenGL Renderer Plugin
+include(../opengl_render_plugin.pri)
diff --git a/tests/auto/render/opengl/textures/tst_textures.cpp b/tests/auto/render/opengl/textures/tst_textures.cpp
new file mode 100644
index 000000000..5343a6fde
--- /dev/null
+++ b/tests/auto/render/opengl/textures/tst_textures.cpp
@@ -0,0 +1,821 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** 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
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** 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.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// TODO Remove in Qt6
+#include <QtCore/qcompilerdetection.h>
+QT_WARNING_DISABLE_DEPRECATED
+
+#include <QtTest/QTest>
+#include <qbackendnodetester.h>
+#include <qtextureimagedatagenerator.h>
+#include <Qt3DRender/qtexture.h>
+#include <Qt3DRender/qtextureimage.h>
+#include <Qt3DRender/qtexturedata.h>
+
+#include <renderer_p.h>
+#include <glresourcemanagers_p.h>
+#include <gltexture_p.h>
+#include <Qt3DRender/private/texture_p.h>
+#include <Qt3DRender/private/textureimage_p.h>
+#include <Qt3DRender/private/nodemanagers_p.h>
+#include <Qt3DRender/private/managers_p.h>
+#include <Qt3DRender/private/qtexture_p.h>
+
+#include <testrenderer.h>
+
+/**
+ * @brief Dummy QTextureImageDataGenerator
+ */
+class TestImageDataGenerator : public Qt3DRender::QTextureImageDataGenerator
+{
+ int m_id;
+public:
+ TestImageDataGenerator(int id) : m_id(id) {}
+
+ Qt3DRender::QTextureImageDataPtr operator ()() override {
+ return Qt3DRender::QTextureImageDataPtr::create();
+ }
+
+ bool operator ==(const Qt3DRender::QTextureImageDataGenerator &other) const override {
+ const TestImageDataGenerator *otherFunctor = Qt3DRender::functor_cast<TestImageDataGenerator>(&other);
+ return (otherFunctor != nullptr && otherFunctor->m_id == m_id);
+ }
+
+ QT3D_FUNCTOR(TestImageDataGenerator)
+};
+
+/**
+ * @brief Dummy QTextureGenerator
+ */
+class TestTextureGenerator : public Qt3DRender::QTextureGenerator
+{
+ int m_id;
+public:
+ TestTextureGenerator(int id) : m_id(id) {}
+
+ Qt3DRender::QTextureDataPtr operator ()() override {
+ return Qt3DRender::QTextureDataPtr::create();
+ }
+
+ bool operator ==(const Qt3DRender::QTextureGenerator &other) const override {
+ const TestTextureGenerator *otherFunctor = Qt3DRender::functor_cast<TestTextureGenerator>(&other);
+ return (otherFunctor != nullptr && otherFunctor->m_id == m_id);
+ }
+
+ QT3D_FUNCTOR(TestTextureGenerator)
+};
+
+typedef QSharedPointer<TestTextureGenerator> TestTextureGeneratorPtr;
+
+class TestTexturePrivate : public Qt3DRender::QAbstractTexturePrivate
+{
+public:
+ int genId;
+};
+
+/**
+ * @brief Test QTexture. Assign texture data functor if genId > 0.
+ */
+class TestTexture : public Qt3DRender::QAbstractTexture
+{
+public:
+ TestTexture(int genId, Qt3DCore::QNode *p = nullptr)
+ : QAbstractTexture(*new TestTexturePrivate(), p)
+ {
+ d_func()->genId = genId;
+ if (genId > 0)
+ d_func()->setDataFunctor(TestTextureGeneratorPtr::create(genId));
+ }
+private:
+ Q_DECLARE_PRIVATE(TestTexture)
+};
+
+class TestSharedGLTexturePrivate : public Qt3DRender::QAbstractTexturePrivate
+{
+};
+
+class TestSharedGLTexture : public Qt3DRender::QAbstractTexture
+{
+public:
+ TestSharedGLTexture(int textureId, Qt3DCore::QNode *p = nullptr)
+ : QAbstractTexture(*new TestSharedGLTexturePrivate(), p)
+ {
+ d_func()->m_sharedTextureId = textureId;
+ }
+
+private:
+ Q_DECLARE_PRIVATE(TestSharedGLTexture)
+};
+
+
+/**
+ * @brief Test QTextureImage
+ */
+class TestTextureImage : public Qt3DRender::QAbstractTextureImage
+{
+public:
+ TestTextureImage(int genId, Qt3DCore::QNode *p = nullptr)
+ : QAbstractTextureImage(p)
+ , m_genId(genId)
+ {
+ }
+
+ void updateGenerator()
+ {
+ Qt3DRender::QAbstractTextureImage::notifyDataGeneratorChanged();
+ }
+
+ Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const
+ {
+ return Qt3DRender::QTextureImageDataGeneratorPtr(new TestImageDataGenerator(m_genId));
+ }
+protected:
+ int m_genId;
+};
+
+class EmptyTextureImage : public Qt3DRender::QAbstractTextureImage
+{
+public:
+ EmptyTextureImage(Qt3DCore::QNode *p = nullptr)
+ : QAbstractTextureImage(p)
+ {
+ }
+
+ Qt3DRender::QTextureImageDataGeneratorPtr dataGenerator() const
+ {
+ return {};
+ }
+};
+
+class tst_RenderTextures : public Qt3DCore::QBackendNodeTester
+{
+ Q_OBJECT
+
+ Qt3DRender::QAbstractTexture *createQTexture(int genId,
+ const QVector<int> &imgGenIds,
+ bool genMipMaps)
+ {
+ TestTexture *tex = new TestTexture(genId);
+
+ for (int imgGen : imgGenIds)
+ tex->addTextureImage(new TestTextureImage(imgGen));
+ tex->setGenerateMipMaps(genMipMaps);
+
+ return tex;
+ }
+
+ Qt3DRender::QAbstractTexture *createQTextureWithTextureId(int textureId)
+ {
+ return new TestSharedGLTexture(textureId);
+ }
+
+ Qt3DRender::Render::Texture *createBackendTexture(Qt3DRender::QAbstractTexture *frontend,
+ Qt3DRender::Render::TextureManager *texMgr,
+ Qt3DRender::Render::TextureImageManager *texImgMgr,
+ Qt3DRender::Render::AbstractRenderer *renderer)
+ {
+ Qt3DRender::Render::Texture *backend = texMgr->getOrCreateResource(frontend->id());
+ backend->setRenderer(renderer);
+ simulateInitializationSync(frontend, backend);
+
+ // create texture images
+ for (const auto texImgFrontend : frontend->textureImages()) {
+ // make sure TextureImageManager has backend node for this QTextureImage
+ if (!texImgMgr->contains(texImgFrontend->id())) {
+ Qt3DRender::Render::TextureImage *texImgBackend = texImgMgr->getOrCreateResource(texImgFrontend->id());
+ texImgBackend->setRenderer(renderer);
+ simulateInitializationSync(texImgFrontend, texImgBackend);
+ }
+ }
+
+ return backend;
+ }
+
+private Q_SLOTS:
+
+ void shouldCreateSameGLTextures()
+ {
+ QSKIP("Texture Sharing is now disabled");
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTexture(-1, {1,2}, true);
+ Qt3DRender::QAbstractTexture *tex1b = createQTexture(-1, {1,2}, true);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1a = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ Qt3DRender::Render::Texture *bt1b = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ renderer.updateTexture(bt1a);
+ renderer.updateTexture(bt1b);
+
+ // THEN
+ QCOMPARE(renderer.glResourceManagers()->glTextureManager()->lookupResource(bt1a->peerId()),
+ renderer.glResourceManagers()->glTextureManager()->lookupResource(bt1b->peerId()));
+ renderer.shutdown();
+ }
+
+ void shouldCreateDifferentGLTexturess()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ // GIVEN
+ QVector<Qt3DRender::QAbstractTexture*> textures;
+ textures << createQTexture(-1, {1,2}, true);
+ textures << createQTexture(-1, {1,2}, false);
+ textures << createQTexture(1, {1,2}, true);
+ textures << createQTexture(1, {1,2}, false);
+ textures << createQTexture(1, {1,2,3}, true);
+ textures << createQTexture(1, {1,2,3}, false);
+
+ // WHEN
+ QVector<Qt3DRender::Render::Texture*> backend;
+ for (auto *t : textures) {
+ Qt3DRender::Render::Texture *backendTexture = createBackendTexture(t,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ backend.push_back(backendTexture);
+ renderer.updateTexture(backendTexture);
+ }
+
+ // THEN
+
+ // no 2 textures must be the same
+ for (int i = 0; i < backend.size(); i++)
+ for (int k = i+1; k < backend.size(); k++)
+ QVERIFY(renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[i]->peerId()) !=
+ renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[k]->peerId()));
+
+ QVector<Qt3DRender::Render::OpenGL::GLTexture *> glTextures;
+ for (Qt3DRender::Render::Texture *t : backend)
+ glTextures.push_back(renderer.glResourceManagers()->glTextureManager()->lookupResource(t->peerId()));
+
+ // some texture generators must be the same
+ QVERIFY(glTextures[0]->textureGenerator().data() == nullptr);
+ QVERIFY(glTextures[1]->textureGenerator().data() == nullptr);
+ QCOMPARE(*(glTextures[2]->textureGenerator()), *(glTextures[3]->textureGenerator()));
+
+ // some images must be the same
+ QCOMPARE(glTextures[0]->images(), glTextures[1]->images());
+ QCOMPARE(glTextures[0]->images(), glTextures[2]->images());
+ QCOMPARE(glTextures[0]->images(), glTextures[3]->images());
+ QCOMPARE(glTextures[4]->images(), glTextures[5]->images());
+
+ QCOMPARE(glTextures[0]->properties(), glTextures[2]->properties());
+ QCOMPARE(glTextures[1]->properties(), glTextures[3]->properties());
+ QVERIFY(glTextures[0]->properties() != glTextures[1]->properties());
+
+ renderer.shutdown();
+ }
+
+ void shouldCreateDifferentGLTexturesWhenUsingSharedTextureIds()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ Qt3DRender::Render::OpenGL::GLResourceManagers *glMgrs = renderer.glResourceManagers();
+
+ // both texture having the same sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(1);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 1);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::OpenGL::GLTexture *glt1 = glMgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::OpenGL::GLTexture *glt2 = glMgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ // textures having a different sharedTextureId
+ {
+ // GIVEN
+ Qt3DRender::QAbstractTexture *tex1a = createQTextureWithTextureId(1);
+ Qt3DRender::QAbstractTexture *tex1b = createQTextureWithTextureId(2);
+
+ // WHEN
+ Qt3DRender::Render::Texture *bt1 = createBackendTexture(tex1a,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ Qt3DRender::Render::Texture *bt2 = createBackendTexture(tex1b,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ // THEN
+ QCOMPARE(bt1->sharedTextureId(), 1);
+ QCOMPARE(bt2->sharedTextureId(), 2);
+
+ // WHEN
+ renderer.updateTexture(bt1);
+ renderer.updateTexture(bt2);
+
+ // THEN
+ Qt3DRender::Render::OpenGL::GLTexture *glt1 = glMgrs->glTextureManager()->lookupResource(bt1->peerId());
+ Qt3DRender::Render::OpenGL::GLTexture *glt2 = glMgrs->glTextureManager()->lookupResource(bt2->peerId());
+ QVERIFY(glt1 != glt2);
+ QCOMPARE(glt1->sharedTextureId(), bt1->sharedTextureId());
+ QCOMPARE(glt2->sharedTextureId(), bt2->sharedTextureId());
+ }
+
+ renderer.shutdown();
+ }
+
+ void generatorsShouldCreateSameData()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ renderer.setNodeManagers(mgrs.data());
+
+ // GIVEN
+ QVector<Qt3DRender::QAbstractTexture*> textures;
+ textures << createQTexture(1, {1}, true);
+ textures << createQTexture(2, {1,2}, true);
+ textures << createQTexture(1, {1,2}, true);
+
+ // WHEN
+ QVector<Qt3DRender::Render::Texture*> backend;
+ for (auto *t : textures) {
+ Qt3DRender::Render::Texture *backendTexture = createBackendTexture(t,
+ mgrs->textureManager(),
+ mgrs->textureImageManager(),
+ &renderer);
+ backend.push_back(backendTexture);
+ renderer.updateTexture(backendTexture);
+ }
+
+ Qt3DRender::QTextureImageDataGeneratorPtr idg1a = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[0]->peerId())->images()[0].generator;
+ Qt3DRender::QTextureImageDataGeneratorPtr idg1b = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[1]->peerId())->images()[0].generator;
+ Qt3DRender::QTextureImageDataGeneratorPtr idg2 = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[1]->peerId())->images()[1].generator;
+ Qt3DRender::QTextureGeneratorPtr tg1a = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[0]->peerId())->textureGenerator();
+ Qt3DRender::QTextureGeneratorPtr tg1b = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[2]->peerId())->textureGenerator();
+ Qt3DRender::QTextureGeneratorPtr tg2 = renderer.glResourceManagers()->glTextureManager()->lookupResource(backend[1]->peerId())->textureGenerator();
+
+ // THEN
+ QVERIFY(idg1a);
+ QVERIFY(idg1b);
+ QVERIFY(idg2);
+ QVERIFY(tg1a);
+ QVERIFY(tg1b);
+ QVERIFY(tg2);
+
+ QCOMPARE(*idg1a, *idg1b);
+ QVERIFY(!(*idg1a == *idg2));
+ QCOMPARE(*tg1a, *tg1b);
+ QVERIFY(!(*tg1a == *tg2));
+
+ renderer.shutdown();
+ }
+
+ void checkTextureImageInitialState()
+ {
+ // GIVEN
+ Qt3DRender::Render::TextureImage img;
+
+ // THEN
+ QCOMPARE(img.layer(), 0);
+ QCOMPARE(img.mipLevel(), 0);
+ QCOMPARE(img.isDirty(), false);
+ QCOMPARE(img.face(), Qt3DRender::QAbstractTexture::CubeMapPositiveX);
+ QVERIFY(img.dataGenerator().isNull());
+ }
+
+ void checkTextureImageCleanupState()
+ {
+ // GIVEN
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ TestTextureImage img(1);
+ img.setLayer(2);
+ img.setMipLevel(3);
+
+ // WHEN
+ Qt3DRender::Render::TextureImage texImgBackend;
+ texImgBackend.setRenderer(&renderer);
+ simulateInitializationSync(&img, &texImgBackend);
+ texImgBackend.cleanup();
+
+ // THEN
+ QCOMPARE(texImgBackend.isDirty(), false);
+ QCOMPARE(texImgBackend.layer(), 0);
+ QCOMPARE(texImgBackend.mipLevel(), 0);
+ QCOMPARE(texImgBackend.face(), Qt3DRender::QAbstractTexture::CubeMapPositiveX);
+ QVERIFY(texImgBackend.dataGenerator().isNull());
+
+ renderer.shutdown();
+ }
+
+ void checkTextureImageInitializeFromPeer()
+ {
+ // GIVEN
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ TestTextureImage img(1);
+
+ {
+ // WHEN
+ img.setLayer(2);
+ img.setMipLevel(3);
+
+ Qt3DRender::Render::TextureImage texImgBackend;
+ texImgBackend.setRenderer(&renderer);
+ simulateInitializationSync(&img, &texImgBackend);
+
+ // THEN
+ QCOMPARE(texImgBackend.isEnabled(), true);
+ QCOMPARE(texImgBackend.isDirty(), true);
+ QCOMPARE(texImgBackend.peerId(), img.id());
+ QCOMPARE(texImgBackend.layer(), 2);
+ QCOMPARE(texImgBackend.mipLevel(), 3);
+ QCOMPARE(texImgBackend.face(), Qt3DRender::QAbstractTexture::CubeMapPositiveX);
+ QVERIFY(!texImgBackend.dataGenerator().isNull());
+ }
+
+ {
+ // WHEN
+ img.setEnabled(false);
+
+ Qt3DRender::Render::TextureImage texImgBackend;
+ texImgBackend.setRenderer(&renderer);
+ simulateInitializationSync(&img, &texImgBackend);
+
+ // THEN
+ QCOMPARE(texImgBackend.isEnabled(), false);
+ QCOMPARE(texImgBackend.peerId(), img.id());
+ }
+
+ renderer.shutdown();
+ }
+
+ void checkTextureImageSceneChangeEvents()
+ {
+ // GIVEN
+ Qt3DRender::Render::TextureImage backendImage;
+ TestTextureImage textureImage(1);
+ TestRenderer renderer;
+ backendImage.setRenderer(&renderer);
+ simulateInitializationSync(&textureImage, &backendImage);
+
+ {
+ // WHEN
+ const bool newValue = false;
+ textureImage.setEnabled(newValue);
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+ // THEN
+ QCOMPARE(backendImage.isEnabled(), newValue);
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+ }
+
+ {
+ // WHEN
+ const int newValue = 7;
+ textureImage.setLayer(newValue);
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+ // THEN
+ QCOMPARE(backendImage.layer(), newValue);
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+ }
+
+ {
+ // WHEN
+ const int newValue = 3;
+ textureImage.setMipLevel(newValue);
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+
+ // THEN
+ QCOMPARE(backendImage.mipLevel(), newValue);
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+ }
+
+ {
+ // WHEN
+ const Qt3DRender::QAbstractTexture::CubeMapFace newValue = Qt3DRender::QAbstractTexture::CubeMapNegativeX;
+ textureImage.setFace(newValue);
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+ // THEN
+ QCOMPARE(backendImage.face(), newValue);
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+ }
+
+ {
+ // WHEN
+ textureImage.updateGenerator();
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+ // THEN
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+
+ // WHEN
+ textureImage.updateGenerator();
+ backendImage.syncFromFrontEnd(&textureImage, false);
+
+ // THEN
+ QVERIFY(backendImage.isDirty());
+ QVERIFY(renderer.dirtyBits() & Qt3DRender::Render::AbstractRenderer::AllDirty);
+ renderer.clearDirtyBits(Qt3DRender::Render::AbstractRenderer::AllDirty);
+ backendImage.unsetDirty();
+ }
+
+ renderer.shutdown();
+ }
+
+ void checkTextureImageProperlyReleaseGenerator()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
+ Qt3DRender::Render::TextureImageManager *texImgMgr = mgrs->textureImageManager();
+ renderer.setNodeManagers(mgrs.data());
+
+ // GIVEN
+ Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
+
+ Qt3DRender::Render::Texture *backendTexture = texMgr->getOrCreateResource(frontendTexture->id());
+ backendTexture->setRenderer(&renderer);
+ simulateInitializationSync(frontendTexture, backendTexture);
+
+ // THEN
+ QCOMPARE(backendTexture->textureImageIds().size(), 1);
+ QCOMPARE(frontendTexture->textureImages().size(), 1);
+
+ // WHEN
+ TestTextureImage *texImgFrontend = static_cast<TestTextureImage *>(frontendTexture->textureImages().first());
+ const Qt3DRender::QTextureImageDataGeneratorPtr frontendGenerator = texImgFrontend->dataGenerator();
+
+ // THEN
+ QVERIFY(!frontendGenerator.isNull());
+
+ // WHEN
+ Qt3DRender::Render::TextureImage *texImgBackend = texImgMgr->getOrCreateResource(texImgFrontend->id());
+ texImgBackend->setRenderer(&renderer);
+ simulateInitializationSync(texImgFrontend, texImgBackend);
+
+ // THEN
+ qDebug() << frontendGenerator << texImgBackend->dataGenerator();
+ const Qt3DRender::QTextureImageDataGeneratorPtr backendGenerator = texImgFrontend->dataGenerator();
+ QVERIFY(frontendGenerator != backendGenerator);
+ QVERIFY(*frontendGenerator == *backendGenerator);
+
+ renderer.shutdown();
+ }
+
+ void checkTextureIsMarkedForDeletion()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
+ renderer.setNodeManagers(mgrs.data());
+
+ Qt3DRender::Render::TextureFunctor textureBackendNodeMapper(&renderer,
+ texMgr);
+
+ // GIVEN
+ Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
+
+ Qt3DRender::Render::Texture *backendTexture = static_cast<Qt3DRender::Render::Texture *>(textureBackendNodeMapper.create(creationChange(frontendTexture)));
+ backendTexture->setRenderer(&renderer);
+ simulateInitializationSync(frontendTexture, backendTexture);
+
+ // THEN
+ QVERIFY(backendTexture != nullptr);
+ QCOMPARE(texMgr->textureIdsToCleanup().size(), 0);
+
+ QCOMPARE(texMgr->lookupResource(frontendTexture->id()), backendTexture);
+
+ // WHEN
+ textureBackendNodeMapper.destroy(frontendTexture->id());
+
+ // THEN
+ QCOMPARE(texMgr->textureIdsToCleanup().size(), 1);
+ QCOMPARE(texMgr->textureIdsToCleanup().first(), frontendTexture->id());
+ QVERIFY(texMgr->lookupResource(frontendTexture->id()) == nullptr);
+
+ renderer.shutdown();
+ }
+
+ void checkTextureDestructionReconstructionWithinSameLoop()
+ {
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
+ renderer.setNodeManagers(mgrs.data());
+
+ Qt3DRender::Render::TextureFunctor textureBackendNodeMapper(&renderer,
+ texMgr);
+
+ // GIVEN
+ Qt3DRender::QAbstractTexture* frontendTexture = createQTexture(1, {1}, true);
+
+ Qt3DRender::Render::Texture *backendTexture = static_cast<Qt3DRender::Render::Texture *>(textureBackendNodeMapper.create(creationChange(frontendTexture)));
+ backendTexture->setRenderer(&renderer);
+ simulateInitializationSync(frontendTexture, backendTexture);
+
+ // WHEN
+ textureBackendNodeMapper.destroy(frontendTexture->id());
+
+ // THEN
+ QCOMPARE(texMgr->textureIdsToCleanup().size(), 1);
+ QCOMPARE(texMgr->textureIdsToCleanup().first(), frontendTexture->id());
+ QVERIFY(texMgr->lookupResource(frontendTexture->id()) == nullptr);
+
+ // WHEN
+ backendTexture = static_cast<Qt3DRender::Render::Texture *>(textureBackendNodeMapper.create(creationChange(frontendTexture)));
+ backendTexture->setRenderer(&renderer);
+ simulateInitializationSync(frontendTexture, backendTexture);
+
+ // THEN
+ QVERIFY(backendTexture != nullptr);
+ QCOMPARE(texMgr->textureIdsToCleanup().size(), 0);
+ QCOMPARE(texMgr->lookupResource(frontendTexture->id()), backendTexture);
+
+ renderer.shutdown();
+ }
+
+ void checkTextureImageDirtinessPropagatesToTextures()
+ {
+ // GIVEN
+ QScopedPointer<Qt3DRender::Render::NodeManagers> mgrs(new Qt3DRender::Render::NodeManagers());
+ Qt3DRender::Render::OpenGL::Renderer renderer(Qt3DRender::QRenderAspect::Synchronous);
+ Qt3DRender::Render::TextureManager *texMgr = mgrs->textureManager();
+ Qt3DRender::Render::TextureImageManager *texImgMgr = mgrs->textureImageManager();
+ renderer.setNodeManagers(mgrs.data());
+
+ Qt3DRender::QTexture2D *texture1 = new Qt3DRender::QTexture2D();
+ TestTextureImage *image1 = new TestTextureImage(1);
+
+ Qt3DRender::QTexture2D *texture2 = new Qt3DRender::QTexture2D();
+ TestTextureImage *image2 = new TestTextureImage(2);
+
+ Qt3DRender::QTexture2D *texture3 = new Qt3DRender::QTexture2D();
+
+ texture1->addTextureImage(image1);
+ texture2->addTextureImage(image2);
+ texture3->addTextureImage(image1);
+ texture3->addTextureImage(image2);
+
+ Qt3DRender::Render::Texture *backendTexture1 = texMgr->getOrCreateResource(texture1->id());
+ Qt3DRender::Render::Texture *backendTexture2 = texMgr->getOrCreateResource(texture2->id());
+ Qt3DRender::Render::Texture *backendTexture3 = texMgr->getOrCreateResource(texture3->id());
+ Qt3DRender::Render::TextureImage *backendImage1 = texImgMgr->getOrCreateResource(image1->id());
+ Qt3DRender::Render::TextureImage *backendImage2 = texImgMgr->getOrCreateResource(image2->id());
+
+ backendTexture1->setRenderer(&renderer);
+ backendTexture2->setRenderer(&renderer);
+ backendTexture3->setRenderer(&renderer);
+ backendImage1->setRenderer(&renderer);
+ backendImage2->setRenderer(&renderer);
+
+ simulateInitializationSync(texture1, backendTexture1);
+ simulateInitializationSync(texture2, backendTexture2);
+ simulateInitializationSync(texture3, backendTexture3);
+ simulateInitializationSync(image1, backendImage1);
+ simulateInitializationSync(image2, backendImage2);
+
+ // THEN
+ QCOMPARE(backendTexture1->textureImageIds().size(), 1);
+ QCOMPARE(backendTexture1->textureImageIds().first(), image1->id());
+ QCOMPARE(backendTexture2->textureImageIds().size(), 1);
+ QCOMPARE(backendTexture2->textureImageIds().first(), image2->id());
+ QCOMPARE(backendTexture3->textureImageIds().size(), 2);
+ QCOMPARE(backendTexture3->textureImageIds().first(), image1->id());
+ QCOMPARE(backendTexture3->textureImageIds().last(), image2->id());
+
+ // WHEN
+ backendTexture1->unsetDirty();
+ backendTexture2->unsetDirty();
+ backendTexture3->unsetDirty();
+ backendImage1->unsetDirty();
+ backendImage2->unsetDirty();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ // Make Image1 dirty
+ image1->updateGenerator();
+ backendImage1->syncFromFrontEnd(image1, false);
+
+ // THEN
+ QVERIFY(backendImage1->isDirty());
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ // THEN
+ QVERIFY(backendTexture1->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+
+ backendImage1->unsetDirty();
+ backendTexture1->unsetDirty();
+ backendTexture3->unsetDirty();
+
+ // WHEN
+ image2->updateGenerator();
+ backendImage2->syncFromFrontEnd(image2, false);
+
+ // THEN
+ QVERIFY(backendImage2->isDirty());
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture3->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+
+ // WHEN
+ renderer.textureGathererJob()->run();
+
+ QVERIFY(backendTexture1->dirtyFlags() == Qt3DRender::Render::Texture::NotDirty);
+ QVERIFY(backendTexture2->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+ QVERIFY(backendTexture3->dirtyFlags() & Qt3DRender::Render::Texture::DirtyImageGenerators);
+
+ renderer.shutdown();
+ }
+};
+
+QTEST_MAIN(tst_RenderTextures)
+
+#include "tst_textures.moc"