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 <sean.harmer@kdab.com>
bb10
Paul Lemire 2019-03-29 10:54:43 +01:00 committed by Sean Harmer
parent 7c74048e94
commit 60181f13a3
5 changed files with 189 additions and 70 deletions

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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"));

View File

@ -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<QShaderFormat>("format");
QTest::addColumn<QByteArray>("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<QShaderFormat>("format");
QTest::addColumn<QByteArray>("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 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<QShaderLanguage::StorageQualifier>();
const auto typeEnum = QMetaEnum::fromType<QShaderLanguage::VariableType>();
const auto qualifierEnum = QMetaEnum::fromType<QShaderLanguage::StorageQualifier>();
const auto typeEnum = QMetaEnum::fromType<QShaderLanguage::VariableType>();
for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) {
const auto qualifierName = qualifierEnum.key(qualifierIndex);
const auto qualifierValue = static_cast<QShaderLanguage::StorageQualifier>(qualifierEnum.value(qualifierIndex));
for (int qualifierIndex = 0; qualifierIndex < qualifierEnum.keyCount(); qualifierIndex++) {
const auto qualifierName = qualifierEnum.key(qualifierIndex);
const auto qualifierValue = static_cast<QShaderLanguage::StorageQualifier>(qualifierEnum.value(qualifierIndex));
for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) {
const auto typeName = typeEnum.key(typeIndex);
const auto typeValue = static_cast<QShaderLanguage::VariableType>(typeEnum.value(typeIndex));
for (int typeIndex = 0; typeIndex < typeEnum.keyCount(); typeIndex++) {
const auto typeName = typeEnum.key(typeIndex);
const auto typeValue = static_cast<QShaderLanguage::VariableType>(typeEnum.value(typeIndex));
auto graph = QShaderGraph();
auto worldPosition = createNode({
createPort(QShaderNodePort::Output, "value")
});
worldPosition.setParameter("name", "worldPosition");
worldPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(qualifierValue));
worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(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;
}
}
}
{
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<QShaderLanguage::StorageQualifier>(qualifierValue));
worldPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(typeValue));
worldPosition.addRule(es2, QShaderNode::Rule("highp $type $value = $name;",
vertexPosition.setParameter("name", "vertexPosition");
vertexPosition.setParameter("qualifier", QVariant::fromValue<QShaderLanguage::StorageQualifier>(QShaderLanguage::Input));
vertexPosition.setParameter("type", QVariant::fromValue<QShaderLanguage::VariableType>(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;
}
}