Implement graph layers support in QShaderGraph

When creating the statements, it is now possible to pass a list of
enabled layer names. Every node or edge which is not in the list of
enabled layers will be pruned from the graph prior to traversal. Note
that an empty layer list for a node or an edge means it is on all
layers.

Change-Id: I61a4df7d395b4beb42ee55ce08fef8ebe04263c9
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
This commit is contained in:
Kevin Ottens 2017-07-18 16:09:11 +02:00 committed by Sean Harmer
parent 32281653bf
commit f3c70ab9f3
3 changed files with 151 additions and 8 deletions

View File

@ -172,20 +172,46 @@ QVector<QShaderGraph::Edge> QShaderGraph::edges() const Q_DECL_NOTHROW
return m_edges;
}
QVector<QShaderGraph::Statement> QShaderGraph::createStatements() const
QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringList &enabledLayers) const
{
const auto idHash = [this] {
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); });
};
const auto enabledNodes = [this, intersectsEnabledLayers] {
auto res = QVector<QShaderNode>();
std::copy_if(m_nodes.cbegin(), m_nodes.cend(),
std::back_inserter(res),
[intersectsEnabledLayers] (const QShaderNode &node) {
return intersectsEnabledLayers(node.layers());
});
return res;
}();
const auto enabledEdges = [this, intersectsEnabledLayers] {
auto res = QVector<Edge>();
std::copy_if(m_edges.cbegin(), m_edges.cend(),
std::back_inserter(res),
[intersectsEnabledLayers] (const Edge &edge) {
return intersectsEnabledLayers(edge.layers);
});
return res;
}();
const auto idHash = [enabledNodes] {
auto nextVarId = 0;
auto res = QHash<QUuid, Statement>();
for (const auto &node : qAsConst(m_nodes))
for (const auto &node : enabledNodes)
res.insert(node.uuid(), nodeToStatement(node, nextVarId));
return res;
}();
auto result = QVector<Statement>();
auto currentEdges = m_edges;
auto currentUuids = [this] {
const auto inputs = copyOutputNodes(m_nodes);
auto currentEdges = enabledEdges;
auto currentUuids = [enabledNodes] {
const auto inputs = copyOutputNodes(enabledNodes);
auto res = QVector<QUuid>();
std::transform(inputs.cbegin(), inputs.cend(),
std::back_inserter(res),
@ -201,7 +227,7 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements() const
// input nodes
while (!currentUuids.isEmpty()) {
const auto uuid = currentUuids.takeFirst();
result.append(completeStatement(idHash, m_edges, uuid));
result.append(completeStatement(idHash, enabledEdges, uuid));
const auto outgoing = outgoingEdges(currentEdges, uuid);
for (const auto &outgoingEdge : outgoing) {

View File

@ -89,7 +89,7 @@ public:
Q_GUI_EXPORT void removeEdge(const Edge &edge);
Q_GUI_EXPORT QVector<Edge> edges() const Q_DECL_NOTHROW;
Q_GUI_EXPORT QVector<Statement> createStatements() const;
Q_GUI_EXPORT QVector<Statement> createStatements(const QStringList &enabledLayers = QStringList()) const;
private:
QVector<QShaderNode> m_nodes;

View File

@ -113,6 +113,7 @@ private slots:
void shouldHandleUnboundPortsDuringGraphSerialization();
void shouldSurviveCyclesDuringGraphSerialization();
void shouldDealWithEdgesJumpingOverLayers();
void shouldGenerateDifferentStatementsDependingOnActiveLayers();
};
void tst_QShaderGraph::shouldHaveEdgeDefaultState()
@ -657,6 +658,122 @@ void tst_QShaderGraph::shouldDealWithEdgesJumpingOverLayers()
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<QShaderGraph::Statement>()
<< 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<QShaderGraph::Statement>()
<< 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<QShaderGraph::Statement>()
<< 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<QShaderGraph::Statement>()
<< 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);
}
}
QTEST_MAIN(tst_QShaderGraph)
#include "tst_qshadergraph.moc"