From 60181f13a35b05bce664ba5f6cfa7a9d6ae2dc7d Mon Sep 17 00:00:00 2001 From: Paul Lemire Date: Fri, 29 Mar 2019 10:54:43 +0100 Subject: QShaderGenerator: fix substitution for attributes on GL2/ES2 GL2/ES2 expect it to be attribute and not in like later versions of OpenGL. Task-number: QTBUG-74829 Change-Id: Iddd22386ed315d6e6843d8225e49a4b73b6ad9ba Reviewed-by: Sean Harmer --- src/gui/util/qshaderformat.cpp | 17 ++++++++++++++++- src/gui/util/qshaderformat_p.h | 13 +++++++++++++ src/gui/util/qshadergenerator.cpp | 5 ++++- src/gui/util/qshadernodesloader.cpp | 11 +++++++++++ 4 files changed, 44 insertions(+), 2 deletions(-) (limited to 'src/gui/util') diff --git a/src/gui/util/qshaderformat.cpp b/src/gui/util/qshaderformat.cpp index 373bfb9e7e..e4e3718199 100644 --- a/src/gui/util/qshaderformat.cpp +++ b/src/gui/util/qshaderformat.cpp @@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE QShaderFormat::QShaderFormat() Q_DECL_NOTHROW : m_api(NoApi) + , m_shaderType(Fragment) { } @@ -106,6 +107,9 @@ bool QShaderFormat::supports(const QShaderFormat &other) const Q_DECL_NOTHROW if (m_version < other.m_version) return false; + if (m_shaderType != other.m_shaderType) + return false; + const auto containsAllExtensionsFromOther = std::includes(m_extensions.constBegin(), m_extensions.constEnd(), other.m_extensions.constBegin(), @@ -119,12 +123,23 @@ bool QShaderFormat::supports(const QShaderFormat &other) const Q_DECL_NOTHROW return true; } +QShaderFormat::ShaderType QShaderFormat::shaderType() const Q_DECL_NOTHROW +{ + return m_shaderType; +} + +void QShaderFormat::setShaderType(QShaderFormat::ShaderType shaderType) Q_DECL_NOTHROW +{ + m_shaderType = shaderType; +} + bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) Q_DECL_NOTHROW { return lhs.api() == rhs.api() && lhs.version() == rhs.version() && lhs.extensions() == rhs.extensions() - && lhs.vendor() == rhs.vendor(); + && lhs.vendor() == rhs.vendor() + && lhs.shaderType() == rhs.shaderType(); } QT_END_NAMESPACE diff --git a/src/gui/util/qshaderformat_p.h b/src/gui/util/qshaderformat_p.h index 064c2364a7..8d5e83bd22 100644 --- a/src/gui/util/qshaderformat_p.h +++ b/src/gui/util/qshaderformat_p.h @@ -69,6 +69,15 @@ public: OpenGLES }; + enum ShaderType : int { + Vertex = 0, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment, + Compute + }; + Q_GUI_EXPORT QShaderFormat() Q_DECL_NOTHROW; Q_GUI_EXPORT Api api() const Q_DECL_NOTHROW; @@ -86,11 +95,15 @@ public: Q_GUI_EXPORT bool isValid() const Q_DECL_NOTHROW; Q_GUI_EXPORT bool supports(const QShaderFormat &other) const Q_DECL_NOTHROW; + Q_GUI_EXPORT ShaderType shaderType() const Q_DECL_NOTHROW; + Q_GUI_EXPORT void setShaderType(ShaderType shaderType) Q_DECL_NOTHROW; + private: Api m_api; QVersionNumber m_version; QStringList m_extensions; QString m_vendor; + ShaderType m_shaderType; }; Q_GUI_EXPORT bool operator==(const QShaderFormat &lhs, const QShaderFormat &rhs) Q_DECL_NOTHROW; diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp index 60cf5a2fc5..9d2cc387e0 100644 --- a/src/gui/util/qshadergenerator.cpp +++ b/src/gui/util/qshadergenerator.cpp @@ -56,7 +56,10 @@ namespace case QShaderLanguage::Const: return "const"; case QShaderLanguage::Input: - return "varying"; + if (format.shaderType() == QShaderFormat::Vertex) + return "attribute"; + else + return "varying"; case QShaderLanguage::Output: return ""; // Although fragment shaders for <=2 only have fixed outputs case QShaderLanguage::Uniform: diff --git a/src/gui/util/qshadernodesloader.cpp b/src/gui/util/qshadernodesloader.cpp index 9badbb94df..5369e8bd4c 100644 --- a/src/gui/util/qshadernodesloader.cpp +++ b/src/gui/util/qshadernodesloader.cpp @@ -251,6 +251,17 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject) break; } + // We default out to a Fragment ShaderType if nothing is specified + // as that was the initial behavior we introduced + const QString shaderType = formatObject.value(QStringLiteral("shaderType")).toString(); + format.setShaderType(shaderType == QStringLiteral("Fragment") ? QShaderFormat::Fragment + : shaderType == QStringLiteral("Vertex") ? QShaderFormat::Vertex + : shaderType == QStringLiteral("TessellationControl") ? QShaderFormat::TessellationControl + : shaderType == QStringLiteral("TessellationEvaluation") ? QShaderFormat::TessellationEvaluation + : shaderType == QStringLiteral("Geometry") ? QShaderFormat::Geometry + : shaderType == QStringLiteral("Compute") ? QShaderFormat::Compute + : QShaderFormat::Fragment); + const QByteArray substitution = substitutionValue.toString().toUtf8(); const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets")); -- cgit v1.2.3 From a552864c3bd138ae6f09bf0d14d416a18498875c Mon Sep 17 00:00:00 2001 From: Paul Lemire Date: Mon, 1 Apr 2019 16:27:39 +0200 Subject: QShaderGenerator: don't generate temporary variables for global inputs Up until now, the QShaderGenerator would create temporary variables for uniform, attributes, const. This change makes it use the global inputs directly rather than relying on the intermediate properties. Change-Id: Ia9497367d61e536969fe87536606f309c286dbb2 Reviewed-by: Sean Harmer --- src/gui/util/qshadergenerator.cpp | 62 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) (limited to 'src/gui/util') diff --git a/src/gui/util/qshadergenerator.cpp b/src/gui/util/qshadergenerator.cpp index 9d2cc387e0..205118f41c 100644 --- a/src/gui/util/qshadergenerator.cpp +++ b/src/gui/util/qshadergenerator.cpp @@ -40,6 +40,7 @@ #include "qshadergenerator_p.h" #include "qshaderlanguage_p.h" +#include QT_BEGIN_NAMESPACE @@ -317,12 +318,22 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) [enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); }; + QVector globalInputVariables; + const QRegularExpression globalInputExtractRegExp(QStringLiteral("^.*\\s+(\\w+).*;$")); + const QVector nodes = graph.nodes(); for (const QShaderNode &node : nodes) { if (intersectsEnabledLayers(node.layers())) { const QByteArrayList headerSnippets = node.rule(format).headerSnippets; for (const QByteArray &snippet : headerSnippets) { code << replaceParameters(snippet, node, format); + + // If node is an input, record the variable name into the globalInputVariables vector + if (node.type() == QShaderNode::Input) { + const QRegularExpressionMatch match = globalInputExtractRegExp.match(QString::fromUtf8(code.last())); + if (match.hasMatch()) + globalInputVariables.push_back(match.captured(1)); + } } } } @@ -331,6 +342,14 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) code << QByteArrayLiteral("void main()"); code << QByteArrayLiteral("{"); + // Table to store temporary variables that should be replaced by global + // variables. This avoids having vec3 v56 = vertexPosition; when we could + // just use vertexPosition directly. + // The added benefit is when having arrays, we don't try to create + // mat4 v38 = skinningPalelette[100] which would be invalid + QHash localReferencesToGlobalInputs; + const QRegularExpression localToGlobalRegExp(QStringLiteral("^.*\\s+(\\w+)\\s*=\\s*(\\w+).*;$")); + for (const QShaderGraph::Statement &statement : graph.createStatements(enabledLayers)) { const QShaderNode node = statement.node; QByteArray line = node.rule(format).substitution; @@ -341,6 +360,9 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const bool isInput = port.direction == QShaderNodePort::Input; const int portIndex = statement.portIndex(portDirection, portName); + + Q_ASSERT(portIndex >= 0); + const int variableIndex = isInput ? statement.inputs.at(portIndex) : statement.outputs.at(portIndex); if (variableIndex < 0) @@ -348,15 +370,51 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers) const auto placeholder = QByteArray(QByteArrayLiteral("$") + portName.toUtf8()); const auto variable = QByteArray(QByteArrayLiteral("v") + QByteArray::number(variableIndex)); + line.replace(placeholder, variable); } - code << QByteArrayLiteral(" ") + replaceParameters(line, node, format); + const QByteArray substitutionedLine = replaceParameters(line, node, format); + + // Record name of temporary variable that possibly references a global input + // We will replace the temporary variables by the matching global variables later + bool isAGlobalInputVariable = false; + if (node.type() == QShaderNode::Input) { + const QRegularExpressionMatch match = localToGlobalRegExp.match(QString::fromUtf8(substitutionedLine)); + if (match.hasMatch()) { + const QString globalVariable = match.captured(2); + if (globalInputVariables.contains(globalVariable)) { + const QString localVariable = match.captured(1); + // TO DO: Clean globalVariable (remove brackets ...) + localReferencesToGlobalInputs.insert(localVariable, globalVariable); + isAGlobalInputVariable = true; + } + } + } + + // Only insert content for lines aren't inputs or have not matching + // globalVariables for now + if (!isAGlobalInputVariable) + code << QByteArrayLiteral(" ") + substitutionedLine; } code << QByteArrayLiteral("}"); code << QByteArray(); - return code.join('\n'); + + // Replace occurrences of local variables which reference a global variable + // by the global variables directly + auto it = localReferencesToGlobalInputs.cbegin(); + const auto end = localReferencesToGlobalInputs.cend(); + QString codeString = QString::fromUtf8(code.join('\n')); + + while (it != end) { + const QRegularExpression r(QStringLiteral("\\b(%1)([\\b|\\.|;|\\)|\\[|\\s|\\*|\\+|\\/|\\-|,])").arg(it.key()), + QRegularExpression::MultilineOption); + codeString.replace(r, QStringLiteral("%1\\2").arg(it.value())); + ++it; + } + + return codeString.toUtf8(); } QT_END_NAMESPACE -- cgit v1.2.3