summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Guichard <nicolas.guichard@kdab.com>2020-02-18 17:16:14 +0100
committerNicolas Guichard <nicolas.guichard@kdab.com>2020-02-20 08:00:14 +0100
commit39994e0705f11afc45e20872b95fb3a6e684c913 (patch)
treee3e28111dd7bf2d4121e5111d791422213583a71
parent8cf4ce0fea65db841bb5345169401aebbe0a5587 (diff)
QShaderGenerator: Don't crash when a node has multiple outputs
It was already possible to declare a node prototype with multiple outputs, but trying to assign to all those outputs was not possible and instead resulted in a crash. It is now possible to declare nodes like this without crashing: "SEPERATE_XYZ": { "inputs": ["vector"], "outputs": ["x", "y", "z"], "rules": [ { "substitution": "float $x = $vector.x; float $y = $vector.y; float $z = $vector.z;" } ] } Change-Id: I748e77e84c9120dc688c573eee33dc13c6bfbace Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r--src/gui/util/qshadergenerator.cpp67
-rw-r--r--tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp73
2 files changed, 112 insertions, 28 deletions
diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp
index 7ea42a7c9a..244b95605b 100644
--- a/src/gui/util/qshadergenerator.cpp
+++ b/src/gui/util/qshadergenerator.cpp
@@ -346,10 +346,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
code << QByteArrayLiteral("void main()");
code << QByteArrayLiteral("{");
- const QRegularExpression localToGlobalRegExp(QStringLiteral("^.*\\s+(\\w+)\\s*=\\s*((?:\\w+\\(.*\\))|(?:\\w+)).*;$"));
- const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("^(.*\\s+(v\\d+))\\s*=\\s*(.*);$"));
+ const QRegularExpression localToGlobalRegExp(QStringLiteral("[^;]*\\s+(\\w+)\\s*=\\s*((?:\\w+\\(.*\\))|(?:\\w+))[^;]*;"));
+ const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
- const QRegularExpression outputToTemporaryAssignmentRegExp(QStringLiteral("^\\s*(\\w+)\\s*=\\s*(.*);$"));
+ const QRegularExpression outputToTemporaryAssignmentRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);"));
struct Variable;
@@ -517,14 +517,31 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
// Substitute variable names by generated vN variable names
const QByteArray substitutionedLine = replaceParameters(line, node, format);
- Variable *v = nullptr;
+ QRegularExpressionMatchIterator matches;
switch (node.type()) {
- // Record name of temporary variable that possibly references a global input
- // We will replace the temporary variables by the matching global variables later
- case QShaderNode::Input: {
- const QRegularExpressionMatch match = localToGlobalRegExp.match(QString::fromUtf8(substitutionedLine));
- if (match.hasMatch()) {
+ case QShaderNode::Input:
+ matches = localToGlobalRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Function:
+ matches = temporaryVariableToAssignmentRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Output:
+ matches = outputToTemporaryAssignmentRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
+ break;
+ case QShaderNode::Invalid:
+ break;
+ }
+
+ while (matches.hasNext()) {
+ QRegularExpressionMatch match = matches.next();
+
+ Variable *v = nullptr;
+
+ switch (node.type()) {
+ // Record name of temporary variable that possibly references a global input
+ // We will replace the temporary variables by the matching global variables later
+ case QShaderNode::Input: {
const QString localVariable = match.captured(1);
const QString globalVariable = match.captured(2);
@@ -535,13 +552,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
Assignment assignment;
assignment.expression = globalVariable;
v->assignment = assignment;
+ break;
}
- break;
- }
- case QShaderNode::Function: {
- const QRegularExpressionMatch match = temporaryVariableToAssignmentRegExp.match(QString::fromUtf8(substitutionedLine));
- if (match.hasMatch()) {
+ case QShaderNode::Function: {
const QString localVariableDeclaration = match.captured(1);
const QString localVariableName = match.captured(2);
const QString assignmentContent = match.captured(3);
@@ -554,13 +568,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
// Find variables that may be referenced in the assignment
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
}
- break;
- }
- case QShaderNode::Output: {
- const QRegularExpressionMatch match = outputToTemporaryAssignmentRegExp.match(QString::fromUtf8(substitutionedLine));
- if (match.hasMatch()) {
+ case QShaderNode::Output: {
const QString outputDeclaration = match.captured(1);
const QString assignmentContent = match.captured(2);
@@ -575,17 +586,17 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
// Find variables that may be referenced in the assignment
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
+ break;
+ }
+ case QShaderNode::Invalid:
+ break;
}
- break;
- }
- case QShaderNode::Invalid:
- break;
- }
- LineContent lineContent;
- lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
- lineContent.var = v;
- lines << lineContent;
+ LineContent lineContent;
+ lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
+ lineContent.var = v;
+ lines << lineContent;
+ }
}
// Go through all lines
diff --git a/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp
index 2b9b08a18a..76211f8358 100644
--- a/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp
+++ b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp
@@ -199,6 +199,7 @@ private slots:
void shouldUseGlobalVariableRatherThanTemporaries();
void shouldGenerateTemporariesWisely();
void shouldHandlePortNamesPrefixingOneAnother();
+ void shouldHandleNodesWithMultipleOutputPorts();
};
void tst_QShaderGenerator::shouldHaveDefaultState()
@@ -1299,6 +1300,78 @@ void tst_QShaderGenerator::shouldHandlePortNamesPrefixingOneAnother()
QCOMPARE(code, expected.join("\n"));
}
+void tst_QShaderGenerator::shouldHandleNodesWithMultipleOutputPorts()
+{
+ // GIVEN
+ const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0);
+
+ auto input = createNode({
+ createPort(QShaderNodePort::Output, "output0"),
+ createPort(QShaderNodePort::Output, "output1")
+ });
+ input.addRule(gl4, QShaderNode::Rule("vec4 $output0 = globalIn0;"
+ "float $output1 = globalIn1;",
+ QByteArrayList() << "in vec4 globalIn0;" << "in float globalIn1;"));
+
+ auto function = createNode({
+ createPort(QShaderNodePort::Input, "input0"),
+ createPort(QShaderNodePort::Input, "input1"),
+ createPort(QShaderNodePort::Output, "output0"),
+ createPort(QShaderNodePort::Output, "output1")
+ });
+ function.addRule(gl4, QShaderNode::Rule("vec4 $output0 = $input0;"
+ "float $output1 = $input1;"));
+
+ auto output = createNode({
+ createPort(QShaderNodePort::Input, "input0"),
+ createPort(QShaderNodePort::Input, "input1")
+ });
+
+ output.addRule(gl4, QShaderNode::Rule("globalOut0 = $input0;"
+ "globalOut1 = $input1;",
+ QByteArrayList() << "out vec4 globalOut0;" << "out float globalOut1;"));
+
+ // WHEN
+ const auto graph = [=] {
+ auto res = QShaderGraph();
+
+ res.addNode(input);
+ res.addNode(function);
+ res.addNode(output);
+
+ res.addEdge(createEdge(input.uuid(), "output0", function.uuid(), "input0"));
+ res.addEdge(createEdge(input.uuid(), "output1", function.uuid(), "input1"));
+
+ res.addEdge(createEdge(function.uuid(), "output0", output.uuid(), "input0"));
+ res.addEdge(createEdge(function.uuid(), "output1", output.uuid(), "input1"));
+
+ return res;
+ }();
+
+ auto generator = QShaderGenerator();
+ generator.graph = graph;
+ generator.format = gl4;
+
+ const auto code = generator.createShaderCode();
+
+ // THEN
+ const auto expected = QByteArrayList()
+ << "#version 400 core"
+ << ""
+ << "in vec4 globalIn0;"
+ << "in float globalIn1;"
+ << "out vec4 globalOut0;"
+ << "out float globalOut1;"
+ << ""
+ << "void main()"
+ << "{"
+ << " globalOut0 = globalIn0;"
+ << " globalOut1 = globalIn1;"
+ << "}"
+ << "";
+ QCOMPARE(code, expected.join("\n"));
+}
+
QTEST_MAIN(tst_QShaderGenerator)
#include "tst_qshadergenerator.moc"