From: Michael Brasser Date: Wed, 23 Apr 2014 02:19:50 +0000 (-0500) Subject: Allow simple ShaderEffects to be batched by the renderer. X-Git-Tag: v5.3.99+beta1~208 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ee616b3905106a3eedef9ee964ab283ef45c7dbc;p=platform%2Fupstream%2Fqtdeclarative.git Allow simple ShaderEffects to be batched by the renderer. Identical ShaderEffects that use the standard vertex shader with a single source texture, and that set supportsAtlasTextures, are now candidates for batching. Task-number: QTBUG-37914 Change-Id: Ib0ce58647a8c7c48e88bd84cf2645f1a8f28691f Reviewed-by: Gunnar Sletta --- diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 22a6eeb..2951fc7 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -271,6 +271,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri, 2, 4, "Item"); qmlRegisterType(uri, 2, 4, "MouseArea"); + qmlRegisterType(uri, 2, 4, "ShaderEffect"); } static void initResources() diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 8bcd2e6..9dfce3f 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -565,7 +565,8 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, position (0, 0), the bottom-right (\l{Item::width}{width}, \l{Item::height}{height}). \li attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left - coordinate is (0, 0), the bottom-right (1, 1). + coordinate is (0, 0), the bottom-right (1, 1). If \l supportsAtlasTextures + is true, coordinates will be based on position in the atlas instead. \endlist In addition, any property that can be mapped to an OpenGL Shading Language @@ -594,7 +595,8 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, it is by default copied from the texture atlas into a stand-alone texture so that the texture coordinates span from 0 to 1, and you get the expected wrap modes. However, this will increase the memory usage. To avoid the - texture copy, you can for each "uniform sampler2D " declare a + texture copy, set \l supportsAtlasTextures for simple shaders using + qt_MultiTexCoord0, or for each "uniform sampler2D " declare a "uniform vec4 qt_SubRect_" which will be assigned the texture's normalized source rectangle. For stand-alone textures, the source rectangle is [0, 1]x[0, 1]. For textures in an atlas, the source rectangle corresponds @@ -665,6 +667,8 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) , m_dirtyParseLog(true) , m_dirtyMesh(true) , m_dirtyGeometry(true) + , m_customVertexShader(false) + , m_supportsAtlasTextures(false) { setFlag(QQuickItem::ItemHasContents); } @@ -718,6 +722,7 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code) m_common.source.sourceCode[Key::VertexShader] = code; m_dirtyProgram = true; m_dirtyParseLog = true; + m_customVertexShader = true; if (isComponentComplete()) m_common.updateShader(this, Key::VertexShader); @@ -828,6 +833,31 @@ void QQuickShaderEffect::setCullMode(CullMode face) emit cullModeChanged(); } +/*! + \qmlproperty bool QtQuick::ShaderEffect::supportsAtlasTextures + + Set this property true to indicate that the ShaderEffect is able to + use the default source texture without first removing it from an atlas. + In this case the range of qt_MultiTexCoord0 will based on the position of + the texture within the atlas, rather than (0,0) to (1,1). + + Setting this to true may enable some optimizations. + + The default value is false. + + \since 5.4 + \since QtQuick 2.4 +*/ + +void QQuickShaderEffect::setSupportsAtlasTextures(bool supports) +{ + if (supports == m_supportsAtlasTextures) + return; + m_supportsAtlasTextures = supports; + updateGeometry(); + emit supportsAtlasTexturesChanged(); +} + QString QQuickShaderEffect::parseLog() { if (m_dirtyParseLog) { @@ -940,39 +970,6 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa connect(node, SIGNAL(logAndStatusChanged(QString,int)), this, SLOT(updateLogAndStatus(QString,int))); } - if (m_dirtyMesh) { - node->setGeometry(0); - m_dirtyMesh = false; - m_dirtyGeometry = true; - } - - if (m_dirtyGeometry) { - node->setFlag(QSGNode::OwnsGeometry, false); - QSGGeometry *geometry = node->geometry(); - QRectF rect(0, 0, width(), height()); - QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; - - geometry = mesh->updateGeometry(geometry, m_common.attributes, rect); - if (!geometry) { - QString log = mesh->log(); - if (!log.isNull()) { - m_log = parseLog(); - m_log += QLatin1String("*** Mesh ***\n"); - m_log += log; - m_status = Error; - emit logChanged(); - emit statusChanged(); - } - delete node; - return 0; - } - - node->setGeometry(geometry); - node->setFlag(QSGNode::OwnsGeometry, true); - - m_dirtyGeometry = false; - } - QQuickShaderEffectMaterial *material = static_cast(node->material()); // Update blending @@ -1014,6 +1011,60 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa m_dirtyUniforms = m_dirtyUniformValues = m_dirtyTextureProviders = false; } + int textureCount = material->textureProviders.size(); + bool preventBatching = m_customVertexShader || textureCount > 1 || (textureCount > 0 && !m_supportsAtlasTextures); + + QRectF srcRect(0, 0, 1, 1); + if (m_supportsAtlasTextures && textureCount != 0) { + if (QSGTextureProvider *provider = material->textureProviders.at(0)) { + if (provider->texture()) + srcRect = provider->texture()->normalizedTextureSubRect(); + } + } + + if (bool(material->flags() & QSGMaterial::RequiresFullMatrix) != preventBatching) { + material->setFlag(QSGMaterial::RequiresFullMatrix, preventBatching); + node->markDirty(QSGNode::DirtyMaterial); + } + + if (material->supportsAtlasTextures != m_supportsAtlasTextures) { + material->supportsAtlasTextures = m_supportsAtlasTextures; + node->markDirty(QSGNode::DirtyMaterial); + } + + if (m_dirtyMesh) { + node->setGeometry(0); + m_dirtyMesh = false; + m_dirtyGeometry = true; + } + + if (m_dirtyGeometry) { + node->setFlag(QSGNode::OwnsGeometry, false); + QSGGeometry *geometry = node->geometry(); + QRectF rect(0, 0, width(), height()); + QQuickShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh; + + geometry = mesh->updateGeometry(geometry, m_common.attributes, srcRect, rect); + if (!geometry) { + QString log = mesh->log(); + if (!log.isNull()) { + m_log = parseLog(); + m_log += QLatin1String("*** Mesh ***\n"); + m_log += log; + m_status = Error; + emit logChanged(); + emit statusChanged(); + } + delete node; + return 0; + } + + node->setGeometry(geometry); + node->setFlag(QSGNode::OwnsGeometry, true); + + m_dirtyGeometry = false; + } + return node; } diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 8b613b4..14d11a7 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -99,6 +99,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffect : public QQuickItem Q_PROPERTY(CullMode cullMode READ cullMode WRITE setCullMode NOTIFY cullModeChanged) Q_PROPERTY(QString log READ log NOTIFY logChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(bool supportsAtlasTextures READ supportsAtlasTextures WRITE setSupportsAtlasTextures NOTIFY supportsAtlasTexturesChanged REVISION 1) Q_ENUMS(CullMode) Q_ENUMS(Status) @@ -138,6 +139,9 @@ public: QString log() const { return m_log; } Status status() const { return m_status; } + bool supportsAtlasTextures() const { return m_supportsAtlasTextures; } + void setSupportsAtlasTextures(bool supports); + QString parseLog(); virtual bool event(QEvent *); @@ -150,6 +154,7 @@ Q_SIGNALS: void cullModeChanged(); void logChanged(); void statusChanged(); + void supportsAtlasTexturesChanged(); protected: virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); @@ -187,6 +192,8 @@ private: uint m_dirtyParseLog : 1; uint m_dirtyMesh : 1; uint m_dirtyGeometry : 1; + uint m_customVertexShader : 1; + uint m_supportsAtlasTextures : 1; }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp index bc97a42..cf2f8fa 100644 --- a/src/quick/items/qquickshadereffectmesh.cpp +++ b/src/quick/items/qquickshadereffectmesh.cpp @@ -70,7 +70,7 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent) connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged())); } -QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &dstRect) +QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &srcRect, const QRectF &dstRect) { int vmesh = m_resolution.height(); int hmesh = m_resolution.width(); @@ -125,7 +125,6 @@ QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector QSGGeometry::Point2D *vdata = static_cast(geometry->vertexData()); - QRectF srcRect(0, 0, 1, 1); for (int iy = 0; iy <= vmesh; ++iy) { float fy = iy / float(vmesh); float y = float(dstRect.top()) + fy * float(dstRect.height()); diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h index ea92490..99dbaca 100644 --- a/src/quick/items/qquickshadereffectmesh_p.h +++ b/src/quick/items/qquickshadereffectmesh_p.h @@ -62,7 +62,7 @@ class QQuickShaderEffectMesh : public QObject public: QQuickShaderEffectMesh(QObject *parent = 0); // If 'geometry' != 0, 'attributes' is the same as last time the function was called. - virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect) = 0; + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &srcRect, const QRectF &rect) = 0; // If updateGeometry() fails, the reason should appear in the log. virtual QString log() const { return QString(); } @@ -77,7 +77,7 @@ class QQuickGridMesh : public QQuickShaderEffectMesh Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged) public: QQuickGridMesh(QObject *parent = 0); - virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &rect); + virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector &attributes, const QRectF &srcRect, const QRectF &rect); virtual QString log() const { return m_log; } void setResolution(const QSize &res); diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index fcf5591..95d4d58 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -41,7 +41,6 @@ #include -#include "qquickshadereffectmesh_p.h" #include "qquickshadereffect_p.h" #include #include @@ -67,7 +66,6 @@ protected: const QQuickShaderEffectMaterialKey m_key; QVector m_attributeNames; - const QVector m_attributes; QString m_log; bool m_compiled; @@ -77,7 +75,6 @@ protected: QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector &attributes) : m_key(key) - , m_attributes(attributes) , m_compiled(false) , m_initialized(false) { @@ -146,7 +143,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri if (loc >= 0) { QRectF r = texture->normalizedTextureSubRect(); program()->setUniformValue(loc, r.x(), r.y(), r.width(), r.height()); - } else if (texture->isAtlasTexture()) { + } else if (texture->isAtlasTexture() && (idx != 0 || !material->supportsAtlasTextures)) { texture = texture->removedFromAtlas(); } texture->bind(); @@ -346,6 +343,7 @@ QHash > QQuickSha QQuickShaderEffectMaterial::QQuickShaderEffectMaterial(QQuickShaderEffectNode *node) : cullMode(NoCulling) + , supportsAtlasTextures(false) , m_node(node) , m_emittedLogChanged(false) { @@ -362,9 +360,36 @@ QSGMaterialShader *QQuickShaderEffectMaterial::createShader() const return new QQuickCustomMaterialShader(m_source, attributes); } -int QQuickShaderEffectMaterial::compare(const QSGMaterial *other) const +bool QQuickShaderEffectMaterial::UniformData::operator == (const UniformData &other) const { - return this - static_cast(other); + if (specialType != other.specialType) + return false; + if (name != other.name) + return false; + + if (specialType == UniformData::Sampler) { + QQuickItem *source = qobject_cast(qvariant_cast(value)); + QQuickItem *otherSource = qobject_cast(qvariant_cast(other.value)); + if (!source || !otherSource || !source->isTextureProvider() || !otherSource->isTextureProvider()) + return false; + return source->textureProvider()->texture()->textureId() == otherSource->textureProvider()->texture()->textureId(); + } else { + return value == other.value; + } +} + +int QQuickShaderEffectMaterial::compare(const QSGMaterial *o) const +{ + const QQuickShaderEffectMaterial *other = static_cast(o); + if (cullMode != other->cullMode) + return 1; + if (supportsAtlasTextures != other->supportsAtlasTextures) + return 1; + for (int shaderType = 0; shaderType < QQuickShaderEffectMaterialKey::ShaderTypeCount; ++shaderType) { + if (uniforms[shaderType] != other->uniforms[shaderType]) + return 1; + } + return 0; } void QQuickShaderEffectMaterial::setProgramSource(const QQuickShaderEffectMaterialKey &source) diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index 232dd86..53c2cb9 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -82,6 +82,8 @@ public: QByteArray name; QVariant value; SpecialType specialType; + + bool operator == (const UniformData &other) const; }; enum CullMode @@ -100,6 +102,7 @@ public: QVector uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount]; QVector textureProviders; CullMode cullMode; + bool supportsAtlasTextures; void setProgramSource(const QQuickShaderEffectMaterialKey &source); void updateTextures() const;