Implement graph layers support in QShaderGenerator

Change-Id: I823f7866bc5e1f3b262f1aacf4c341dabda7305d
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
This commit is contained in:
Kevin Ottens 2017-07-19 15:12:38 +02:00 committed by Sean Harmer
parent 236284357f
commit 4bfff6a98b
3 changed files with 198 additions and 7 deletions

View File

@ -279,7 +279,7 @@ namespace
}
}
QByteArray QShaderGenerator::createShaderCode() const
QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
{
auto code = QByteArrayList();
@ -303,9 +303,17 @@ QByteArray QShaderGenerator::createShaderCode() const
code << QByteArray();
}
const auto intersectsEnabledLayers = [enabledLayers] (const QStringList &layers) {
return layers.isEmpty()
|| std::any_of(layers.cbegin(), layers.cend(),
[enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
};
for (const auto &node : graph.nodes()) {
for (const auto &snippet : node.rule(format).headerSnippets) {
code << replaceParameters(snippet, node, format);
if (intersectsEnabledLayers(node.layers())) {
for (const auto &snippet : node.rule(format).headerSnippets) {
code << replaceParameters(snippet, node, format);
}
}
}
@ -313,7 +321,7 @@ QByteArray QShaderGenerator::createShaderCode() const
code << QByteArrayLiteral("void main()");
code << QByteArrayLiteral("{");
for (const auto &statement : graph.createStatements()) {
for (const auto &statement : graph.createStatements(enabledLayers)) {
const auto node = statement.node;
auto line = node.rule(format).substitution;
for (const auto &port : node.ports()) {

View File

@ -60,7 +60,7 @@ QT_BEGIN_NAMESPACE
class QShaderGenerator
{
public:
Q_GUI_EXPORT QByteArray createShaderCode() const;
Q_GUI_EXPORT QByteArray createShaderCode(const QStringList &enabledLayers = QStringList()) const;
QShaderGraph graph;
QShaderFormat format;

View File

@ -51,23 +51,26 @@ namespace
return port;
}
QShaderNode createNode(const QVector<QShaderNodePort> &ports)
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 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;
}
@ -190,6 +193,7 @@ private slots:
void shouldGenerateVersionCommands();
void shouldProcessLanguageQualifierAndTypeEnums_data();
void shouldProcessLanguageQualifierAndTypeEnums();
void shouldGenerateDifferentCodeDependingOnActiveLayers();
};
void tst_QShaderGenerator::shouldHaveDefaultState()
@ -707,6 +711,185 @@ void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums()
QCOMPARE(code, expectedCode);
}
void tst_QShaderGenerator::shouldGenerateDifferentCodeDependingOnActiveLayers()
{
// GIVEN
const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
auto texCoord = createNode({
createPort(QShaderNodePort::Output, "texCoord")
}, {
"diffuseTexture",
"normalTexture"
});
texCoord.addRule(gl4, QShaderNode::Rule("vec2 $texCoord = texCoord;",
QByteArrayList() << "in vec2 texCoord;"));
auto diffuseUniform = createNode({
createPort(QShaderNodePort::Output, "color")
}, {"diffuseUniform"});
diffuseUniform.addRule(gl4, QShaderNode::Rule("vec4 $color = diffuseUniform;",
QByteArrayList() << "uniform vec4 diffuseUniform;"));
auto diffuseTexture = createNode({
createPort(QShaderNodePort::Input, "coord"),
createPort(QShaderNodePort::Output, "color")
}, {"diffuseTexture"});
diffuseTexture.addRule(gl4, QShaderNode::Rule("vec4 $color = texture2D(diffuseTexture, $coord);",
QByteArrayList() << "uniform sampler2D diffuseTexture;"));
auto normalUniform = createNode({
createPort(QShaderNodePort::Output, "normal")
}, {"normalUniform"});
normalUniform.addRule(gl4, QShaderNode::Rule("vec3 $normal = normalUniform;",
QByteArrayList() << "uniform vec3 normalUniform;"));
auto normalTexture = createNode({
createPort(QShaderNodePort::Input, "coord"),
createPort(QShaderNodePort::Output, "normal")
}, {"normalTexture"});
normalTexture.addRule(gl4, QShaderNode::Rule("vec3 $normal = texture2D(normalTexture, $coord).rgb;",
QByteArrayList() << "uniform sampler2D normalTexture;"));
auto lightFunction = createNode({
createPort(QShaderNodePort::Input, "color"),
createPort(QShaderNodePort::Input, "normal"),
createPort(QShaderNodePort::Output, "output")
});
lightFunction.addRule(gl4, QShaderNode::Rule("vec4 $output = lightModel($color, $normal);",
QByteArrayList() << "#pragma include gl4/lightmodel.frag.inc"));
auto fragColor = createNode({
createPort(QShaderNodePort::Input, "fragColor")
});
fragColor.addRule(gl4, QShaderNode::Rule("fragColor = $fragColor;",
QByteArrayList() << "out vec4 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;
}();
auto generator = QShaderGenerator();
generator.graph = graph;
generator.format = gl4;
{
// WHEN
const auto code = generator.createShaderCode({"diffuseUniform", "normalUniform"});
// THEN
const auto expected = QByteArrayList()
<< "#version 400 core"
<< ""
<< "uniform vec4 diffuseUniform;"
<< "uniform vec3 normalUniform;"
<< "#pragma include gl4/lightmodel.frag.inc"
<< "out vec4 fragColor;"
<< ""
<< "void main()"
<< "{"
<< " vec3 v1 = normalUniform;"
<< " vec4 v0 = diffuseUniform;"
<< " vec4 v2 = lightModel(v0, v1);"
<< " fragColor = v2;"
<< "}"
<< "";
QCOMPARE(code, expected.join("\n"));
}
{
// WHEN
const auto code = generator.createShaderCode({"diffuseUniform", "normalTexture"});
// THEN
const auto expected = QByteArrayList()
<< "#version 400 core"
<< ""
<< "in vec2 texCoord;"
<< "uniform vec4 diffuseUniform;"
<< "uniform sampler2D normalTexture;"
<< "#pragma include gl4/lightmodel.frag.inc"
<< "out vec4 fragColor;"
<< ""
<< "void main()"
<< "{"
<< " vec2 v0 = texCoord;"
<< " vec3 v2 = texture2D(normalTexture, v0).rgb;"
<< " vec4 v1 = diffuseUniform;"
<< " vec4 v3 = lightModel(v1, v2);"
<< " fragColor = v3;"
<< "}"
<< "";
QCOMPARE(code, expected.join("\n"));
}
{
// WHEN
const auto code = generator.createShaderCode({"diffuseTexture", "normalUniform"});
// THEN
const auto expected = QByteArrayList()
<< "#version 400 core"
<< ""
<< "in vec2 texCoord;"
<< "uniform sampler2D diffuseTexture;"
<< "uniform vec3 normalUniform;"
<< "#pragma include gl4/lightmodel.frag.inc"
<< "out vec4 fragColor;"
<< ""
<< "void main()"
<< "{"
<< " vec2 v0 = texCoord;"
<< " vec3 v2 = normalUniform;"
<< " vec4 v1 = texture2D(diffuseTexture, v0);"
<< " vec4 v3 = lightModel(v1, v2);"
<< " fragColor = v3;"
<< "}"
<< "";
QCOMPARE(code, expected.join("\n"));
}
{
// WHEN
const auto code = generator.createShaderCode({"diffuseTexture", "normalTexture"});
// THEN
const auto expected = QByteArrayList()
<< "#version 400 core"
<< ""
<< "in vec2 texCoord;"
<< "uniform sampler2D diffuseTexture;"
<< "uniform sampler2D normalTexture;"
<< "#pragma include gl4/lightmodel.frag.inc"
<< "out vec4 fragColor;"
<< ""
<< "void main()"
<< "{"
<< " vec2 v0 = texCoord;"
<< " vec3 v2 = texture2D(normalTexture, v0).rgb;"
<< " vec4 v1 = texture2D(diffuseTexture, v0);"
<< " vec4 v3 = lightModel(v1, v2);"
<< " fragColor = v3;"
<< "}"
<< "";
QCOMPARE(code, expected.join("\n"));
}
}
QTEST_MAIN(tst_QShaderGenerator)
#include "tst_qshadergenerator.moc"