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 ++ .../util/qshadergenerator/tst_qshadergenerator.cpp | 217 ++++++++++++++------- 5 files changed, 191 insertions(+), 72 deletions(-) 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")); diff --git a/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp index 82197f815e..c873aebef7 100644 --- a/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp +++ b/tests/auto/gui/util/qshadergenerator/tst_qshadergenerator.cpp @@ -35,11 +35,13 @@ namespace { - QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion) + QShaderFormat createFormat(QShaderFormat::Api api, int majorVersion, int minorVersion, + QShaderFormat::ShaderType shaderType= QShaderFormat::Fragment) { auto format = QShaderFormat(); format.setApi(api); format.setVersion(QVersionNumber(majorVersion, minorVersion)); + format.setShaderType(shaderType); return format; } @@ -74,7 +76,7 @@ namespace return edge; } - QShaderGraph createGraph() + QShaderGraph createFragmentShaderGraph() { const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); @@ -213,7 +215,7 @@ void tst_QShaderGenerator::shouldGenerateShaderCode_data() QTest::addColumn("format"); QTest::addColumn("expectedCode"); - const auto graph = createGraph(); + const auto graph = createFragmentShaderGraph(); const auto openGLES2 = createFormat(QShaderFormat::OpenGLES, 2, 0); const auto openGL3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); @@ -580,120 +582,195 @@ void tst_QShaderGenerator::shouldProcessLanguageQualifierAndTypeEnums_data() QTest::addColumn("format"); QTest::addColumn("expectedCode"); - const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0); - const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0); - const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0); - const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); - const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0); - - const auto qualifierEnum = QMetaEnum::fromType(); - const auto typeEnum = QMetaEnum::fromType(); - - for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) { - const auto qualifierName = qualifierEnum.key(qualifierIndex); - const auto qualifierValue = static_cast(qualifierEnum.value(qualifierIndex)); + { + const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0); + const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0); + const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0); + const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0); + const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0); + + const auto qualifierEnum = QMetaEnum::fromType(); + const auto typeEnum = QMetaEnum::fromType(); + + for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) { + const auto qualifierName = qualifierEnum.key(qualifierIndex); + const auto qualifierValue = static_cast(qualifierEnum.value(qualifierIndex)); + + for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) { + const auto typeName = typeEnum.key(typeIndex); + const auto typeValue = static_cast(typeEnum.value(typeIndex)); + + auto graph = QShaderGraph(); + + auto worldPosition = createNode({ + createPort(QShaderNodePort::Output, "value") + }); + worldPosition.setParameter("name", "worldPosition"); + worldPosition.setParameter("qualifier", QVariant::fromValue(qualifierValue)); + worldPosition.setParameter("type", QVariant::fromValue(typeValue)); + worldPosition.addRule(es2, QShaderNode::Rule("highp $type $value = $name;", + QByteArrayList() << "$qualifier highp $type $name;")); + worldPosition.addRule(gl2, QShaderNode::Rule("$type $value = $name;", + QByteArrayList() << "$qualifier $type $name;")); + worldPosition.addRule(gl3, QShaderNode::Rule("$type $value = $name;", + QByteArrayList() << "$qualifier $type $name;")); + + auto fragColor = createNode({ + createPort(QShaderNodePort::Input, "fragColor") + }); + fragColor.addRule(es2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(gl2, QShaderNode::Rule("gl_fragColor = $fragColor;")); + fragColor.addRule(gl3, QShaderNode::Rule("fragColor = $fragColor;", + QByteArrayList() << "out vec4 fragColor;")); + + graph.addNode(worldPosition); + graph.addNode(fragColor); + + graph.addEdge(createEdge(worldPosition.uuid(), "value", fragColor.uuid(), "fragColor")); + + const auto gl2Code = (QByteArrayList() << "#version 110" + << "" + << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl2)) + .arg(toGlsl(typeValue)) + .toUtf8() + << "" + << "void main()" + << "{" + << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() + << " gl_fragColor = v0;" + << "}" + << "").join("\n"); + const auto gl3Code = (QByteArrayList() << "#version 130" + << "" + << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl3)) + .arg(toGlsl(typeValue)) + .toUtf8() + << "out vec4 fragColor;" + << "" + << "void main()" + << "{" + << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() + << " fragColor = v0;" + << "}" + << "").join("\n"); + const auto gl4Code = (QByteArrayList() << "#version 400 core" + << "" + << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl4)) + .arg(toGlsl(typeValue)) + .toUtf8() + << "out vec4 fragColor;" + << "" + << "void main()" + << "{" + << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() + << " fragColor = v0;" + << "}" + << "").join("\n"); + const auto es2Code = (QByteArrayList() << "#version 100" + << "" + << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es2)) + .arg(toGlsl(typeValue)) + .toUtf8() + << "" + << "void main()" + << "{" + << QStringLiteral(" highp %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() + << " gl_fragColor = v0;" + << "}" + << "").join("\n"); + const auto es3Code = (QByteArrayList() << "#version 300 es" + << "" + << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es3)) + .arg(toGlsl(typeValue)) + .toUtf8() + << "" + << "void main()" + << "{" + << QStringLiteral(" highp %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() + << " gl_fragColor = v0;" + << "}" + << "").join("\n"); + + QTest::addRow("%s %s ES2", qualifierName, typeName) << graph << es2 << es2Code; + QTest::addRow("%s %s ES3", qualifierName, typeName) << graph << es3 << es3Code; + QTest::addRow("%s %s GL2", qualifierName, typeName) << graph << gl2 << gl2Code; + QTest::addRow("%s %s GL3", qualifierName, typeName) << graph << gl3 << gl3Code; + QTest::addRow("%s %s GL4", qualifierName, typeName) << graph << gl4 << gl4Code; + } + } + } - for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) { - const auto typeName = typeEnum.key(typeIndex); - const auto typeValue = static_cast(typeEnum.value(typeIndex)); + { + const auto es2 = createFormat(QShaderFormat::OpenGLES, 2, 0, QShaderFormat::Vertex); + const auto es3 = createFormat(QShaderFormat::OpenGLES, 3, 0, QShaderFormat::Vertex); + const auto gl2 = createFormat(QShaderFormat::OpenGLNoProfile, 2, 0, QShaderFormat::Vertex); + const auto gl3 = createFormat(QShaderFormat::OpenGLCoreProfile, 3, 0, QShaderFormat::Vertex); + const auto gl4 = createFormat(QShaderFormat::OpenGLCoreProfile, 4, 0, QShaderFormat::Vertex); auto graph = QShaderGraph(); - auto worldPosition = createNode({ + auto vertexPosition = createNode({ createPort(QShaderNodePort::Output, "value") }); - worldPosition.setParameter("name", "worldPosition"); - worldPosition.setParameter("qualifier", QVariant::fromValue(qualifierValue)); - worldPosition.setParameter("type", QVariant::fromValue(typeValue)); - worldPosition.addRule(es2, QShaderNode::Rule("highp $type $value = $name;", + vertexPosition.setParameter("name", "vertexPosition"); + vertexPosition.setParameter("qualifier", QVariant::fromValue(QShaderLanguage::Input)); + vertexPosition.setParameter("type", QVariant::fromValue(QShaderLanguage::Vec4)); + + vertexPosition.addRule(es2, QShaderNode::Rule("", QByteArrayList() << "$qualifier highp $type $name;")); - worldPosition.addRule(gl2, QShaderNode::Rule("$type $value = $name;", + vertexPosition.addRule(gl2, QShaderNode::Rule("", QByteArrayList() << "$qualifier $type $name;")); - worldPosition.addRule(gl3, QShaderNode::Rule("$type $value = $name;", + vertexPosition.addRule(gl3, QShaderNode::Rule("", QByteArrayList() << "$qualifier $type $name;")); - auto fragColor = createNode({ - createPort(QShaderNodePort::Input, "fragColor") - }); - fragColor.addRule(es2, QShaderNode::Rule("gl_fragColor = $fragColor;")); - fragColor.addRule(gl2, QShaderNode::Rule("gl_fragColor = $fragColor;")); - fragColor.addRule(gl3, QShaderNode::Rule("fragColor = $fragColor;", - QByteArrayList() << "out vec4 fragColor;")); - - graph.addNode(worldPosition); - graph.addNode(fragColor); - - graph.addEdge(createEdge(worldPosition.uuid(), "value", fragColor.uuid(), "fragColor")); + graph.addNode(vertexPosition); const auto gl2Code = (QByteArrayList() << "#version 110" << "" - << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl2)) - .arg(toGlsl(typeValue)) - .toUtf8() + << "attribute vec4 vertexPosition;" << "" << "void main()" << "{" - << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() - << " gl_fragColor = v0;" << "}" << "").join("\n"); const auto gl3Code = (QByteArrayList() << "#version 130" << "" - << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl3)) - .arg(toGlsl(typeValue)) - .toUtf8() - << "out vec4 fragColor;" + << "in vec4 vertexPosition;" << "" << "void main()" << "{" - << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() - << " fragColor = v0;" << "}" << "").join("\n"); const auto gl4Code = (QByteArrayList() << "#version 400 core" << "" - << QStringLiteral("%1 %2 worldPosition;").arg(toGlsl(qualifierValue, gl4)) - .arg(toGlsl(typeValue)) - .toUtf8() - << "out vec4 fragColor;" + << "in vec4 vertexPosition;" << "" << "void main()" << "{" - << QStringLiteral(" %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() - << " fragColor = v0;" << "}" << "").join("\n"); const auto es2Code = (QByteArrayList() << "#version 100" << "" - << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es2)) - .arg(toGlsl(typeValue)) - .toUtf8() + << "attribute highp vec4 vertexPosition;" << "" << "void main()" << "{" - << QStringLiteral(" highp %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() - << " gl_fragColor = v0;" << "}" << "").join("\n"); const auto es3Code = (QByteArrayList() << "#version 300 es" << "" - << QStringLiteral("%1 highp %2 worldPosition;").arg(toGlsl(qualifierValue, es3)) - .arg(toGlsl(typeValue)) - .toUtf8() + << "in highp vec4 vertexPosition;" << "" << "void main()" << "{" - << QStringLiteral(" highp %1 v0 = worldPosition;").arg(toGlsl(typeValue)).toUtf8() - << " gl_fragColor = v0;" << "}" << "").join("\n"); - QTest::addRow("%s %s ES2", qualifierName, typeName) << graph << es2 << es2Code; - QTest::addRow("%s %s ES3", qualifierName, typeName) << graph << es3 << es3Code; - QTest::addRow("%s %s GL2", qualifierName, typeName) << graph << gl2 << gl2Code; - QTest::addRow("%s %s GL3", qualifierName, typeName) << graph << gl3 << gl3Code; - QTest::addRow("%s %s GL4", qualifierName, typeName) << graph << gl4 << gl4Code; - } + QTest::addRow("Attribute header substitution ES2") << graph << es2 << es2Code; + QTest::addRow("Attribute header substitution ES3") << graph << es3 << es3Code; + QTest::addRow("Attribute header substitution GL2") << graph << gl2 << gl2Code; + QTest::addRow("Attribute header substitution GL3") << graph << gl3 << gl3Code; + QTest::addRow("Attribute header substitution GL4") << graph << gl4 << gl4Code; } } -- cgit v1.2.3