diff options
author | Nicolas Guichard <nicolas.guichard@kdab.com> | 2020-02-18 17:16:14 +0100 |
---|---|---|
committer | Nicolas Guichard <nicolas.guichard@kdab.com> | 2020-02-20 08:00:14 +0100 |
commit | 39994e0705f11afc45e20872b95fb3a6e684c913 (patch) | |
tree | e3e28111dd7bf2d4121e5111d791422213583a71 | |
parent | 8cf4ce0fea65db841bb5345169401aebbe0a5587 (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.cpp | 67 | ||||
-rw-r--r-- | tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp | 73 |
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" |