236284357f
Change-Id: I5038600e73523ac7eb37a88ddff1f942af18f18e Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
626 lines
30 KiB
C++
626 lines
30 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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"
|