/**************************************************************************** ** ** 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 #include #include using QBufferPointer = QSharedPointer; Q_DECLARE_METATYPE(QBufferPointer); using NodeHash = QHash; Q_DECLARE_METATYPE(NodeHash); 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, const QStringList &extensions = QStringList(), const QString &vendor = QString()) { auto format = QShaderFormat(); format.setApi(api); format.setVersion(QVersionNumber(majorVersion, minorVersion)); format.setExtensions(extensions); format.setVendor(vendor); return format; } QShaderNodePort createPort(QShaderNodePort::Direction portDirection, const QString &portName) { auto port = QShaderNodePort(); port.direction = portDirection; port.name = portName; return port; } QShaderNode createNode(const QList &ports) { auto node = QShaderNode(); for (const auto &port : ports) node.addPort(port); return node; } } class tst_QShaderNodesLoader : public QObject { Q_OBJECT private slots: void shouldManipulateLoaderMembers(); void shouldLoadFromJsonStream_data(); void shouldLoadFromJsonStream(); }; void tst_QShaderNodesLoader::shouldManipulateLoaderMembers() { // GIVEN auto loader = QShaderNodesLoader(); // THEN (default state) QCOMPARE(loader.status(), QShaderNodesLoader::Null); QVERIFY(!loader.device()); QVERIFY(loader.nodes().isEmpty()); // WHEN auto device1 = createBuffer(QByteArray("..........."), QIODevice::NotOpen); loader.setDevice(device1.data()); // THEN QCOMPARE(loader.status(), QShaderNodesLoader::Error); QCOMPARE(loader.device(), device1.data()); QVERIFY(loader.nodes().isEmpty()); // WHEN auto device2 = createBuffer(QByteArray("..........."), QIODevice::ReadOnly); loader.setDevice(device2.data()); // THEN QCOMPARE(loader.status(), QShaderNodesLoader::Waiting); QCOMPARE(loader.device(), device2.data()); QVERIFY(loader.nodes().isEmpty()); } void tst_QShaderNodesLoader::shouldLoadFromJsonStream_data() { QTest::addColumn("device"); QTest::addColumn("nodes"); QTest::addColumn("status"); QTest::newRow("empty") << createBuffer("", QIODevice::ReadOnly) << NodeHash() << QShaderNodesLoader::Error; const auto smallJson = "{" " \"inputValue\": {" " \"outputs\": [" " \"value\"" " ]," " \"parameters\": {" " \"name\": \"defaultName\"," " \"qualifier\": {" " \"type\": \"QShaderLanguage::StorageQualifier\"," " \"value\": \"QShaderLanguage::Uniform\"" " }," " \"type\": {" " \"type\": \"QShaderLanguage::VariableType\"," " \"value\": \"QShaderLanguage::Vec3\"" " }," " \"defaultValue\": {" " \"type\": \"float\"," " \"value\": \"1.25\"" " }" " }," " \"rules\": [" " {" " \"format\": {" " \"api\": \"OpenGLES\"," " \"major\": 2," " \"minor\": 0" " }," " \"substitution\": \"highp vec3 $value = $name;\"," " \"headerSnippets\": [ \"varying highp vec3 $name;\" ]" " }," " {" " \"format\": {" " \"api\": \"OpenGLCompatibilityProfile\"," " \"major\": 2," " \"minor\": 1" " }," " \"substitution\": \"vec3 $value = $name;\"," " \"headerSnippets\": [ \"in vec3 $name;\" ]" " }" " ]" " }," " \"fragColor\": {" " \"inputs\": [" " \"fragColor\"" " ]," " \"rules\": [" " {" " \"format\": {" " \"api\": \"OpenGLES\"," " \"major\": 2," " \"minor\": 0" " }," " \"substitution\": \"gl_fragColor = $fragColor;\"" " }," " {" " \"format\": {" " \"api\": \"OpenGLNoProfile\"," " \"major\": 4," " \"minor\": 0" " }," " \"substitution\": \"fragColor = $fragColor;\"," " \"headerSnippets\": [ \"out vec4 fragColor;\" ]" " }" " ]" " }," " \"lightModel\": {" " \"inputs\": [" " \"baseColor\"," " \"position\"," " \"lightIntensity\"" " ]," " \"outputs\": [" " \"outputColor\"" " ]," " \"rules\": [" " {" " \"format\": {" " \"api\": \"OpenGLES\"," " \"major\": 2," " \"minor\": 0," " \"extensions\": [ \"ext1\", \"ext2\" ]," " \"vendor\": \"kdab\"" " }," " \"substitution\": \"highp vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\"," " \"headerSnippets\": [ \"#pragma include es2/lightmodel.frag.inc\" ]" " }," " {" " \"format\": {" " \"api\": \"OpenGLCoreProfile\"," " \"major\": 3," " \"minor\": 3" " }," " \"substitution\": \"vec4 $outputColor = lightModel($baseColor, $position, $lightIntensity);\"," " \"headerSnippets\": [ \"#pragma include gl3/lightmodel.frag.inc\" ]" " }" " ]" " }" "}"; const auto smallProtos = [&]{ const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); const auto openGLES2Extended = createFormat(QShaderFormat::OpenGLES, 2, 0, {"ext1", "ext2"}, "kdab"); const auto openGL2 = createFormat(QShaderFormat::OpenGLCompatibilityProfile, 2, 1); const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 3); const auto openGL4 = createFormat(QShaderFormat::OpenGLNoProfile, 4, 0); auto protos = NodeHash(); auto inputValue = createNode({ createPort(QShaderNodePort::Output, "value") }); inputValue.setParameter("name", "defaultName"); inputValue.setParameter("qualifier", QVariant::fromValue(QShaderLanguage::Uniform)); inputValue.setParameter("type", QVariant::fromValue(QShaderLanguage::Vec3)); inputValue.setParameter("defaultValue", QVariant(1.25f)); inputValue.addRule(openGLES2, QShaderNode::Rule("highp vec3 $value = $name;", QByteArrayList() << "varying highp vec3 $name;")); inputValue.addRule(openGL2, QShaderNode::Rule("vec3 $value = $name;", QByteArrayList() << "in vec3 $name;")); protos.insert("inputValue", inputValue); auto fragColor = createNode({ createPort(QShaderNodePort::Input, "fragColor") }); fragColor.addRule(openGLES2, QShaderNode::Rule("gl_fragColor = $fragColor;")); fragColor.addRule(openGL4, QShaderNode::Rule("fragColor = $fragColor;", QByteArrayList() << "out vec4 fragColor;")); protos.insert(QStringLiteral("fragColor"), fragColor); auto lightModel = createNode({ createPort(QShaderNodePort::Input, "baseColor"), createPort(QShaderNodePort::Input, "position"), createPort(QShaderNodePort::Input, "lightIntensity"), createPort(QShaderNodePort::Output, "outputColor") }); lightModel.addRule(openGLES2Extended, 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); return protos; }(); QTest::newRow("NotOpen") << createBuffer(smallJson, QIODevice::NotOpen) << NodeHash() << QShaderNodesLoader::Error; QTest::newRow("CorrectJSON") << createBuffer(smallJson) << smallProtos << QShaderNodesLoader::Ready; // These types are normaly registered by QShaderGraphLoader qRegisterMetaType(); qRegisterMetaType(); } void tst_QShaderNodesLoader::shouldLoadFromJsonStream() { // GIVEN QFETCH(QBufferPointer, device); auto loader = QShaderNodesLoader(); // WHEN loader.setDevice(device.data()); loader.load(); // THEN QFETCH(QShaderNodesLoader::Status, status); QCOMPARE(loader.status(), status); QFETCH(NodeHash, nodes); const auto sortedKeys = [](const NodeHash &nodes) { auto res = nodes.keys(); res.sort(); return res; }; const auto sortedParameters = [](const QShaderNode &node) { auto res = node.parameterNames(); res.sort(); return res; }; QCOMPARE(sortedKeys(loader.nodes()), sortedKeys(nodes)); for (const auto &key : nodes.keys()) { const auto actual = loader.nodes().value(key); const auto expected = nodes.value(key); QVERIFY(actual.uuid().isNull()); QCOMPARE(actual.ports(), expected.ports()); QCOMPARE(sortedParameters(actual), sortedParameters(expected)); for (const auto &name : expected.parameterNames()) { QCOMPARE(actual.parameter(name), expected.parameter(name)); } QCOMPARE(actual.availableFormats(), expected.availableFormats()); for (const auto &format : expected.availableFormats()) { QCOMPARE(actual.rule(format), expected.rule(format)); } } } QTEST_MAIN(tst_QShaderNodesLoader) #include "tst_qshadernodesloader.moc"