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("void main()");
|
||||||
code << QByteArrayLiteral("{");
|
code << QByteArrayLiteral("{");
|
||||||
|
|
||||||
const QRegularExpression localToGlobalRegExp(QStringLiteral("^.*\\s+(\\w+)\\s*=\\s*((?:\\w+\\(.*\\))|(?:\\w+)).*;$"));
|
const QRegularExpression localToGlobalRegExp(QStringLiteral("[^;]*\\s+(\\w+)\\s*=\\s*((?:\\w+\\(.*\\))|(?:\\w+))[^;]*;"));
|
||||||
const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("^(.*\\s+(v\\d+))\\s*=\\s*(.*);$"));
|
const QRegularExpression temporaryVariableToAssignmentRegExp(QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
|
||||||
const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
|
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;
|
struct Variable;
|
||||||
|
|
||||||
@ -517,14 +517,31 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
|
|||||||
// Substitute variable names by generated vN variable names
|
// Substitute variable names by generated vN variable names
|
||||||
const QByteArray substitutionedLine = replaceParameters(line, node, format);
|
const QByteArray substitutionedLine = replaceParameters(line, node, format);
|
||||||
|
|
||||||
Variable *v = nullptr;
|
QRegularExpressionMatchIterator matches;
|
||||||
|
|
||||||
switch (node.type()) {
|
switch (node.type()) {
|
||||||
// Record name of temporary variable that possibly references a global input
|
case QShaderNode::Input:
|
||||||
// We will replace the temporary variables by the matching global variables later
|
matches = localToGlobalRegExp.globalMatch(QString::fromUtf8(substitutionedLine));
|
||||||
case QShaderNode::Input: {
|
break;
|
||||||
const QRegularExpressionMatch match = localToGlobalRegExp.match(QString::fromUtf8(substitutionedLine));
|
case QShaderNode::Function:
|
||||||
if (match.hasMatch()) {
|
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 localVariable = match.captured(1);
|
||||||
const QString globalVariable = match.captured(2);
|
const QString globalVariable = match.captured(2);
|
||||||
|
|
||||||
@ -535,13 +552,10 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
|
|||||||
Assignment assignment;
|
Assignment assignment;
|
||||||
assignment.expression = globalVariable;
|
assignment.expression = globalVariable;
|
||||||
v->assignment = assignment;
|
v->assignment = assignment;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case QShaderNode::Function: {
|
case QShaderNode::Function: {
|
||||||
const QRegularExpressionMatch match = temporaryVariableToAssignmentRegExp.match(QString::fromUtf8(substitutionedLine));
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
const QString localVariableDeclaration = match.captured(1);
|
const QString localVariableDeclaration = match.captured(1);
|
||||||
const QString localVariableName = match.captured(2);
|
const QString localVariableName = match.captured(2);
|
||||||
const QString assignmentContent = match.captured(3);
|
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
|
// Find variables that may be referenced in the assignment
|
||||||
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
|
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case QShaderNode::Output: {
|
case QShaderNode::Output: {
|
||||||
const QRegularExpressionMatch match = outputToTemporaryAssignmentRegExp.match(QString::fromUtf8(substitutionedLine));
|
|
||||||
if (match.hasMatch()) {
|
|
||||||
const QString outputDeclaration = match.captured(1);
|
const QString outputDeclaration = match.captured(1);
|
||||||
const QString assignmentContent = match.captured(2);
|
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
|
// Find variables that may be referenced in the assignment
|
||||||
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
|
gatherTemporaryVariablesFromAssignment(v, assignmentContent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QShaderNode::Invalid:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QShaderNode::Invalid:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LineContent lineContent;
|
LineContent lineContent;
|
||||||
lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
|
lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
|
||||||
lineContent.var = v;
|
lineContent.var = v;
|
||||||
lines << lineContent;
|
lines << lineContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go through all lines
|
// Go through all lines
|
||||||
|
@ -199,6 +199,7 @@ private slots:
|
|||||||
void shouldUseGlobalVariableRatherThanTemporaries();
|
void shouldUseGlobalVariableRatherThanTemporaries();
|
||||||
void shouldGenerateTemporariesWisely();
|
void shouldGenerateTemporariesWisely();
|
||||||
void shouldHandlePortNamesPrefixingOneAnother();
|
void shouldHandlePortNamesPrefixingOneAnother();
|
||||||
|
void shouldHandleNodesWithMultipleOutputPorts();
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QShaderGenerator::shouldHaveDefaultState()
|
void tst_QShaderGenerator::shouldHaveDefaultState()
|
||||||
@ -1299,6 +1300,78 @@ void tst_QShaderGenerator::shouldHandlePortNamesPrefixingOneAnother()
|
|||||||
QCOMPARE(code, expected.join("\n"));
|
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)
|
QTEST_MAIN(tst_QShaderGenerator)
|
||||||
|
|
||||||
#include "tst_qshadergenerator.moc"
|
#include "tst_qshadergenerator.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user