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 <gunnar.sletta@jollamobile.com>
This commit is contained in:
Michael Brasser 2014-04-22 21:19:50 -05:00
parent 6b31418a1b
commit ee616b3905
7 changed files with 131 additions and 45 deletions

View File

@ -271,6 +271,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickItem, 2>(uri, 2, 4, "Item");
qmlRegisterType<QQuickMouseArea, 1>(uri, 2, 4, "MouseArea");
qmlRegisterType<QQuickShaderEffect, 1>(uri, 2, 4, "ShaderEffect");
}
static void initResources()

View File

@ -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 <name>" declare a
texture copy, set \l supportsAtlasTextures for simple shaders using
qt_MultiTexCoord0, or for each "uniform sampler2D <name>" declare a
"uniform vec4 qt_SubRect_<name>" 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<QQuickShaderEffectMaterial *>(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;
}

View File

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

View File

@ -70,7 +70,7 @@ QQuickGridMesh::QQuickGridMesh(QObject *parent)
connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged()));
}
QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &dstRect)
QSGGeometry *QQuickGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &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<QSGGeometry::Point2D *>(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());

View File

@ -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<QByteArray> &attributes, const QRectF &rect) = 0;
virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &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<QByteArray> &attributes, const QRectF &rect);
virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &srcRect, const QRectF &rect);
virtual QString log() const { return m_log; }
void setResolution(const QSize &res);

View File

@ -41,7 +41,6 @@
#include <private/qquickshadereffectnode_p.h>
#include "qquickshadereffectmesh_p.h"
#include "qquickshadereffect_p.h"
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/private/qsgrenderer_p.h>
@ -67,7 +66,6 @@ protected:
const QQuickShaderEffectMaterialKey m_key;
QVector<const char *> m_attributeNames;
const QVector<QByteArray> m_attributes;
QString m_log;
bool m_compiled;
@ -77,7 +75,6 @@ protected:
QQuickCustomMaterialShader::QQuickCustomMaterialShader(const QQuickShaderEffectMaterialKey &key, const QVector<QByteArray> &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<QQuickShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > 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<const QQuickShaderEffectMaterial *>(other);
if (specialType != other.specialType)
return false;
if (name != other.name)
return false;
if (specialType == UniformData::Sampler) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(value));
QQuickItem *otherSource = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(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<const QQuickShaderEffectMaterial *>(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)

View File

@ -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<UniformData> uniforms[QQuickShaderEffectMaterialKey::ShaderTypeCount];
QVector<QSGTextureProvider *> textureProviders;
CullMode cullMode;
bool supportsAtlasTextures;
void setProgramSource(const QQuickShaderEffectMaterialKey &source);
void updateTextures() const;