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> (cherry picked from commit 39994e0705f11afc45e20872b95fb3a6e684c913)
This commit is contained in:
parent
c2827e617c
commit
b150901525
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user