/**************************************************************************** ** ** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB). ** 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 #include using namespace Qt3DRender; namespace { QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) { auto port = QShaderNodePort(); port.direction = portDirection; port.name = portName; return port; } QShaderNode createNode(const QVector &ports, const QStringList &layers = QStringList()) { auto node = QShaderNode(); node.setUuid(QUuid::createUuid()); node.setLayers(layers); for (const auto &port : ports) node.addPort(port); return node; } QShaderGraph::Edge createEdge(const QUuid &sourceUuid, const QString &sourceName, const QUuid &targetUuid, const QString &targetName, const QStringList &layers = QStringList()) { auto edge = QShaderGraph::Edge(); edge.sourceNodeUuid = sourceUuid; edge.sourcePortName = sourceName; edge.targetNodeUuid = targetUuid; edge.targetPortName = targetName; edge.layers = layers; return edge; } QShaderGraph::Statement createStatement(const QShaderNode &node, const QVector &inputs = QVector(), const QVector &outputs = QVector()) { auto statement = QShaderGraph::Statement(); statement.node = node; statement.inputs = inputs; statement.outputs = outputs; return statement; } void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement) { qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs; } void dumpStatementsIfNeeded(const QVector &statements, const QVector &expected) { if (statements != expected) { for (int i = 0; i < qMax(statements.size(), expected.size()); i++) { qDebug() << "----" << i << "----"; if (i < statements.size()) debugStatement("A:", statements.at(i)); if (i < expected.size()) debugStatement("E:", expected.at(i)); qDebug() << "-----------"; } } } } class tst_QShaderGraph : public QObject { Q_OBJECT private slots: void shouldHaveEdgeDefaultState(); void shouldTestEdgesEquality_data(); void shouldTestEdgesEquality(); void shouldManipulateStatementMembers(); void shouldTestStatementsEquality_data(); void shouldTestStatementsEquality(); void shouldFindIndexFromPortNameInStatements_data(); void shouldFindIndexFromPortNameInStatements(); void shouldManageNodeList(); void shouldManageEdgeList(); void shouldSerializeGraphForCodeGeneration(); void shouldHandleUnboundPortsDuringGraphSerialization(); void shouldSurviveCyclesDuringGraphSerialization(); void shouldDealWithEdgesJumpingOverLayers(); void shouldGenerateDifferentStatementsDependingOnActiveLayers(); void shouldDealWithBranchesWithoutOutput(); }; void tst_QShaderGraph::shouldHaveEdgeDefaultState() { // GIVEN auto edge = QShaderGraph::Edge(); // THEN QVERIFY(edge.sourceNodeUuid.isNull()); QVERIFY(edge.sourcePortName.isEmpty()); QVERIFY(edge.targetNodeUuid.isNull()); QVERIFY(edge.targetPortName.isEmpty()); } void tst_QShaderGraph::shouldTestEdgesEquality_data() { QTest::addColumn("left"); QTest::addColumn("right"); QTest::addColumn("expected"); const auto sourceUuid1 = QUuid::createUuid(); const auto sourceUuid2 = QUuid::createUuid(); const auto targetUuid1 = QUuid::createUuid(); const auto targetUuid2 = QUuid::createUuid(); QTest::newRow("Equals") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << true; QTest::newRow("SourceUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << createEdge(sourceUuid2, "foo", targetUuid1, "bar") << false; QTest::newRow("SourceName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << createEdge(sourceUuid1, "bleh", targetUuid1, "bar") << false; QTest::newRow("TargetUuid") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << createEdge(sourceUuid1, "foo", targetUuid2, "bar") << false; QTest::newRow("TargetName") << createEdge(sourceUuid1, "foo", targetUuid1, "bar") << createEdge(sourceUuid1, "foo", targetUuid1, "bleh") << false; } void tst_QShaderGraph::shouldTestEdgesEquality() { // GIVEN QFETCH(QShaderGraph::Edge, left); QFETCH(QShaderGraph::Edge, right); // WHEN const auto equal = (left == right); const auto notEqual = (left != right); // THEN QFETCH(bool, expected); QCOMPARE(equal, expected); QCOMPARE(notEqual, !expected); } void tst_QShaderGraph::shouldManipulateStatementMembers() { // GIVEN auto statement = QShaderGraph::Statement(); // THEN (default state) QVERIFY(statement.inputs.isEmpty()); QVERIFY(statement.outputs.isEmpty()); QVERIFY(statement.node.uuid().isNull()); QVERIFY(statement.uuid().isNull()); // WHEN const auto node = createNode({}); statement.node = node; // THEN QCOMPARE(statement.uuid(), node.uuid()); // WHEN statement.node = QShaderNode(); // THEN QVERIFY(statement.uuid().isNull()); } void tst_QShaderGraph::shouldTestStatementsEquality_data() { QTest::addColumn("left"); QTest::addColumn("right"); QTest::addColumn("expected"); const auto node1 = createNode({}); const auto node2 = createNode({}); QTest::newRow("EqualNodes") << createStatement(node1, {1, 2}, {3, 4}) << createStatement(node1, {1, 2}, {3, 4}) << true; QTest::newRow("EqualInvalids") << createStatement(QShaderNode(), {1, 2}, {3, 4}) << createStatement(QShaderNode(), {1, 2}, {3, 4}) << true; QTest::newRow("Nodes") << createStatement(node1, {1, 2}, {3, 4}) << createStatement(node2, {1, 2}, {3, 4}) << false; QTest::newRow("Inputs") << createStatement(node1, {1, 2}, {3, 4}) << createStatement(node1, {1, 2, 0}, {3, 4}) << false; QTest::newRow("Outputs") << createStatement(node1, {1, 2}, {3, 4}) << createStatement(node1, {1, 2}, {3, 0, 4}) << false; } void tst_QShaderGraph::shouldTestStatementsEquality() { // GIVEN QFETCH(QShaderGraph::Statement, left); QFETCH(QShaderGraph::Statement, right); // WHEN const auto equal = (left == right); const auto notEqual = (left != right); // THEN QFETCH(bool, expected); QCOMPARE(equal, expected); QCOMPARE(notEqual, !expected); } void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements_data() { QTest::addColumn("statement"); QTest::addColumn("portName"); QTest::addColumn("expectedInputIndex"); QTest::addColumn("expectedOutputIndex"); const auto inputNodeStatement = createStatement(createNode({ createPort(QShaderNodePort::Output, "input") })); const auto outputNodeStatement = createStatement(createNode({ createPort(QShaderNodePort::Input, "output") })); const auto functionNodeStatement = createStatement(createNode({ createPort(QShaderNodePort::Input, "input1"), createPort(QShaderNodePort::Output, "output1"), createPort(QShaderNodePort::Input, "input2"), createPort(QShaderNodePort::Output, "output2"), createPort(QShaderNodePort::Output, "output3"), createPort(QShaderNodePort::Input, "input3") })); QTest::newRow("Invalid") << QShaderGraph::Statement() << "foo" << -1 << -1; QTest::newRow("InputNodeWrongName") << inputNodeStatement << "foo" << -1 << -1; QTest::newRow("InputNodeExistingName") << inputNodeStatement << "input" << -1 << 0; QTest::newRow("OutputNodeWrongName") << outputNodeStatement << "foo" << -1 << -1; QTest::newRow("OutputNodeExistingName") << outputNodeStatement << "output" << 0 << -1; QTest::newRow("FunctionNodeWrongName") << functionNodeStatement << "foo" << -1 << -1; QTest::newRow("FunctionNodeInput1") << functionNodeStatement << "input1" << 0 << -1; QTest::newRow("FunctionNodeOutput1") << functionNodeStatement << "output1" << -1 << 0; QTest::newRow("FunctionNodeInput2") << functionNodeStatement << "input2" << 1 << -1; QTest::newRow("FunctionNodeOutput2") << functionNodeStatement << "output2" << -1 << 1; QTest::newRow("FunctionNodeInput3") << functionNodeStatement << "input3" << 2 << -1; QTest::newRow("FunctionNodeOutput3") << functionNodeStatement << "output3" << -1 << 2; } void tst_QShaderGraph::shouldFindIndexFromPortNameInStatements() { // GIVEN QFETCH(QShaderGraph::Statement, statement); QFETCH(QString, portName); QFETCH(int, expectedInputIndex); QFETCH(int, expectedOutputIndex); // WHEN const auto inputIndex = statement.portIndex(QShaderNodePort::Input, portName); const auto outputIndex = statement.portIndex(QShaderNodePort::Output, portName); // THEN QCOMPARE(inputIndex, expectedInputIndex); QCOMPARE(outputIndex, expectedOutputIndex); } void tst_QShaderGraph::shouldManageNodeList() { // GIVEN const auto node1 = createNode({createPort(QShaderNodePort::Output, "node1")}); const auto node2 = createNode({createPort(QShaderNodePort::Output, "node2")}); auto graph = QShaderGraph(); // THEN (default state) QVERIFY(graph.nodes().isEmpty()); // WHEN graph.addNode(node1); // THEN QCOMPARE(graph.nodes().size(), 1); QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); // WHEN graph.addNode(node2); // THEN QCOMPARE(graph.nodes().size(), 2); QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid()); QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name); // WHEN graph.removeNode(node2); // THEN QCOMPARE(graph.nodes().size(), 1); QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); // WHEN graph.addNode(node2); // THEN QCOMPARE(graph.nodes().size(), 2); QCOMPARE(graph.nodes().at(0).uuid(), node1.uuid()); QCOMPARE(graph.nodes().at(0).ports().at(0).name, node1.ports().at(0).name); QCOMPARE(graph.nodes().at(1).uuid(), node2.uuid()); QCOMPARE(graph.nodes().at(1).ports().at(0).name, node2.ports().at(0).name); // WHEN const auto node1bis = [node1] { auto res = node1; auto port = res.ports().at(0); port.name = QStringLiteral("node1bis"); res.addPort(port); return res; }(); graph.addNode(node1bis); // THEN QCOMPARE(graph.nodes().size(), 2); QCOMPARE(graph.nodes().at(0).uuid(), node2.uuid()); QCOMPARE(graph.nodes().at(0).ports().at(0).name, node2.ports().at(0).name); QCOMPARE(graph.nodes().at(1).uuid(), node1bis.uuid()); QCOMPARE(graph.nodes().at(1).ports().at(0).name, node1bis.ports().at(0).name); } void tst_QShaderGraph::shouldManageEdgeList() { // GIVEN const auto edge1 = createEdge(QUuid::createUuid(), "foo", QUuid::createUuid(), "bar"); const auto edge2 = createEdge(QUuid::createUuid(), "baz", QUuid::createUuid(), "boo"); auto graph = QShaderGraph(); // THEN (default state) QVERIFY(graph.edges().isEmpty()); // WHEN graph.addEdge(edge1); // THEN QCOMPARE(graph.edges().size(), 1); QCOMPARE(graph.edges().at(0), edge1); // WHEN graph.addEdge(edge2); // THEN QCOMPARE(graph.edges().size(), 2); QCOMPARE(graph.edges().at(0), edge1); QCOMPARE(graph.edges().at(1), edge2); // WHEN graph.removeEdge(edge2); // THEN QCOMPARE(graph.edges().size(), 1); QCOMPARE(graph.edges().at(0), edge1); // WHEN graph.addEdge(edge2); // THEN QCOMPARE(graph.edges().size(), 2); QCOMPARE(graph.edges().at(0), edge1); QCOMPARE(graph.edges().at(1), edge2); // WHEN graph.addEdge(edge1); // THEN QCOMPARE(graph.edges().size(), 2); QCOMPARE(graph.edges().at(0), edge1); QCOMPARE(graph.edges().at(1), edge2); } void tst_QShaderGraph::shouldSerializeGraphForCodeGeneration() { // GIVEN const auto input1 = createNode({ createPort(QShaderNodePort::Output, "input1Value") }); const auto input2 = createNode({ createPort(QShaderNodePort::Output, "input2Value") }); const auto output1 = createNode({ createPort(QShaderNodePort::Input, "output1Value") }); const auto output2 = createNode({ createPort(QShaderNodePort::Input, "output2Value") }); const auto function1 = createNode({ createPort(QShaderNodePort::Input, "function1Input"), createPort(QShaderNodePort::Output, "function1Output") }); const auto function2 = createNode({ createPort(QShaderNodePort::Input, "function2Input1"), createPort(QShaderNodePort::Input, "function2Input2"), createPort(QShaderNodePort::Output, "function2Output") }); const auto function3 = createNode({ createPort(QShaderNodePort::Input, "function3Input1"), createPort(QShaderNodePort::Input, "function3Input2"), createPort(QShaderNodePort::Output, "function3Output1"), createPort(QShaderNodePort::Output, "function3Output2") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(input1); res.addNode(input2); res.addNode(output1); res.addNode(output2); res.addNode(function1); res.addNode(function2); res.addNode(function3); res.addEdge(createEdge(input1.uuid(), "input1Value", function1.uuid(), "function1Input")); res.addEdge(createEdge(input1.uuid(), "input1Value", function2.uuid(), "function2Input1")); res.addEdge(createEdge(input2.uuid(), "input2Value", function2.uuid(), "function2Input2")); res.addEdge(createEdge(function1.uuid(), "function1Output", function3.uuid(), "function3Input1")); res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input2")); res.addEdge(createEdge(function3.uuid(), "function3Output1", output1.uuid(), "output1Value")); res.addEdge(createEdge(function3.uuid(), "function3Output2", output2.uuid(), "output2Value")); return res; }(); // WHEN const auto statements = graph.createStatements(); // THEN const auto expected = QVector() << createStatement(input2, {}, {1}) << createStatement(input1, {}, {0}) << createStatement(function2, {0, 1}, {3}) << createStatement(function1, {0}, {2}) << createStatement(function3, {2, 3}, {4, 5}) << createStatement(output2, {5}, {}) << createStatement(output1, {4}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } void tst_QShaderGraph::shouldHandleUnboundPortsDuringGraphSerialization() { // GIVEN const auto input = createNode({ createPort(QShaderNodePort::Output, "input") }); const auto unboundInput = createNode({ createPort(QShaderNodePort::Output, "unbound") }); const auto output = createNode({ createPort(QShaderNodePort::Input, "output") }); const auto unboundOutput = createNode({ createPort(QShaderNodePort::Input, "unbound") }); const auto function = createNode({ createPort(QShaderNodePort::Input, "functionInput1"), createPort(QShaderNodePort::Input, "functionInput2"), createPort(QShaderNodePort::Input, "functionInput3"), createPort(QShaderNodePort::Output, "functionOutput1"), createPort(QShaderNodePort::Output, "functionOutput2"), createPort(QShaderNodePort::Output, "functionOutput3") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(input); res.addNode(unboundInput); res.addNode(output); res.addNode(unboundOutput); res.addNode(function); res.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput2")); res.addEdge(createEdge(function.uuid(), "functionOutput2", output.uuid(), "output")); return res; }(); // WHEN const auto statements = graph.createStatements(); // THEN // Note that no statement has any unbound input const auto expected = QVector() << createStatement(input, {}, {0}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } void tst_QShaderGraph::shouldSurviveCyclesDuringGraphSerialization() { // GIVEN const auto input = createNode({ createPort(QShaderNodePort::Output, "input") }); const auto output = createNode({ createPort(QShaderNodePort::Input, "output") }); const auto function1 = createNode({ createPort(QShaderNodePort::Input, "function1Input1"), createPort(QShaderNodePort::Input, "function1Input2"), createPort(QShaderNodePort::Output, "function1Output") }); const auto function2 = createNode({ createPort(QShaderNodePort::Input, "function2Input"), createPort(QShaderNodePort::Output, "function2Output") }); const auto function3 = createNode({ createPort(QShaderNodePort::Input, "function3Input"), createPort(QShaderNodePort::Output, "function3Output") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(input); res.addNode(output); res.addNode(function1); res.addNode(function2); res.addNode(function3); res.addEdge(createEdge(input.uuid(), "input", function1.uuid(), "function1Input1")); res.addEdge(createEdge(function1.uuid(), "function1Output", function2.uuid(), "function2Input")); res.addEdge(createEdge(function2.uuid(), "function2Output", function3.uuid(), "function3Input")); res.addEdge(createEdge(function3.uuid(), "function3Output", function1.uuid(), "function1Input2")); res.addEdge(createEdge(function2.uuid(), "function2Output", output.uuid(), "output")); return res; }(); // WHEN const auto statements = graph.createStatements(); // THEN // The cycle is ignored const auto expected = QVector(); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } void tst_QShaderGraph::shouldDealWithEdgesJumpingOverLayers() { // GIVEN const auto worldPosition = createNode({ createPort(QShaderNodePort::Output, "worldPosition") }); const auto texture = createNode({ createPort(QShaderNodePort::Output, "texture") }); const auto texCoord = createNode({ createPort(QShaderNodePort::Output, "texCoord") }); const auto lightIntensity = createNode({ createPort(QShaderNodePort::Output, "lightIntensity") }); const auto exposure = createNode({ createPort(QShaderNodePort::Output, "exposure") }); const auto fragColor = createNode({ createPort(QShaderNodePort::Input, "fragColor") }); const auto sampleTexture = createNode({ createPort(QShaderNodePort::Input, "sampler"), createPort(QShaderNodePort::Input, "coord"), createPort(QShaderNodePort::Output, "color") }); const auto lightFunction = createNode({ createPort(QShaderNodePort::Input, "baseColor"), createPort(QShaderNodePort::Input, "position"), createPort(QShaderNodePort::Input, "lightIntensity"), createPort(QShaderNodePort::Output, "outputColor") }); const auto exposureFunction = createNode({ createPort(QShaderNodePort::Input, "inputColor"), createPort(QShaderNodePort::Input, "exposure"), createPort(QShaderNodePort::Output, "outputColor") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(worldPosition); res.addNode(texture); res.addNode(texCoord); res.addNode(lightIntensity); res.addNode(exposure); res.addNode(fragColor); res.addNode(sampleTexture); res.addNode(lightFunction); res.addNode(exposureFunction); res.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler")); res.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord")); res.addEdge(createEdge(worldPosition.uuid(), "worldPosition", lightFunction.uuid(), "position")); res.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor")); res.addEdge(createEdge(lightIntensity.uuid(), "lightIntensity", lightFunction.uuid(), "lightIntensity")); res.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor")); res.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure")); res.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor")); return res; }(); // WHEN const auto statements = graph.createStatements(); // THEN const auto expected = QVector() << createStatement(texCoord, {}, {2}) << createStatement(texture, {}, {1}) << createStatement(lightIntensity, {}, {3}) << createStatement(sampleTexture, {1, 2}, {5}) << createStatement(worldPosition, {}, {0}) << createStatement(exposure, {}, {4}) << createStatement(lightFunction, {5, 0, 3}, {6}) << createStatement(exposureFunction, {6, 4}, {7}) << createStatement(fragColor, {7}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } void tst_QShaderGraph::shouldGenerateDifferentStatementsDependingOnActiveLayers() { // GIVEN const auto texCoord = createNode({ createPort(QShaderNodePort::Output, "texCoord") }, { "diffuseTexture", "normalTexture" }); const auto diffuseUniform = createNode({ createPort(QShaderNodePort::Output, "color") }, {"diffuseUniform"}); const auto diffuseTexture = createNode({ createPort(QShaderNodePort::Input, "coord"), createPort(QShaderNodePort::Output, "color") }, {"diffuseTexture"}); const auto normalUniform = createNode({ createPort(QShaderNodePort::Output, "normal") }, {"normalUniform"}); const auto normalTexture = createNode({ createPort(QShaderNodePort::Input, "coord"), createPort(QShaderNodePort::Output, "normal") }, {"normalTexture"}); const auto lightFunction = createNode({ createPort(QShaderNodePort::Input, "color"), createPort(QShaderNodePort::Input, "normal"), createPort(QShaderNodePort::Output, "output") }); const auto fragColor = createNode({ createPort(QShaderNodePort::Input, "fragColor") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(texCoord); res.addNode(diffuseUniform); res.addNode(diffuseTexture); res.addNode(normalUniform); res.addNode(normalTexture); res.addNode(lightFunction); res.addNode(fragColor); res.addEdge(createEdge(diffuseUniform.uuid(), "color", lightFunction.uuid(), "color", {"diffuseUniform"})); res.addEdge(createEdge(texCoord.uuid(), "texCoord", diffuseTexture.uuid(), "coord", {"diffuseTexture"})); res.addEdge(createEdge(diffuseTexture.uuid(), "color", lightFunction.uuid(), "color", {"diffuseTexture"})); res.addEdge(createEdge(normalUniform.uuid(), "normal", lightFunction.uuid(), "normal", {"normalUniform"})); res.addEdge(createEdge(texCoord.uuid(), "texCoord", normalTexture.uuid(), "coord", {"normalTexture"})); res.addEdge(createEdge(normalTexture.uuid(), "normal", lightFunction.uuid(), "normal", {"normalTexture"})); res.addEdge(createEdge(lightFunction.uuid(), "output", fragColor.uuid(), "fragColor")); return res; }(); { // WHEN const auto statements = graph.createStatements({"diffuseUniform", "normalUniform"}); // THEN const auto expected = QVector() << createStatement(normalUniform, {}, {1}) << createStatement(diffuseUniform, {}, {0}) << createStatement(lightFunction, {0, 1}, {2}) << createStatement(fragColor, {2}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } { // WHEN const auto statements = graph.createStatements({"diffuseUniform", "normalTexture"}); // THEN const auto expected = QVector() << createStatement(texCoord, {}, {0}) << createStatement(normalTexture, {0}, {2}) << createStatement(diffuseUniform, {}, {1}) << createStatement(lightFunction, {1, 2}, {3}) << createStatement(fragColor, {3}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } { // WHEN const auto statements = graph.createStatements({"diffuseTexture", "normalUniform"}); // THEN const auto expected = QVector() << createStatement(texCoord, {}, {0}) << createStatement(normalUniform, {}, {2}) << createStatement(diffuseTexture, {0}, {1}) << createStatement(lightFunction, {1, 2}, {3}) << createStatement(fragColor, {3}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } { // WHEN const auto statements = graph.createStatements({"diffuseTexture", "normalTexture"}); // THEN const auto expected = QVector() << createStatement(texCoord, {}, {0}) << createStatement(normalTexture, {0}, {2}) << createStatement(diffuseTexture, {0}, {1}) << createStatement(lightFunction, {1, 2}, {3}) << createStatement(fragColor, {3}, {}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } } void tst_QShaderGraph::shouldDealWithBranchesWithoutOutput() { // GIVEN const auto input = createNode({ createPort(QShaderNodePort::Output, "input") }); const auto output = createNode({ createPort(QShaderNodePort::Input, "output") }); const auto danglingFunction = createNode({ createPort(QShaderNodePort::Input, "functionInput"), createPort(QShaderNodePort::Output, "unbound") }); const auto function = createNode({ createPort(QShaderNodePort::Input, "functionInput"), createPort(QShaderNodePort::Output, "functionOutput") }); const auto graph = [=] { auto res = QShaderGraph(); res.addNode(input); res.addNode(function); res.addNode(danglingFunction); res.addNode(output); res.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput")); res.addEdge(createEdge(input.uuid(), "input", danglingFunction.uuid(), "functionInput")); res.addEdge(createEdge(function.uuid(), "functionOutput", output.uuid(), "output")); return res; }(); // WHEN const auto statements = graph.createStatements(); // THEN // Note that no edge leads to the unbound input const auto expected = QVector() << createStatement(input, {}, {0}) << createStatement(function, {0}, {1}) << createStatement(output, {1}, {}) << createStatement(danglingFunction, {0}, {2}); dumpStatementsIfNeeded(statements, expected); QCOMPARE(statements, expected); } QTEST_MAIN(tst_QShaderGraph) #include "tst_qshadergraph.moc"