diff options
Diffstat (limited to 'tests/auto/render/opengl')
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, ¤tProg); + QVERIFY(currentProg == 0); + + // WHEN + m_glHelper.useProgram(shaderProgram.programId()); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + QCOMPARE(GLuint(currentProg), shaderProgram.programId()); + + // WHEN + m_glHelper.useProgram(0); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + 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, ¤tProg); + QVERIFY(currentProg == 0); + + // WHEN + m_glHelper.useProgram(shaderProgram.programId()); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + QCOMPARE(GLuint(currentProg), shaderProgram.programId()); + + // WHEN + m_glHelper.useProgram(0); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + 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, ¤tProg); + QVERIFY(currentProg == 0); + + // WHEN + m_glHelper.useProgram(shaderProgram.programId()); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + QCOMPARE(GLuint(currentProg), shaderProgram.programId()); + + // WHEN + m_glHelper.useProgram(0); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + 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, ¤tProg); + QVERIFY(currentProg == 0); + + // WHEN + m_glHelper.useProgram(shaderProgram.programId()); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + QCOMPARE(GLuint(currentProg), shaderProgram.programId()); + + // WHEN + m_glHelper.useProgram(0); + + // THEN + m_func->glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProg); + 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" |