1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qshadergenerator_p.h"
5
6#include "qshaderlanguage_p.h"
7#include <QRegularExpression>
8
9#include <cctype>
10#include <qshaderprogram_p.h>
11
12QT_BEGIN_NAMESPACE
13namespace Qt3DRender {
14Q_LOGGING_CATEGORY(ShaderGenerator, "ShaderGenerator", QtWarningMsg)
15
16namespace
17{
18 QByteArray toGlsl(QShaderLanguage::StorageQualifier qualifier, const QShaderFormat &format) noexcept
19 {
20 if (format.version().majorVersion() <= 2 && format.api() != QShaderFormat::RHI) {
21 // Note we're assuming fragment shader only here, it'd be different
22 // values for vertex shader, will need to be fixed properly at some
23 // point but isn't necessary yet (this problem already exists in past
24 // commits anyway)
25 switch (qualifier) {
26 case QShaderLanguage::Const:
27 return "const";
28 case QShaderLanguage::Input:
29 if (format.shaderType() == QShaderFormat::Vertex)
30 return "attribute";
31 else
32 return "varying";
33 case QShaderLanguage::Output:
34 return ""; // Although fragment shaders for <=2 only have fixed outputs
35 case QShaderLanguage::Uniform:
36 return "uniform";
37 case QShaderLanguage::BuiltIn:
38 return "//";
39 }
40 } else {
41 switch (qualifier) {
42 case QShaderLanguage::Const:
43 return "const";
44 case QShaderLanguage::Input:
45 return "in";
46 case QShaderLanguage::Output:
47 return "out";
48 case QShaderLanguage::Uniform:
49 return "uniform";
50 case QShaderLanguage::BuiltIn:
51 return "//";
52 }
53 }
54
55 Q_UNREACHABLE();
56 }
57
58 QByteArray toGlsl(QShaderLanguage::VariableType type) noexcept
59 {
60 switch (type) {
61 case QShaderLanguage::Bool:
62 return "bool";
63 case QShaderLanguage::Int:
64 return "int";
65 case QShaderLanguage::Uint:
66 return "uint";
67 case QShaderLanguage::Float:
68 return "float";
69 case QShaderLanguage::Double:
70 return "double";
71 case QShaderLanguage::Vec2:
72 return "vec2";
73 case QShaderLanguage::Vec3:
74 return "vec3";
75 case QShaderLanguage::Vec4:
76 return "vec4";
77 case QShaderLanguage::DVec2:
78 return "dvec2";
79 case QShaderLanguage::DVec3:
80 return "dvec3";
81 case QShaderLanguage::DVec4:
82 return "dvec4";
83 case QShaderLanguage::BVec2:
84 return "bvec2";
85 case QShaderLanguage::BVec3:
86 return "bvec3";
87 case QShaderLanguage::BVec4:
88 return "bvec4";
89 case QShaderLanguage::IVec2:
90 return "ivec2";
91 case QShaderLanguage::IVec3:
92 return "ivec3";
93 case QShaderLanguage::IVec4:
94 return "ivec4";
95 case QShaderLanguage::UVec2:
96 return "uvec2";
97 case QShaderLanguage::UVec3:
98 return "uvec3";
99 case QShaderLanguage::UVec4:
100 return "uvec4";
101 case QShaderLanguage::Mat2:
102 return "mat2";
103 case QShaderLanguage::Mat3:
104 return "mat3";
105 case QShaderLanguage::Mat4:
106 return "mat4";
107 case QShaderLanguage::Mat2x2:
108 return "mat2x2";
109 case QShaderLanguage::Mat2x3:
110 return "mat2x3";
111 case QShaderLanguage::Mat2x4:
112 return "mat2x4";
113 case QShaderLanguage::Mat3x2:
114 return "mat3x2";
115 case QShaderLanguage::Mat3x3:
116 return "mat3x3";
117 case QShaderLanguage::Mat3x4:
118 return "mat3x4";
119 case QShaderLanguage::Mat4x2:
120 return "mat4x2";
121 case QShaderLanguage::Mat4x3:
122 return "mat4x3";
123 case QShaderLanguage::Mat4x4:
124 return "mat4x4";
125 case QShaderLanguage::DMat2:
126 return "dmat2";
127 case QShaderLanguage::DMat3:
128 return "dmat3";
129 case QShaderLanguage::DMat4:
130 return "dmat4";
131 case QShaderLanguage::DMat2x2:
132 return "dmat2x2";
133 case QShaderLanguage::DMat2x3:
134 return "dmat2x3";
135 case QShaderLanguage::DMat2x4:
136 return "dmat2x4";
137 case QShaderLanguage::DMat3x2:
138 return "dmat3x2";
139 case QShaderLanguage::DMat3x3:
140 return "dmat3x3";
141 case QShaderLanguage::DMat3x4:
142 return "dmat3x4";
143 case QShaderLanguage::DMat4x2:
144 return "dmat4x2";
145 case QShaderLanguage::DMat4x3:
146 return "dmat4x3";
147 case QShaderLanguage::DMat4x4:
148 return "dmat4x4";
149 case QShaderLanguage::Sampler1D:
150 return "sampler1D";
151 case QShaderLanguage::Sampler2D:
152 return "sampler2D";
153 case QShaderLanguage::Sampler3D:
154 return "sampler3D";
155 case QShaderLanguage::SamplerCube:
156 return "samplerCube";
157 case QShaderLanguage::Sampler2DRect:
158 return "sampler2DRect";
159 case QShaderLanguage::Sampler2DMs:
160 return "sampler2DMS";
161 case QShaderLanguage::SamplerBuffer:
162 return "samplerBuffer";
163 case QShaderLanguage::Sampler1DArray:
164 return "sampler1DArray";
165 case QShaderLanguage::Sampler2DArray:
166 return "sampler2DArray";
167 case QShaderLanguage::Sampler2DMsArray:
168 return "sampler2DMSArray";
169 case QShaderLanguage::SamplerCubeArray:
170 return "samplerCubeArray";
171 case QShaderLanguage::Sampler1DShadow:
172 return "sampler1DShadow";
173 case QShaderLanguage::Sampler2DShadow:
174 return "sampler2DShadow";
175 case QShaderLanguage::Sampler2DRectShadow:
176 return "sampler2DRectShadow";
177 case QShaderLanguage::Sampler1DArrayShadow:
178 return "sampler1DArrayShadow";
179 case QShaderLanguage::Sampler2DArrayShadow:
180 return "sampler2DArrayShadow";
181 case QShaderLanguage::SamplerCubeShadow:
182 return "samplerCubeShadow";
183 case QShaderLanguage::SamplerCubeArrayShadow:
184 return "samplerCubeArrayShadow";
185 case QShaderLanguage::ISampler1D:
186 return "isampler1D";
187 case QShaderLanguage::ISampler2D:
188 return "isampler2D";
189 case QShaderLanguage::ISampler3D:
190 return "isampler3D";
191 case QShaderLanguage::ISamplerCube:
192 return "isamplerCube";
193 case QShaderLanguage::ISampler2DRect:
194 return "isampler2DRect";
195 case QShaderLanguage::ISampler2DMs:
196 return "isampler2DMS";
197 case QShaderLanguage::ISamplerBuffer:
198 return "isamplerBuffer";
199 case QShaderLanguage::ISampler1DArray:
200 return "isampler1DArray";
201 case QShaderLanguage::ISampler2DArray:
202 return "isampler2DArray";
203 case QShaderLanguage::ISampler2DMsArray:
204 return "isampler2DMSArray";
205 case QShaderLanguage::ISamplerCubeArray:
206 return "isamplerCubeArray";
207 case QShaderLanguage::USampler1D:
208 return "usampler1D";
209 case QShaderLanguage::USampler2D:
210 return "usampler2D";
211 case QShaderLanguage::USampler3D:
212 return "usampler3D";
213 case QShaderLanguage::USamplerCube:
214 return "usamplerCube";
215 case QShaderLanguage::USampler2DRect:
216 return "usampler2DRect";
217 case QShaderLanguage::USampler2DMs:
218 return "usampler2DMS";
219 case QShaderLanguage::USamplerBuffer:
220 return "usamplerBuffer";
221 case QShaderLanguage::USampler1DArray:
222 return "usampler1DArray";
223 case QShaderLanguage::USampler2DArray:
224 return "usampler2DArray";
225 case QShaderLanguage::USampler2DMsArray:
226 return "usampler2DMSArray";
227 case QShaderLanguage::USamplerCubeArray:
228 return "usamplerCubeArray";
229 }
230
231 Q_UNREACHABLE();
232 }
233
234 QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node,
235 const QShaderFormat &format) noexcept
236 {
237 QByteArray result = original;
238
239 const QStringList parameterNames = node.parameterNames();
240 for (const QString &parameterName : parameterNames) {
241 const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8());
242 const QVariant parameter = node.parameter(name: parameterName);
243 if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) {
244 const QShaderLanguage::StorageQualifier qualifier =
245 qvariant_cast<QShaderLanguage::StorageQualifier>(v: parameter);
246 const QByteArray value = toGlsl(qualifier, format);
247 result.replace(before: placeholder, after: value);
248 } else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) {
249 const QShaderLanguage::VariableType type =
250 qvariant_cast<QShaderLanguage::VariableType>(v: parameter);
251 const QByteArray value = toGlsl(type);
252 result.replace(before: placeholder, after: value);
253 } else {
254 const QByteArray value = parameter.toString().toUtf8();
255 result.replace(before: placeholder, after: value);
256 }
257 }
258
259 return result;
260 }
261
262 struct ShaderGenerationState
263 {
264 ShaderGenerationState(const QShaderGenerator &gen,
265 QList<QShaderGraph::Statement> statements)
266 : generator { gen }, statements { statements }
267 {
268
269 }
270
271 const QShaderGenerator &generator;
272 QList<QShaderGraph::Statement> statements;
273 QByteArrayList code;
274 };
275
276 class GLSL45HeaderWriter
277 {
278 public:
279 void writeHeader(ShaderGenerationState &state)
280 {
281 const auto &format = state.generator.format;
282 auto &code = state.code;
283 for (const QShaderGraph::Statement &statement : state.statements) {
284 const QShaderNode &node = statement.node;
285 const QByteArrayList &headerSnippets = node.rule(format).headerSnippets;
286 for (const QByteArray &snippet : headerSnippets) {
287 auto replacedSnippet = replaceParameters(original: snippet, node, format).trimmed();
288
289 if (replacedSnippet.startsWith(QByteArrayLiteral("add-input"))) {
290 onInOut(code, snippet: replacedSnippet);
291 } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-uniform"))) {
292 onNamedUniform(ubo, snippet: replacedSnippet);
293 } else if (replacedSnippet.startsWith(QByteArrayLiteral("add-sampler"))) {
294 onNamedSampler(code, snippet: replacedSnippet);
295 } else if (replacedSnippet.startsWith(QByteArrayLiteral("#pragma include "))) {
296 onInclude(code, snippet: replacedSnippet);
297 } else {
298 code << replacedSnippet;
299 }
300 }
301 }
302
303 if (!ubo.isEmpty()) {
304 code << QByteArrayLiteral("layout(std140, binding = ")
305 + QByteArray::number(currentBinding++)
306 + QByteArrayLiteral(") uniform qt3d_shadergraph_generated_uniforms {");
307 code << ubo;
308 code << "};";
309 }
310 }
311
312 private:
313 void onInOut(QByteArrayList &code, const QByteArray &snippet) noexcept
314 {
315 const auto split = snippet.split(sep: ' ');
316 if (split.size() < 4) {
317 qDebug() << "Invalid header snippet: " << snippet;
318 return;
319 }
320 const auto &qualifier = split[1];
321 const auto &type = split[2];
322 const auto &name = split[3];
323
324 if (qualifier == QByteArrayLiteral("in")) {
325 code << (QByteArrayLiteral("layout(location = ")
326 + QByteArray::number(currentInputLocation++) + QByteArrayLiteral(") in ")
327 + type + ' ' + name + QByteArrayLiteral(";"));
328 } else if (qualifier == QByteArrayLiteral("out")) {
329 code << (QByteArrayLiteral("layout(location = ")
330 + QByteArray::number(currentOutputLocation++) + QByteArrayLiteral(") out ")
331 + type + ' ' + name + QByteArrayLiteral(";"));
332 } else if (qualifier == QByteArrayLiteral("uniform")) {
333 ubo << (type + ' ' + name + ';');
334 }
335 }
336
337 void onNamedUniform(QByteArrayList &ubo, const QByteArray &snippet) noexcept
338 {
339 const auto split = snippet.split(sep: ' ');
340 if (split.size() < 3) {
341 qDebug() << "Invalid header snippet: " << snippet;
342 return;
343 }
344
345 const auto &type = split[1];
346 const auto &name = split[2];
347
348 ubo << (type + ' ' + name + ';');
349 }
350
351 void onNamedSampler(QByteArrayList &code, const QByteArray &snippet) noexcept
352 {
353 const auto split = snippet.split(sep: ' ');
354 if (split.size() < 3) {
355 qDebug() << "Invalid header snippet: " << snippet;
356 return;
357 }
358 const auto binding = QByteArray::number(currentBinding++);
359 const auto &type = split[1];
360 const auto &name = split[2];
361
362 code << (QByteArrayLiteral("layout(binding = ") + binding + QByteArrayLiteral(") uniform ")
363 + type + ' ' + name + QByteArrayLiteral(";"));
364 }
365
366 void onInclude(QByteArrayList &code, const QByteArray &snippet) noexcept
367 {
368 const auto filepath = QString::fromUtf8(ba: snippet.mid(index: strlen(s: "#pragma include ")));
369 const QByteArray deincluded = QShaderProgramPrivate::deincludify(filePath: filepath);
370
371 code << QShaderProgramPrivate::resolveAutoBindingIndices(content: deincluded,
372 currentBinding,
373 currentInputLocation,
374 currentOutputLocation);
375 }
376
377 int currentInputLocation { 0 };
378 int currentOutputLocation { 0 };
379 int currentBinding { 2 };
380 QByteArrayList ubo;
381 };
382
383 struct GLSLHeaderWriter
384 {
385 void writeHeader(ShaderGenerationState &state)
386 {
387 const auto &format = state.generator.format;
388 auto &code = state.code;
389 for (const QShaderGraph::Statement &statement : state.statements) {
390 const QShaderNode &node = statement.node;
391 const QByteArrayList &headerSnippets = node.rule(format).headerSnippets;
392 for (const QByteArray &snippet : headerSnippets) {
393 code << replaceParameters(original: snippet, node, format);
394 }
395 }
396 }
397 };
398
399 QByteArray versionString(const QShaderFormat &format) noexcept
400 {
401 if (!format.isValid())
402 return {};
403
404 switch (format.api()) {
405 case QShaderFormat::RHI: {
406 return QByteArrayLiteral("#version 450");
407 }
408 case QShaderFormat::VulkanFlavoredGLSL: {
409 const int major = format.version().majorVersion();
410 const int minor = format.version().minorVersion();
411 return (QByteArrayLiteral("#version ") + QByteArray::number(major * 100 + minor * 10));
412 }
413 default: {
414 const bool isGLES = format.api() == QShaderFormat::OpenGLES;
415 const int major = format.version().majorVersion();
416 const int minor = format.version().minorVersion();
417
418 const int version = major == 2 && isGLES ? 100
419 : major == 3 && isGLES ? 300
420 : major == 2 ? 100 + 10 * (minor + 1)
421 : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
422 : major * 100 + minor * 10;
423
424 const QByteArray profile =
425 isGLES && version > 100 ? QByteArrayLiteral(" es")
426 : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
427 : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
428 : QByteArray();
429
430 return (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
431 }
432 }
433 }
434
435 QByteArrayList layerDefines(const QStringList &enabledLayers) noexcept
436 {
437 QByteArrayList defines;
438 const QString defineTemplate = QStringLiteral("#define LAYER_%1");
439
440 for (const QString &layer : enabledLayers)
441 defines << defineTemplate.arg(a: layer).toUtf8();
442
443 return defines;
444 }
445}
446
447QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const
448{
449 const QList<QShaderNode> nodes = graph.nodes();
450 const auto statements = graph.createStatements(enabledLayers);
451 ShaderGenerationState state(*this, statements);
452 QByteArrayList &code = state.code;
453
454 code << versionString(format);
455 code << QByteArray();
456 code << layerDefines(enabledLayers);
457
458 if (format.api() == QShaderFormat::VulkanFlavoredGLSL || format.api() == QShaderFormat::RHI) {
459 GLSL45HeaderWriter builder;
460 builder.writeHeader(state);
461 } else {
462 GLSLHeaderWriter builder;
463 builder.writeHeader(state);
464 }
465
466 code << QByteArray();
467 code << QByteArrayLiteral("void main()");
468 code << QByteArrayLiteral("{");
469
470 const QRegularExpression temporaryVariableToAssignmentRegExp(
471 QStringLiteral("([^;]*\\s+(v\\d+))\\s*=\\s*([^;]*);"));
472 const QRegularExpression temporaryVariableInAssignmentRegExp(QStringLiteral("\\W*(v\\d+)\\W*"));
473 const QRegularExpression statementRegExp(QStringLiteral("\\s*(\\w+)\\s*=\\s*([^;]*);"));
474
475 struct Variable;
476
477 struct Assignment
478 {
479 QString expression;
480 QList<Variable *> referencedVariables;
481 };
482
483 struct Variable
484 {
485 enum Type { GlobalInput, TemporaryAssignment, Output };
486
487 QString name;
488 QString declaration;
489 int referenceCount = 0;
490 Assignment assignment;
491 Type type = TemporaryAssignment;
492 bool substituted = false;
493
494 static void substitute(Variable *v)
495 {
496 if (v->substituted)
497 return;
498
499 qCDebug(ShaderGenerator)
500 << "Begin Substituting " << v->name << " = " << v->assignment.expression;
501 for (Variable *ref : std::as_const(t&: v->assignment.referencedVariables)) {
502 // Recursively substitute
503 Variable::substitute(v: ref);
504
505 // Replace all variables referenced only once in the assignment
506 // by their actual expression
507 if (ref->referenceCount == 1 || ref->type == Variable::GlobalInput) {
508 const QRegularExpression r(QStringLiteral("(.*\\b)(%1)(\\b.*)").arg(a: ref->name));
509 if (v->assignment.referencedVariables.size() == 1)
510 v->assignment.expression.replace(
511 re: r, QStringLiteral("\\1%2\\3").arg(a: ref->assignment.expression));
512 else
513 v->assignment.expression.replace(
514 re: r, QStringLiteral("(\\1%2\\3)").arg(a: ref->assignment.expression));
515 }
516 }
517 qCDebug(ShaderGenerator)
518 << "Done Substituting " << v->name << " = " << v->assignment.expression;
519 v->substituted = true;
520 }
521 };
522
523 struct LineContent
524 {
525 QByteArray rawContent;
526 Variable *var = nullptr;
527 };
528
529 // Table to store temporary variables that should be replaced:
530 // - If variable references a a global variables
531 // -> we will use the global variable directly
532 // - If variable references a function results
533 // -> will be kept only if variable is referenced more than once.
534 // This avoids having vec3 v56 = vertexPosition; when we could
535 // just use vertexPosition directly.
536 // The added benefit is when having arrays, we don't try to create
537 // mat4 v38 = skinningPalelette[100] which would be invalid
538 std::vector<Variable> temporaryVariables;
539 // Reserve more than enough space to ensure no reallocation will take place
540 temporaryVariables.reserve(n: nodes.size() * 8);
541
542 QList<LineContent> lines;
543
544 auto createVariable = [&] () -> Variable * {
545 Q_ASSERT(temporaryVariables.capacity() > 0);
546 temporaryVariables.resize(new_size: temporaryVariables.size() + 1);
547 return &temporaryVariables.back();
548 };
549
550 auto findVariable = [&] (const QString &name) -> Variable * {
551 const auto end = temporaryVariables.end();
552 auto it = std::find_if(first: temporaryVariables.begin(), last: end,
553 pred: [=] (const Variable &a) { return a.name == name; });
554 if (it != end)
555 return &(*it);
556 return nullptr;
557 };
558
559 auto gatherTemporaryVariablesFromAssignment = [&](Variable *v,
560 const QString &assignmentContent) {
561 QRegularExpressionMatchIterator subMatchIt =
562 temporaryVariableInAssignmentRegExp.globalMatch(subject: assignmentContent);
563 while (subMatchIt.hasNext()) {
564 const QRegularExpressionMatch subMatch = subMatchIt.next();
565 const QString variableName = subMatch.captured(nth: 1);
566
567 // Variable we care about should already exists -> an expression cannot reference a
568 // variable that hasn't been defined
569 Variable *u = findVariable(variableName);
570 Q_ASSERT(u);
571
572 // Increase reference count for u
573 ++u->referenceCount;
574 // Insert u as reference for variable v
575 v->assignment.referencedVariables.push_back(t: u);
576 }
577 };
578
579 for (const QShaderGraph::Statement &statement : statements) {
580 const QShaderNode node = statement.node;
581 QByteArray line = node.rule(format).substitution;
582 const QList<QShaderNodePort> ports = node.ports();
583
584 struct VariableReplacement
585 {
586 QByteArray placeholder;
587 QByteArray variable;
588 };
589
590 QList<VariableReplacement> variableReplacements;
591
592 // Generate temporary variable names vN
593 for (const QShaderNodePort &port : ports) {
594 const QString portName = port.name;
595 const QShaderNodePort::Direction portDirection = port.direction;
596 const bool isInput = port.direction == QShaderNodePort::Input;
597
598 const int portIndex = statement.portIndex(direction: portDirection, portName);
599
600 Q_ASSERT(portIndex >= 0);
601
602 const int variableIndex =
603 isInput ? statement.inputs.at(i: portIndex) : statement.outputs.at(i: portIndex);
604 if (variableIndex < 0)
605 continue;
606
607 VariableReplacement replacement;
608 replacement.placeholder = QByteArrayLiteral("$") + portName.toUtf8();
609 replacement.variable = QByteArrayLiteral("v") + QByteArray::number(variableIndex);
610
611 variableReplacements.append(t: std::move(replacement));
612 }
613
614 qsizetype begin = 0;
615 while ((begin = line.indexOf(c: '$', from: begin)) != -1) {
616 qsizetype end = begin + 1;
617 char endChar = line.at(i: end);
618 const qsizetype size = line.size();
619 while (end < size && (std::isalnum(uchar(endChar)) || endChar == '_')) {
620 ++end;
621 endChar = line.at(i: end);
622 }
623
624 const qsizetype placeholderLength = end - begin;
625
626 const QByteArray variableName = line.mid(index: begin, len: placeholderLength);
627 const auto replacementIt =
628 std::find_if(first: variableReplacements.cbegin(), last: variableReplacements.cend(),
629 pred: [&variableName](const VariableReplacement &replacement) {
630 return variableName == replacement.placeholder;
631 });
632
633 if (replacementIt != variableReplacements.cend()) {
634 line.replace(index: begin, len: placeholderLength, s: replacementIt->variable);
635 begin += replacementIt->variable.size();
636 } else {
637 begin = end;
638 }
639 }
640
641 // Substitute variable names by generated vN variable names
642 const QByteArray substitutionedLine = replaceParameters(original: line, node, format);
643
644 QRegularExpressionMatchIterator matches;
645
646 switch (node.type()) {
647 case QShaderNode::Input:
648 case QShaderNode::Output:
649 matches = statementRegExp.globalMatch(subject: QString::fromUtf8(ba: substitutionedLine));
650 break;
651 case QShaderNode::Function:
652 matches = temporaryVariableToAssignmentRegExp.globalMatch(
653 subject: QString::fromUtf8(ba: substitutionedLine));
654 break;
655 case QShaderNode::Invalid:
656 break;
657 }
658
659 while (matches.hasNext()) {
660 QRegularExpressionMatch match = matches.next();
661
662 Variable *v = nullptr;
663
664 switch (node.type()) {
665 // Record name of temporary variable that possibly references a global input
666 // We will replace the temporary variables by the matching global variables later
667 case QShaderNode::Input: {
668 const QString localVariable = match.captured(nth: 1);
669 const QString globalVariable = match.captured(nth: 2);
670
671 v = createVariable();
672 v->name = localVariable;
673 v->type = Variable::GlobalInput;
674
675 Assignment assignment;
676 assignment.expression = globalVariable;
677 v->assignment = assignment;
678 break;
679 }
680
681 case QShaderNode::Function: {
682 const QString localVariableDeclaration = match.captured(nth: 1);
683 const QString localVariableName = match.captured(nth: 2);
684 const QString assignmentContent = match.captured(nth: 3);
685
686 // Add new variable -> it cannot exist already
687 v = createVariable();
688 v->name = localVariableName;
689 v->declaration = localVariableDeclaration;
690 v->assignment.expression = assignmentContent;
691
692 // Find variables that may be referenced in the assignment
693 gatherTemporaryVariablesFromAssignment(v, assignmentContent);
694 break;
695 }
696
697 case QShaderNode::Output: {
698 const QString outputDeclaration = match.captured(nth: 1);
699 const QString assignmentContent = match.captured(nth: 2);
700
701 v = createVariable();
702 v->name = outputDeclaration;
703 v->declaration = outputDeclaration;
704 v->type = Variable::Output;
705
706 Assignment assignment;
707 assignment.expression = assignmentContent;
708 v->assignment = assignment;
709
710 // Find variables that may be referenced in the assignment
711 gatherTemporaryVariablesFromAssignment(v, assignmentContent);
712 break;
713 }
714 case QShaderNode::Invalid:
715 break;
716 }
717
718 LineContent lineContent;
719 lineContent.rawContent = QByteArray(QByteArrayLiteral(" ") + substitutionedLine);
720 lineContent.var = v;
721 lines << lineContent;
722 }
723 }
724
725 // Go through all lines
726 // Perform substitution of line with temporary variables substitution
727 for (LineContent &lineContent : lines) {
728 Variable *v = lineContent.var;
729 qCDebug(ShaderGenerator) << lineContent.rawContent;
730 if (v != nullptr) {
731 Variable::substitute(v);
732
733 qCDebug(ShaderGenerator)
734 << "Line " << lineContent.rawContent << "is assigned to temporary" << v->name;
735
736 // Check number of occurrences a temporary variable is referenced
737 if (v->referenceCount == 1 || v->type == Variable::GlobalInput) {
738 // If it is referenced only once, no point in creating a temporary
739 // Clear content for current line
740 lineContent.rawContent.clear();
741 // We assume expression that were referencing vN will have vN properly substituted
742 } else {
743 lineContent.rawContent = QStringLiteral(" %1 = %2;")
744 .arg(a: v->declaration)
745 .arg(a: v->assignment.expression)
746 .toUtf8();
747 }
748
749 qCDebug(ShaderGenerator) << "Updated Line is " << lineContent.rawContent;
750 }
751 }
752
753 // Go throug all lines and insert content
754 for (const LineContent &lineContent : std::as_const(t&: lines)) {
755 if (!lineContent.rawContent.isEmpty()) {
756 code << lineContent.rawContent;
757 }
758 }
759
760 code << QByteArrayLiteral("}");
761 code << QByteArray();
762
763 return code.join(sep: '\n');
764}
765
766}
767QT_END_NAMESPACE
768

source code of qt3d/src/render/shadergraph/qshadergenerator.cpp