diff options
Diffstat (limited to 'tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp')
-rw-r--r-- | tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp | 625 |
1 files changed, 625 insertions, 0 deletions
diff --git a/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp b/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp new file mode 100644 index 0000000000..761e03a195 --- /dev/null +++ b/tests/auto/gui/util/qshadergraphloader/tst_qshadergraphloader.cpp @@ -0,0 +1,625 @@ +/**************************************************************************** +** +** 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 <QtTest/QtTest> + +#include <QtCore/qbuffer.h> + +#include <QtGui/private/qshadergraphloader_p.h> +#include <QtGui/private/qshaderlanguage_p.h> + +using QBufferPointer = QSharedPointer<QBuffer>; +Q_DECLARE_METATYPE(QBufferPointer); + +using PrototypeHash = QHash<QString, QShaderNode>; +Q_DECLARE_METATYPE(PrototypeHash); + +namespace +{ + QBufferPointer createBuffer(const QByteArray &data, QIODevice::OpenMode openMode = QIODevice::ReadOnly) + { + auto buffer = QBufferPointer::create(); + buffer->setData(data); + if (openMode != QIODevice::NotOpen) + buffer->open(openMode); + return buffer; + } + + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion) + { + auto format = QShaderFormat(); + format.setApi(api); + format.setVersion(QVersionNumber(majorVersion, minorVersion)); + return format; + } + + QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) + { + auto port = QShaderNodePort(); + port.direction = portDirection; + port.name = portName; + return port; + } + + QShaderNode createNode(const QVector<QShaderNodePort> &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 createGraph() + { + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + + auto graph = QShaderGraph(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "value") + }); + worldPosition.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}")); + worldPosition.setParameter("name", "worldPosition"); + worldPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Input)); + worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Vec3)); + worldPosition.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;", + QByteArrayList() << "$qualifier highp $type $name;")); + worldPosition.addRule(openGL3, QShaderNode::Rule("$type $value = $name;", + QByteArrayList() << "$qualifier $type $name;")); + + auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + texture.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}")); + texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + + auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + texCoord.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}")); + texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;", + QByteArrayList() << "varying highp vec2 texCoord;")); + texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;", + QByteArrayList() << "in vec2 texCoord;")); + + auto lightIntensity = createNode({ + createPort(QShaderNodePort::Output, "value") + }); + lightIntensity.setUuid(QUuid("{00000000-0000-0000-0000-000000000004}")); + lightIntensity.setParameter("name", "defaultName"); + lightIntensity.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Uniform)); + lightIntensity.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Float)); + lightIntensity.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;", + QByteArrayList() << "$qualifier highp $type $name;")); + lightIntensity.addRule(openGL3, QShaderNode::Rule("$type $value = $name;", + QByteArrayList() << "$qualifier $type $name;")); + + auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + exposure.setUuid(QUuid("{00000000-0000-0000-0000-000000000005}")); + exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;", + QByteArrayList() << "uniform highp float exposure;")); + exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;", + QByteArrayList() << "uniform float exposure;")); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.setUuid(QUuid("{00000000-0000-0000-0000-000000000006}")); + fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + + auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + sampleTexture.setUuid(QUuid("{00000000-0000-0000-0000-000000000007}")); + sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);")); + sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);")); + + auto lightFunction = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + lightFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000008}")); + lightFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include es2/lightmodel.frag.inc")); + lightFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc")); + + auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + exposureFunction.setUuid(QUuid("{00000000-0000-0000-0000-000000000009}")); + exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + + graph.addNode(worldPosition); + graph.addNode(texture); + graph.addNode(texCoord); + graph.addNode(lightIntensity); + graph.addNode(exposure); + graph.addNode(fragColor); + graph.addNode(sampleTexture); + graph.addNode(lightFunction); + graph.addNode(exposureFunction); + + graph.addEdge(createEdge(texture.uuid(), "texture", sampleTexture.uuid(), "sampler")); + graph.addEdge(createEdge(texCoord.uuid(), "texCoord", sampleTexture.uuid(), "coord")); + + graph.addEdge(createEdge(worldPosition.uuid(), "value", lightFunction.uuid(), "position")); + graph.addEdge(createEdge(sampleTexture.uuid(), "color", lightFunction.uuid(), "baseColor")); + graph.addEdge(createEdge(lightIntensity.uuid(), "value", lightFunction.uuid(), "lightIntensity")); + + graph.addEdge(createEdge(lightFunction.uuid(), "outputColor", exposureFunction.uuid(), "inputColor")); + graph.addEdge(createEdge(exposure.uuid(), "exposure", exposureFunction.uuid(), "exposure")); + + graph.addEdge(createEdge(exposureFunction.uuid(), "outputColor", fragColor.uuid(), "fragColor")); + + return graph; + } + + void debugStatement(const QString &prefix, const QShaderGraph::Statement &statement) + { + qDebug() << prefix << statement.inputs << statement.uuid().toString() << statement.outputs; + } + + void dumpStatementsIfNeeded(const QVector<QShaderGraph::Statement> &statements, const QVector<QShaderGraph::Statement> &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_QShaderGraphLoader : public QObject +{ + Q_OBJECT +private slots: + void shouldManipulateLoaderMembers(); + void shouldLoadFromJsonStream_data(); + void shouldLoadFromJsonStream(); +}; + +void tst_QShaderGraphLoader::shouldManipulateLoaderMembers() +{ + // GIVEN + auto loader = QShaderGraphLoader(); + + // THEN (default state) + QCOMPARE(loader.status(), QShaderGraphLoader::Null); + QVERIFY(!loader.device()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + QVERIFY(loader.prototypes().isEmpty()); + + // WHEN + auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen); + loader.setDevice(device1.data()); + + // THEN + QCOMPARE(loader.status(), QShaderGraphLoader::Error); + QCOMPARE(loader.device(), device1.data()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + + // WHEN + auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly); + loader.setDevice(device2.data()); + + // THEN + QCOMPARE(loader.status(), QShaderGraphLoader::Waiting); + QCOMPARE(loader.device(), device2.data()); + QVERIFY(loader.graph().nodes().isEmpty()); + QVERIFY(loader.graph().edges().isEmpty()); + + + // WHEN + const auto prototypes = [this]{ + auto res = QHash<QString, QShaderNode>(); + res.insert("foo", createNode({})); + return res; + }(); + loader.setPrototypes(prototypes); + + // THEN + QCOMPARE(loader.prototypes().size(), prototypes.size()); + QVERIFY(loader.prototypes().contains("foo")); + QCOMPARE(loader.prototypes().value("foo").uuid(), prototypes.value("foo").uuid()); +} + +void tst_QShaderGraphLoader::shouldLoadFromJsonStream_data() +{ + QTest::addColumn<QBufferPointer>("device"); + QTest::addColumn<PrototypeHash>("prototypes"); + QTest::addColumn<QShaderGraph>("graph"); + QTest::addColumn<QShaderGraphLoader::Status>("status"); + + QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << PrototypeHash() + << QShaderGraph() << QShaderGraphLoader::Error; + + const auto smallJson = "{" + " \"nodes\": [" + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"type\": \"MyInput\"," + " \"layers\": [\"foo\", \"bar\"]" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"type\": \"MyOutput\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"type\": \"MyFunction\"" + " }" + " ]," + " \"edges\": [" + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"sourcePort\": \"input\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"targetPort\": \"functionInput\"," + " \"layers\": [\"bar\", \"baz\"]" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"sourcePort\": \"functionOutput\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"targetPort\": \"output\"" + " }" + " ]" + "}"; + + const auto smallProtos = [this]{ + auto protos = PrototypeHash(); + + auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }); + protos.insert("MyInput", input); + + auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + protos.insert("MyOutput", output); + + auto function = createNode({ + createPort(QShaderNodePort::Input, "functionInput"), + createPort(QShaderNodePort::Output, "functionOutput") + }); + protos.insert("MyFunction", function); + return protos; + }(); + + const auto smallGraph = [this]{ + auto graph = QShaderGraph(); + + auto input = createNode({ + createPort(QShaderNodePort::Output, "input") + }, {"foo", "bar"}); + input.setUuid(QUuid("{00000000-0000-0000-0000-000000000001}")); + auto output = createNode({ + createPort(QShaderNodePort::Input, "output") + }); + output.setUuid(QUuid("{00000000-0000-0000-0000-000000000002}")); + auto function = createNode({ + createPort(QShaderNodePort::Input, "functionInput"), + createPort(QShaderNodePort::Output, "functionOutput") + }); + function.setUuid(QUuid("{00000000-0000-0000-0000-000000000003}")); + + graph.addNode(input); + graph.addNode(output); + graph.addNode(function); + graph.addEdge(createEdge(input.uuid(), "input", function.uuid(), "functionInput", {"bar", "baz"})); + graph.addEdge(createEdge(function.uuid(), "functionOutput", output.uuid(), "output")); + + return graph; + }(); + + QTest::newRow("TwoNodesOneEdge") << createBuffer(smallJson) << smallProtos << smallGraph << QShaderGraphLoader::Ready; + QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << smallProtos << QShaderGraph() << QShaderGraphLoader::Error; + QTest::newRow("NoPrototype") << createBuffer(smallJson) << PrototypeHash() << QShaderGraph() << QShaderGraphLoader::Error; + + const auto complexJson = "{" + " \"nodes\": [" + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"type\": \"inputValue\"," + " \"parameters\": {" + " \"name\": \"worldPosition\"," + " \"qualifier\": {" + " \"type\": \"QShaderLanguage::StorageQualifier\"," + " \"value\": \"QShaderLanguage::Input\"" + " }," + " \"type\": {" + " \"type\": \"QShaderLanguage::VariableType\"," + " \"value\": \"QShaderLanguage::Vec3\"" + " }" + " }" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"type\": \"texture\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"type\": \"texCoord\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000004}\"," + " \"type\": \"inputValue\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000005}\"," + " \"type\": \"exposure\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000006}\"," + " \"type\": \"fragColor\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"type\": \"sampleTexture\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"type\": \"lightModel\"" + " }," + " {" + " \"uuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"type\": \"exposureFunction\"" + " }" + " ]," + " \"edges\": [" + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000002}\"," + " \"sourcePort\": \"texture\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"targetPort\": \"sampler\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000003}\"," + " \"sourcePort\": \"texCoord\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"targetPort\": \"coord\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000001}\"," + " \"sourcePort\": \"value\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"position\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000007}\"," + " \"sourcePort\": \"color\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"baseColor\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000004}\"," + " \"sourcePort\": \"value\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"targetPort\": \"lightIntensity\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000008}\"," + " \"sourcePort\": \"outputColor\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"targetPort\": \"inputColor\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000005}\"," + " \"sourcePort\": \"exposure\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"targetPort\": \"exposure\"" + " }," + " {" + " \"sourceUuid\": \"{00000000-0000-0000-0000-000000000009}\"," + " \"sourcePort\": \"outputColor\"," + " \"targetUuid\": \"{00000000-0000-0000-0000-000000000006}\"," + " \"targetPort\": \"fragColor\"" + " }" + " ]" + "}"; + + const auto complexProtos = [this]{ + const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + + auto protos = PrototypeHash(); + + auto inputValue = createNode({ + createPort(QShaderNodePort::Output, "value") + }); + inputValue.setParameter("name", "defaultName"); + inputValue.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Uniform)); + inputValue.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(QShaderLanguage::Float)); + inputValue.addRule(openGLES2, QShaderNode::Rule("highp $type $value = $name;", + QByteArrayList() << "$qualifier highp $type $name;")); + inputValue.addRule(openGL3, QShaderNode::Rule("$type $value = $name;", + QByteArrayList() << "$qualifier $type $name;")); + protos.insert("inputValue", inputValue); + + auto texture = createNode({ + createPort(QShaderNodePort::Output, "texture") + }); + texture.addRule(openGLES2, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + texture.addRule(openGL3, QShaderNode::Rule("sampler2D $texture = texture;", + QByteArrayList() << "uniform sampler2D texture;")); + protos.insert("texture", texture); + + auto texCoord = createNode({ + createPort(QShaderNodePort::Output, "texCoord") + }); + texCoord.addRule(openGLES2, QShaderNode::Rule("highp vec2 $texCoord = texCoord;", + QByteArrayList() << "varying highp vec2 texCoord;")); + texCoord.addRule(openGL3, QShaderNode::Rule("vec2 $texCoord = texCoord;", + QByteArrayList() << "in vec2 texCoord;")); + protos.insert("texCoord", texCoord); + + auto exposure = createNode({ + createPort(QShaderNodePort::Output, "exposure") + }); + exposure.addRule(openGLES2, QShaderNode::Rule("highp float $exposure = exposure;", + QByteArrayList() << "uniform highp float exposure;")); + exposure.addRule(openGL3, QShaderNode::Rule("float $exposure = exposure;", + QByteArrayList() << "uniform float exposure;")); + protos.insert("exposure", exposure); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(openGL3, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + protos.insert("fragColor", fragColor); + + auto sampleTexture = createNode({ + createPort(QShaderNodePort::Input, "sampler"), + createPort(QShaderNodePort::Input, "coord"), + createPort(QShaderNodePort::Output, "color") + }); + sampleTexture.addRule(openGLES2, QShaderNode::Rule("highp vec4 $color = texture2D($sampler, $coord);")); + sampleTexture.addRule(openGL3, QShaderNode::Rule("vec4 $color = texture2D($sampler, $coord);")); + protos.insert("sampleTexture", sampleTexture); + + auto lightModel = createNode({ + createPort(QShaderNodePort::Input, "baseColor"), + createPort(QShaderNodePort::Input, "position"), + createPort(QShaderNodePort::Input, "lightIntensity"), + createPort(QShaderNodePort::Output, "outputColor") + }); + lightModel.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include es2/lightmodel.frag.inc")); + lightModel.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);", + QByteArrayList() << "#pragma include gl3/lightmodel.frag.inc")); + protos.insert("lightModel", lightModel); + + auto exposureFunction = createNode({ + createPort(QShaderNodePort::Input, "inputColor"), + createPort(QShaderNodePort::Input, "exposure"), + createPort(QShaderNodePort::Output, "outputColor") + }); + exposureFunction.addRule(openGLES2, QShaderNode::Rule("highp vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + exposureFunction.addRule(openGL3, QShaderNode::Rule("vec4 $outputColor = $inputColor * pow(2.0, $exposure);")); + protos.insert("exposureFunction", exposureFunction); + + return protos; + }(); + + const auto complexGraph = createGraph(); + + QTest::newRow("ComplexGraph") << createBuffer(complexJson) << complexProtos << complexGraph << QShaderGraphLoader::Ready; +} + +void tst_QShaderGraphLoader::shouldLoadFromJsonStream() +{ + // GIVEN + QFETCH(QBufferPointer, device); + QFETCH(PrototypeHash, prototypes); + + auto loader = QShaderGraphLoader(); + + // WHEN + loader.setPrototypes(prototypes); + loader.setDevice(device.data()); + loader.load(); + + // THEN + QFETCH(QShaderGraphLoader::Status, status); + QCOMPARE(loader.status(), status); + + QFETCH(QShaderGraph, graph); + const auto statements = loader.graph().createStatements({"foo", "bar", "baz"}); + const auto expected = graph.createStatements({"foo", "bar", "baz"}); + dumpStatementsIfNeeded(statements, expected); + QCOMPARE(statements, expected); + + const auto sortedParameters = [](const QShaderNode &node) { + auto res = node.parameterNames(); + res.sort(); + return res; + }; + + for (int i = 0; i < statements.size(); i++) { + const auto actualNode = statements.at(i).node; + const auto expectedNode = expected.at(i).node; + + QCOMPARE(actualNode.layers(), expectedNode.layers()); + QCOMPARE(actualNode.ports(), expectedNode.ports()); + QCOMPARE(sortedParameters(actualNode), sortedParameters(expectedNode)); + for (const auto &name : expectedNode.parameterNames()) { + QCOMPARE(actualNode.parameter(name), expectedNode.parameter(name)); + } + QCOMPARE(actualNode.availableFormats(), expectedNode.availableFormats()); + for (const auto &format : expectedNode.availableFormats()) { + QCOMPARE(actualNode.rule(format), expectedNode.rule(format)); + } + } +} + +QTEST_MAIN(tst_QShaderGraphLoader) + +#include "tst_qshadergraphloader.moc" |