From: Kim Motoyoshi Kalland Date: Tue, 1 Nov 2011 17:11:38 +0000 (+0100) Subject: Improved QQuickShaderEffect::lookThroughShaderCode() performance. X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=fd58c5fc9fa5dc1aa975cf40a5a58945671e22ee;p=konrad%2Fqtdeclarative.git Improved QQuickShaderEffect::lookThroughShaderCode() performance. Removed use of regexp. Skip comments and compiler directives in the shader code. Don't look through default shader code. Task-number: QTBUG-22423 Change-Id: Ie08cd8288ba7d7a33f1e3b0dc2ab5f2bedad04dd Reviewed-by: Yoann Lopes --- diff --git a/src/declarative/items/qquickshadereffect.cpp b/src/declarative/items/qquickshadereffect.cpp index 03247f9..ea7350d 100644 --- a/src/declarative/items/qquickshadereffect.cpp +++ b/src/declarative/items/qquickshadereffect.cpp @@ -474,15 +474,25 @@ void QQuickShaderEffect::reset() void QQuickShaderEffect::updateProperties() { - QByteArray vertexCode = m_source.vertexCode; - QByteArray fragmentCode = m_source.fragmentCode; - if (vertexCode.isEmpty()) - vertexCode = qt_default_vertex_code; - if (fragmentCode.isEmpty()) - fragmentCode = qt_default_fragment_code; - - lookThroughShaderCode(vertexCode); - lookThroughShaderCode(fragmentCode); + if (m_source.vertexCode.isEmpty()) { + m_source.attributeNames.append(QByteArray(qt_position_attribute_name)); + m_source.attributeNames.append(QByteArray(qt_texcoord_attribute_name)); + m_source.respectsMatrix = true; + } else { + lookThroughShaderCode(m_source.vertexCode); + } + if (m_source.fragmentCode.isEmpty()) { + m_source.respectsOpacity = true; + QByteArray name("source"); + m_source.uniformNames.insert(name); + SourceData d; + d.mapper = new QSignalMapper; + d.name = name; + d.sourceObject = 0; + m_sources.append(d); + } else { + lookThroughShaderCode(m_source.fragmentCode); + } if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name)) qWarning("QQuickShaderEffect: Missing reference to \'%s\'.", qt_position_attribute_name); @@ -501,38 +511,149 @@ void QQuickShaderEffect::updateProperties() connectPropertySignals(); } -void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code) -{ - // Regexp for matching attributes and uniforms. - // In human readable form: attribute|uniform [lowp|mediump|highp] - static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)")); - Q_ASSERT(re.isValid()); +namespace { + + enum VariableQualifier { + AttributeQualifier, + UniformQualifier + }; - int pos = -1; + inline bool qt_isalpha(char c) + { + char ch = c | 0x20; + return (ch >= 'a' && ch <= 'z') || c == '_'; + } - QString wideCode = QString::fromLatin1(code.constData(), code.size()); + inline bool qt_isalnum(char c) + { + return qt_isalpha(c) || (c >= '0' && c <= '9'); + } - while ((pos = re.indexIn(wideCode, pos + 1)) != -1) { - QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute - QByteArray type = re.cap(2).toLatin1(); // type - QByteArray name = re.cap(3).toLatin1(); // variable name + inline bool qt_isspace(char c) + { + return c == ' ' || (c >= 0x09 && c <= 0x0d); + } - if (decl == "attribute") { - m_source.attributeNames.append(name); + // Returns -1 if not found, returns index to first character after the name if found. + int qt_search_for_variable(const char *s, int length, int index, VariableQualifier &decl, + int &typeIndex, int &typeLength, + int &nameIndex, int &nameLength) + { + enum Identifier { + QualifierIdentifier, // Base state + PrecisionIdentifier, + TypeIdentifier, + NameIdentifier + }; + Identifier expected = QualifierIdentifier; + bool compilerDirectiveExpected = index == 0; + + while (index < length) { + // Skip whitespace. + while (qt_isspace(s[index])) { + compilerDirectiveExpected |= s[index] == '\n'; + ++index; + } + + if (qt_isalpha(s[index])) { + // Read identifier. + int identifierIndex = index; + ++index; + while (qt_isalnum(s[index])) + ++index; + int identifierLength = index - identifierIndex; + + switch (expected) { + case QualifierIdentifier: + if (qstrncmp("attribute", s + identifierIndex, identifierLength) == 0) { + decl = AttributeQualifier; + expected = PrecisionIdentifier; + } else if (qstrncmp("uniform", s + identifierIndex, identifierLength) == 0) { + decl = UniformQualifier; + expected = PrecisionIdentifier; + } + break; + case PrecisionIdentifier: + if (qstrncmp("lowp", s + identifierIndex, identifierLength) == 0 + || qstrncmp("mediump", s + identifierIndex, identifierLength) == 0 + || qstrncmp("highp", s + identifierIndex, identifierLength) == 0) + { + expected = TypeIdentifier; + break; + } + // Fall through. + case TypeIdentifier: + typeIndex = identifierIndex; + typeLength = identifierLength; + expected = NameIdentifier; + break; + case NameIdentifier: + nameIndex = identifierIndex; + nameLength = identifierLength; + return index; // Attribute or uniform declaration found. Return result. + default: + break; + } + } else if (s[index] == '#' && compilerDirectiveExpected) { + // Skip compiler directives. + ++index; + while (index < length && (s[index] != '\n' || s[index - 1] == '\\')) + ++index; + } else if (s[index] == '/' && s[index + 1] == '/') { + // Skip comments. + index += 2; + while (index < length && s[index] != '\n') + ++index; + } else if (s[index] == '/' && s[index + 1] == '*') { + // Skip comments. + index += 2; + while (index < length && (s[index] != '*' || s[index + 1] != '/')) + ++index; + if (index < length) + index += 2; // Skip star-slash. + } else { + expected = QualifierIdentifier; + ++index; + } + compilerDirectiveExpected = false; + } + return -1; + } +} + +void QQuickShaderEffect::lookThroughShaderCode(const QByteArray &code) +{ + int index = 0; + int typeIndex, typeLength, nameIndex, nameLength; + const char *s = code.constData(); + VariableQualifier decl; + while ((index = qt_search_for_variable(s, code.size(), index, decl, typeIndex, typeLength, + nameIndex, nameLength)) != -1) + { + if (decl == AttributeQualifier) { + m_source.attributeNames.append(QByteArray(s + nameIndex, nameLength)); } else { - Q_ASSERT(decl == "uniform"); + Q_ASSERT(decl == UniformQualifier); + + const int matLen = sizeof("qt_Matrix") - 1; + const int opLen = sizeof("qt_Opacity") - 1; + const int mvpMatLen = sizeof("qt_ModelViewProjectionMatrix") - 1; + const int sampLen = sizeof("sampler2D") - 1; - if (name == "qt_Matrix") { + if (nameLength == matLen && qstrncmp("qt_Matrix", s + nameIndex, matLen) == 0) { m_source.respectsMatrix = true; - } else if (name == "qt_ModelViewProjectionMatrix") { + } else if (nameLength == mvpMatLen && qstrncmp("qt_ModelViewProjectionMatrix", + s + nameIndex, mvpMatLen) == 0) + { // TODO: Remove after grace period. qWarning("ShaderEffect: qt_ModelViewProjectionMatrix is deprecated. Use qt_Matrix instead."); m_source.respectsMatrix = true; - } else if (name == "qt_Opacity") { + } else if (nameLength == opLen && qstrncmp("qt_Opacity", s + nameIndex, opLen) == 0) { m_source.respectsOpacity = true; } else { + QByteArray name(s + nameIndex, nameLength); m_source.uniformNames.insert(name); - if (type == "sampler2D") { + if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0) { SourceData d; d.mapper = new QSignalMapper; d.name = name;