| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite 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 <QtCore/QString> |
| #include <QtTest/QtTest> |
| |
| #include <QtGui/QOffscreenSurface> |
| #include <QtGui/QOpenGLContext> |
| #include <QtQuick/qsgnode.h> |
| #include <QtQuick/private/qsgbatchrenderer_p.h> |
| #include <QtQuick/private/qsgnodeupdater_p.h> |
| #include <QtQuick/private/qsgrenderloop_p.h> |
| #include <QtQuick/private/qsgcontext_p.h> |
| |
| #include <QtQuick/qsgsimplerectnode.h> |
| #include <QtQuick/qsgsimpletexturenode.h> |
| #include <QtQuick/private/qsgplaintexture_p.h> |
| |
| #include <QtGui/private/qguiapplication_p.h> |
| #include <QtGui/qpa/qplatformintegration.h> |
| |
| QT_BEGIN_NAMESPACE |
| inline bool operator==(const QSGGeometry::TexturedPoint2D& l, const QSGGeometry::TexturedPoint2D& r) |
| { |
| return l.x == r.x && l.y == r.y && l.tx == r.tx && l.ty == r.ty; |
| } |
| QT_END_NAMESPACE |
| |
| class NodesTest : public QObject |
| { |
| Q_OBJECT |
| |
| public: |
| NodesTest(); |
| |
| private Q_SLOTS: |
| void initTestCase(); |
| void cleanupTestCase(); |
| |
| // Root nodes |
| void propegate(); |
| void propegateWithMultipleRoots(); |
| |
| // Opacity nodes |
| void basicOpacityNode(); |
| void opacityPropegation(); |
| |
| void isBlockedCheck(); |
| |
| void textureNodeTextureOwnership(); |
| void textureNodeRect(); |
| |
| private: |
| QOffscreenSurface *surface = nullptr; |
| QOpenGLContext *context = nullptr; |
| QSGDefaultRenderContext *renderContext = nullptr; |
| }; |
| |
| void NodesTest::initTestCase() |
| { |
| if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) |
| QSKIP("OpenGL not supported by the platform"); |
| |
| QSGRenderLoop *renderLoop = QSGRenderLoop::instance(); |
| |
| surface = new QOffscreenSurface; |
| surface->create(); |
| QVERIFY(surface->isValid()); |
| |
| context = new QOpenGLContext(); |
| QVERIFY(context->create()); |
| QVERIFY(context->makeCurrent(surface)); |
| |
| auto rc = renderLoop->createRenderContext(renderLoop->sceneGraphContext()); |
| renderContext = static_cast<QSGDefaultRenderContext *>(rc); |
| QVERIFY(renderContext); |
| QSGDefaultRenderContext::InitParams rcParams; |
| rcParams.openGLContext = context; |
| rcParams.initialSurfacePixelSize = QSize(512, 512); // dummy, make up something |
| renderContext->initialize(&rcParams); |
| QVERIFY(renderContext->isValid()); |
| } |
| |
| void NodesTest::cleanupTestCase() |
| { |
| if (renderContext) |
| renderContext->invalidate(); |
| if (context) |
| context->doneCurrent(); |
| delete context; |
| delete surface; |
| } |
| |
| class DummyRenderer : public QSGBatchRenderer::Renderer |
| { |
| public: |
| DummyRenderer(QSGRootNode *root, QSGDefaultRenderContext *renderContext) |
| : QSGBatchRenderer::Renderer(renderContext) |
| , changedNode(nullptr) |
| , changedState(nullptr) |
| , renderCount(0) |
| { |
| setRootNode(root); |
| } |
| |
| void render() { |
| ++renderCount; |
| renderingOrder = ++globalRendereringOrder; |
| } |
| |
| void nodeChanged(QSGNode *node, QSGNode::DirtyState state) { |
| changedNode = node; |
| changedState = state; |
| QSGBatchRenderer::Renderer::nodeChanged(node, state); |
| } |
| |
| QSGNode *changedNode; |
| QSGNode::DirtyState changedState; |
| |
| int renderCount; |
| int renderingOrder; |
| static int globalRendereringOrder; |
| }; |
| |
| int DummyRenderer::globalRendereringOrder; |
| |
| NodesTest::NodesTest() |
| { |
| } |
| |
| void NodesTest::propegate() |
| { |
| QSGRootNode root; |
| QSGNode child; child.setFlag(QSGNode::OwnedByParent, false); |
| root.appendChildNode(&child); |
| |
| DummyRenderer renderer(&root, renderContext); |
| |
| child.markDirty(QSGNode::DirtyGeometry); |
| |
| QCOMPARE(&child, renderer.changedNode); |
| QCOMPARE((int) renderer.changedState, (int) QSGNode::DirtyGeometry); |
| } |
| |
| |
| void NodesTest::propegateWithMultipleRoots() |
| { |
| QSGRootNode root1; |
| QSGNode child2; child2.setFlag(QSGNode::OwnedByParent, false); |
| QSGRootNode root3; root3.setFlag(QSGNode::OwnedByParent, false); |
| QSGNode child4; child4.setFlag(QSGNode::OwnedByParent, false); |
| |
| root1.appendChildNode(&child2); |
| child2.appendChildNode(&root3); |
| root3.appendChildNode(&child4); |
| |
| DummyRenderer ren1(&root1, renderContext); |
| DummyRenderer ren2(&root3, renderContext); |
| |
| child4.markDirty(QSGNode::DirtyGeometry); |
| |
| QCOMPARE(ren1.changedNode, &child4); |
| QCOMPARE(ren2.changedNode, &child4); |
| |
| QCOMPARE((int) ren1.changedState, (int) QSGNode::DirtyGeometry); |
| QCOMPARE((int) ren2.changedState, (int) QSGNode::DirtyGeometry); |
| } |
| |
| void NodesTest::basicOpacityNode() |
| { |
| QSGOpacityNode n; |
| QCOMPARE(n.opacity(), 1.); |
| |
| n.setOpacity(0.5); |
| QCOMPARE(n.opacity(), 0.5); |
| |
| n.setOpacity(-1); |
| QCOMPARE(n.opacity(), 0.); |
| |
| n.setOpacity(2); |
| QCOMPARE(n.opacity(), 1.); |
| } |
| |
| void NodesTest::opacityPropegation() |
| { |
| QSGRootNode root; |
| QSGOpacityNode *a = new QSGOpacityNode; |
| QSGOpacityNode *b = new QSGOpacityNode; |
| QSGOpacityNode *c = new QSGOpacityNode; |
| |
| QSGSimpleRectNode *geometry = new QSGSimpleRectNode; |
| geometry->setRect(0, 0, 100, 100); |
| |
| DummyRenderer renderer(&root, renderContext); |
| |
| root.appendChildNode(a); |
| a->appendChildNode(b); |
| b->appendChildNode(c); |
| c->appendChildNode(geometry); |
| |
| a->setOpacity(0.9); |
| b->setOpacity(0.8); |
| c->setOpacity(0.7); |
| |
| renderer.renderScene(); |
| |
| QCOMPARE(a->combinedOpacity(), 0.9); |
| QCOMPARE(b->combinedOpacity(), 0.9 * 0.8); |
| QCOMPARE(c->combinedOpacity(), 0.9 * 0.8 * 0.7); |
| QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.8 * 0.7); |
| |
| b->setOpacity(0.1); |
| renderer.renderScene(); |
| |
| QCOMPARE(a->combinedOpacity(), 0.9); |
| QCOMPARE(b->combinedOpacity(), 0.9 * 0.1); |
| QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7); |
| QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7); |
| |
| b->setOpacity(0); |
| renderer.renderScene(); |
| |
| QVERIFY(b->isSubtreeBlocked()); |
| |
| // Verify that geometry did not get updated as it is in a blocked |
| // subtree |
| QCOMPARE(c->combinedOpacity(), 0.9 * 0.1 * 0.7); |
| QCOMPARE(geometry->inheritedOpacity(), 0.9 * 0.1 * 0.7); |
| } |
| |
| void NodesTest::isBlockedCheck() |
| { |
| QSGRootNode root; |
| QSGOpacityNode *opacity = new QSGOpacityNode(); |
| QSGNode *node = new QSGNode(); |
| |
| root.appendChildNode(opacity); |
| opacity->appendChildNode(node); |
| |
| QSGNodeUpdater updater; |
| |
| opacity->setOpacity(0); |
| QVERIFY(updater.isNodeBlocked(node, &root)); |
| |
| opacity->setOpacity(1); |
| QVERIFY(!updater.isNodeBlocked(node, &root)); |
| } |
| |
| void NodesTest::textureNodeTextureOwnership() |
| { |
| { // Check that it is not deleted by default |
| QPointer<QSGTexture> texture(new QSGPlainTexture()); |
| |
| QSGSimpleTextureNode *tn = new QSGSimpleTextureNode(); |
| QVERIFY(!tn->ownsTexture()); |
| |
| tn->setTexture(texture); |
| delete tn; |
| QVERIFY(!texture.isNull()); |
| } |
| |
| { // Check that it is deleted on destruction when we so desire |
| QPointer<QSGTexture> texture(new QSGPlainTexture()); |
| |
| QSGSimpleTextureNode *tn = new QSGSimpleTextureNode(); |
| tn->setOwnsTexture(true); |
| QVERIFY(tn->ownsTexture()); |
| |
| tn->setTexture(texture); |
| delete tn; |
| QVERIFY(texture.isNull()); |
| } |
| |
| { // Check that it is deleted on update when we so desire |
| QPointer<QSGTexture> oldTexture(new QSGPlainTexture()); |
| QPointer<QSGTexture> newTexture(new QSGPlainTexture()); |
| |
| QSGSimpleTextureNode *tn = new QSGSimpleTextureNode(); |
| tn->setOwnsTexture(true); |
| QVERIFY(tn->ownsTexture()); |
| |
| tn->setTexture(oldTexture); |
| QVERIFY(!oldTexture.isNull()); |
| QVERIFY(!newTexture.isNull()); |
| |
| tn->setTexture(newTexture); |
| QVERIFY(oldTexture.isNull()); |
| QVERIFY(!newTexture.isNull()); |
| |
| delete tn; |
| } |
| } |
| |
| void NodesTest::textureNodeRect() |
| { |
| QSGPlainTexture texture; |
| texture.setTextureSize(QSize(400, 400)); |
| QSGSimpleTextureNode tn; |
| tn.setTexture(&texture); |
| QSGGeometry::TexturedPoint2D *vertices = tn.geometry()->vertexDataAsTexturedPoint2D(); |
| |
| QSGGeometry::TexturedPoint2D topLeft, bottomLeft, topRight, bottomRight; |
| topLeft.set(0, 0, 0, 0); |
| bottomLeft.set(0, 0, 0, 1); |
| topRight.set(0, 0, 1, 0); |
| bottomRight.set(0, 0, 1, 1); |
| QCOMPARE(vertices[0], topLeft); |
| QCOMPARE(vertices[1], bottomLeft); |
| QCOMPARE(vertices[2], topRight); |
| QCOMPARE(vertices[3], bottomRight); |
| |
| tn.setRect(1, 2, 100, 100); |
| topLeft.set(1, 2, 0, 0); |
| bottomLeft.set(1, 102, 0, 1); |
| topRight.set(101, 2, 1, 0); |
| bottomRight.set(101, 102, 1, 1); |
| QCOMPARE(vertices[0], topLeft); |
| QCOMPARE(vertices[1], bottomLeft); |
| QCOMPARE(vertices[2], topRight); |
| QCOMPARE(vertices[3], bottomRight); |
| |
| tn.setRect(0, 0, 100, 100); |
| tn.setSourceRect(100, 100, 200, 200); |
| topLeft.set(0, 0, 0.25, 0.25); |
| bottomLeft.set(0, 100, 0.25, 0.75); |
| topRight.set(100, 0, 0.75, 0.25); |
| bottomRight.set(100, 100, 0.75, 0.75); |
| QCOMPARE(vertices[0], topLeft); |
| QCOMPARE(vertices[1], bottomLeft); |
| QCOMPARE(vertices[2], topRight); |
| QCOMPARE(vertices[3], bottomRight); |
| |
| tn.setSourceRect(300, 300, -200, -200); |
| topLeft.set(0, 0, 0.75, 0.75); |
| bottomLeft.set(0, 100, 0.75, 0.25); |
| topRight.set(100, 0, 0.25, 0.75); |
| bottomRight.set(100, 100, 0.25, 0.25); |
| QCOMPARE(vertices[0], topLeft); |
| QCOMPARE(vertices[1], bottomLeft); |
| QCOMPARE(vertices[2], topRight); |
| QCOMPARE(vertices[3], bottomRight); |
| } |
| |
| QTEST_MAIN(NodesTest); |
| |
| #include "tst_nodestest.moc" |