diff options
author | Erik Verbruggen <erik.verbruggen@digia.com> | 2016-03-03 14:54:31 +0100 |
---|---|---|
committer | Erik Verbruggen <erik.verbruggen@theqtcompany.com> | 2016-03-04 13:54:50 +0000 |
commit | 5a9734c0d071b7490b3ef7edf5f080786dc89f79 (patch) | |
tree | ef8d18707d2f65df718ea785745d079ff3558fb8 /src/particles | |
parent | 554566788f585caaef14d929cee6557ad929e06e (diff) |
Particles: reduce excessive hash accesses to a more passable level.
By caching the group id in the particle emitter, and groups in the
painter.
Test case: samegame, 1 player, click 1 set of 3 stones, quit.
QQuickParticleSystem::emittersChanged(), before patch:
- 64M instr. inclusive
- 145,880 calls to findNode (29M instr.)
after:
- 21M instr. inclusive
- 0 calls to findNode
- 25 calls to QQuickParticlePainter::recalculateGroupIds (9800 instr.
inclusive).
Change-Id: I4aba9d50100513c6b7cdd230e30b3aecaf84485a
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
Diffstat (limited to 'src/particles')
-rw-r--r-- | src/particles/qquickcustomparticle.cpp | 11 | ||||
-rw-r--r-- | src/particles/qquickimageparticle.cpp | 55 | ||||
-rw-r--r-- | src/particles/qquickitemparticle.cpp | 38 | ||||
-rw-r--r-- | src/particles/qquickparticleemitter.cpp | 12 | ||||
-rw-r--r-- | src/particles/qquickparticleemitter_p.h | 19 | ||||
-rw-r--r-- | src/particles/qquickparticlepainter.cpp | 46 | ||||
-rw-r--r-- | src/particles/qquickparticlepainter_p.h | 37 | ||||
-rw-r--r-- | src/particles/qquickparticlesystem.cpp | 32 | ||||
-rw-r--r-- | src/particles/qquickparticlesystem_p.h | 32 | ||||
-rw-r--r-- | src/particles/qquicktrailemitter.cpp | 2 |
10 files changed, 192 insertions, 92 deletions
diff --git a/src/particles/qquickcustomparticle.cpp b/src/particles/qquickcustomparticle.cpp index 97cfdc7bf9..32b2fd847e 100644 --- a/src/particles/qquickcustomparticle.cpp +++ b/src/particles/qquickcustomparticle.cpp @@ -324,19 +324,18 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() return 0; } - if (m_groups.isEmpty()) + if (groups().isEmpty()) return 0; QQuickShaderEffectNode *rootNode = 0; QQuickShaderEffectMaterial *material = new QQuickShaderEffectMaterial; m_dirtyProgram = true; - foreach (const QString &str, m_groups){ - int gIdx = m_system->groupIds[str]; - int count = m_system->groupData[gIdx]->size(); + for (auto groupId : groupIds()) { + int count = m_system->groupData[groupId]->size(); QQuickShaderEffectNode* node = new QQuickShaderEffectNode(); - m_nodes.insert(gIdx, node); + m_nodes.insert(groupId, node); node->setMaterial(material); @@ -349,7 +348,7 @@ QQuickShaderEffectNode* QQuickCustomParticle::buildCustomNodes() node->setFlag(QSGNode::OwnsGeometry, true); PlainVertex *vertices = (PlainVertex *) g->vertexData(); for (int p=0; p < count; ++p) { - commit(gIdx, p); + commit(groupId, p); vertices[0].tx = 0; vertices[0].ty = 0; diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index c88a0b002b..5f2d1db663 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -1020,10 +1020,13 @@ void QQuickImageParticle::setEntryEffect(EntryEffect arg) void QQuickImageParticle::resetColor() { m_explicitColor = false; - foreach (const QString &str, m_groups) - foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) - if (d->colorOwner == this) + for (auto groupId : groupIds()) { + for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) { + if (d->colorOwner == this) { d->colorOwner = 0; + } + } + } m_color = QColor(); m_color_variation = 0.0f; m_redVariation = 0.0f; @@ -1036,10 +1039,13 @@ void QQuickImageParticle::resetColor() void QQuickImageParticle::resetRotation() { m_explicitRotation = false; - foreach (const QString &str, m_groups) - foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) - if (d->rotationOwner == this) + for (auto groupId : groupIds()) { + for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) { + if (d->rotationOwner == this) { d->rotationOwner = 0; + } + } + } m_rotation = 0; m_rotationVariation = 0; m_rotationVelocity = 0; @@ -1050,10 +1056,13 @@ void QQuickImageParticle::resetRotation() void QQuickImageParticle::resetDeformation() { m_explicitDeformation = false; - foreach (const QString &str, m_groups) - foreach (QQuickParticleData* d, m_system->groupData[m_system->groupIds[str]]->data) - if (d->deformationOwner == this) + for (auto groupId : groupIds()) { + for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) { + if (d->deformationOwner == this) { d->deformationOwner = 0; + } + } + } if (m_xVector) delete m_xVector; if (m_yVector) @@ -1255,9 +1264,9 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node) perfLevel = Simple; } - foreach (const QString &str, m_groups){//For sharing higher levels, need to have highest used so it renders - int gIdx = m_system->groupIds[str]; - foreach (QQuickParticlePainter* p, m_system->groupData[gIdx]->painters){ + for (auto groupId : groupIds()) { + //For sharing higher levels, need to have highest used so it renders + for (QQuickParticlePainter* p : qAsConst(m_system->groupData[groupId]->painters)) { QQuickImageParticle* other = qobject_cast<QQuickImageParticle*>(p); if (other){ if (other->perfLevel > perfLevel) { @@ -1382,16 +1391,15 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node) } m_nodes.clear(); - foreach (const QString &str, m_groups){ - int gIdx = m_system->groupIds[str]; - int count = m_system->groupData[gIdx]->size(); + for (auto groupId : groupIds()) { + int count = m_system->groupData[groupId]->size(); QSGGeometryNode* node = new QSGGeometryNode(); node->setMaterial(m_material); node->markDirty(QSGNode::DirtyMaterial); - m_nodes.insert(gIdx, node); - m_idxStarts.insert(gIdx, m_lastIdxStart); - m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, gIdx)); + m_nodes.insert(groupId, node); + m_idxStarts.insert(groupId, m_lastIdxStart); + m_startsIdx.append(qMakePair<int,int>(m_lastIdxStart, groupId)); m_lastIdxStart += count; //Create Particle Geometry @@ -1423,7 +1431,7 @@ void QQuickImageParticle::finishBuildParticleNodes(QSGNode** node) g->setDrawingMode(GL_TRIANGLES); for (int p=0; p < count; ++p) - commit(gIdx, p);//commit sets geometry for the node, has its own perfLevel switch + commit(groupId, p);//commit sets geometry for the node, has its own perfLevel switch if (perfLevel == Sprites) initTexCoords<SpriteVertex>((SpriteVertex*)g->vertexData(), vCount); @@ -1538,10 +1546,9 @@ void QQuickImageParticle::prepareNextFrame(QSGNode **node) void QQuickImageParticle::spritesUpdate(qreal time) { // Sprite progression handled CPU side, so as to have per-frame control. - foreach (const QString &str, m_groups) { - int gIdx = m_system->groupIds[str]; - foreach (QQuickParticleData* mainDatum, m_system->groupData[gIdx]->data) { - QSGGeometryNode *node = m_nodes[gIdx]; + for (auto groupId : groupIds()) { + for (QQuickParticleData* mainDatum : qAsConst(m_system->groupData[groupId]->data)) { + QSGGeometryNode *node = m_nodes[groupId]; if (!node) continue; //TODO: Interpolate between two different animations if it's going to transition next frame @@ -1549,7 +1556,7 @@ void QQuickImageParticle::spritesUpdate(qreal time) QQuickParticleData* datum = (mainDatum->animationOwner == this ? mainDatum : getShadowDatum(mainDatum)); int spriteIdx = 0; for (int i = 0; i<m_startsIdx.count(); i++) { - if (m_startsIdx[i].second == gIdx){ + if (m_startsIdx[i].second == groupId){ spriteIdx = m_startsIdx[i].first + datum->index; break; } diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp index 4cd8ee9db8..ba6c6f915d 100644 --- a/src/particles/qquickitemparticle.cpp +++ b/src/particles/qquickitemparticle.cpp @@ -231,10 +231,10 @@ void QQuickItemParticle::reset() // delete all managed items which had their logical particles cleared // but leave it alone if the logical particle is maintained QSet<QQuickItem*> lost = QSet<QQuickItem*>::fromList(m_managed); - foreach (const QString group, m_groups){ - int gIdx = m_system->groupIds[group]; - foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data) - lost.remove(d->delegate); + for (auto groupId : groupIds()) { + for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) { + lost.remove(d->delegate); + } } m_deletables.append(lost.toList()); //TODO: This doesn't yet handle calling detach on taken particles in the system reset case @@ -249,11 +249,12 @@ QSGNode* QQuickItemParticle::updatePaintNode(QSGNode* n, UpdatePaintNodeData* d) m_pleaseReset = false; //Refill loadables, delayed here so as to only happen once per frame max //### Constant resetting might lead to m_loadables never being populated when tick() occurs - foreach (const QString group, m_groups){ - int gIdx = m_system->groupIds[group]; - foreach (QQuickParticleData* d, m_system->groupData[gIdx]->data) - if (!d->delegate && d->t != -1 && d->stillAlive()) + for (auto groupId : groupIds()) { + for (QQuickParticleData* d : qAsConst(m_system->groupData[groupId]->data)) { + if (!d->delegate && d->t != -1 && d->stillAlive()) { m_loadables << d; + } + } } } prepareNextFrame(); @@ -276,31 +277,26 @@ void QQuickItemParticle::prepareNextFrame() return; //TODO: Size, better fade? - foreach (const QString &str, m_groups){ - const int gIdx = m_system->groupIds[str]; - const QVector<QQuickParticleData*> dataVector = m_system->groupData[gIdx]->data; - const int count = dataVector.size(); - - for (int i=0; i<count; i++){ - QQuickParticleData* data = dataVector.at(i); + for (auto groupId : groupIds()) { + for (QQuickParticleData* data : qAsConst(m_system->groupData[groupId]->data)) { QQuickItem* item = data->delegate; if (!item) continue; - qreal t = ((timeStamp/1000.0) - data->t) / data->lifeSpan; + float t = ((timeStamp / 1000.0f) - data->t) / data->lifeSpan; if (m_stasis.contains(item)) { data->t += dt;//Stasis effect continue; } - if (t >= 1.0){//Usually happens from load + if (t >= 1.0f){//Usually happens from load m_deletables << item; data->delegate = 0; }else{//Fade data->delegate->setVisible(true); if (m_fade){ - qreal o = 1.; - if (t<0.2) - o = t*5; - if (t>0.8) + float o = 1.f; + if (t <0.2f) + o = t * 5; + if (t > 0.8f) o = (1-t)*5; item->setOpacity(o); } diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp index 5e5c750e0e..7cce4d481a 100644 --- a/src/particles/qquickparticleemitter.cpp +++ b/src/particles/qquickparticleemitter.cpp @@ -237,6 +237,8 @@ QQuickParticleEmitter::QQuickParticleEmitter(QQuickItem *parent) : , m_reset_last(true) , m_last_timestamp(-1) , m_last_emission(0) + , m_groupIdNeedRecalculation(false) + , m_groupId(QQuickParticleGroupData::DefaultGroupID) { //TODO: Reset velocity/acc back to null vector? Or allow null pointer? @@ -257,6 +259,16 @@ bool QQuickParticleEmitter::isEmitConnected() IS_SIGNAL_CONNECTED(this, QQuickParticleEmitter, emitParticles, (QQmlV4Handle)); } +void QQuickParticleEmitter::reclaculateGroupId() const +{ + if (!m_system) { + m_groupId = QQuickParticleGroupData::InvalidID; + return; + } + m_groupId = m_system->groupIds.value(group(), QQuickParticleGroupData::InvalidID); + m_groupIdNeedRecalculation = m_groupId == QQuickParticleGroupData::InvalidID; +} + void QQuickParticleEmitter::componentComplete() { if (!m_system && qobject_cast<QQuickParticleSystem*>(parentItem())) diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h index bb77d132cc..9b114ad46b 100644 --- a/src/particles/qquickparticleemitter_p.h +++ b/src/particles/qquickparticleemitter_p.h @@ -119,6 +119,13 @@ public: return m_group; } + QQuickParticleGroupData::ID groupId() const + { + if (m_groupIdNeedRecalculation) + reclaculateGroupId(); + return m_groupId; + } + int particleDurationVariation() const { return m_particleDurationVariation; @@ -185,6 +192,7 @@ public Q_SLOTS: { if (m_system != arg) { m_system = arg; + m_groupIdNeedRecalculation = true; if (m_system) m_system->registerParticleEmitter(this); Q_EMIT systemChanged(arg); @@ -195,6 +203,7 @@ public Q_SLOTS: { if (m_group != arg) { m_group = arg; + m_groupIdNeedRecalculation = true; Q_EMIT groupChanged(arg); } } @@ -319,7 +328,6 @@ protected: int m_particleDurationVariation; bool m_enabled; QQuickParticleSystem* m_system; - QString m_group; QQuickParticleExtruder* m_extruder; QQuickParticleExtruder* m_defaultExtruder; QQuickParticleExtruder* effectiveExtruder(); @@ -350,7 +358,14 @@ protected: QPointF m_last_last_last_emitter; bool isEmitConnected(); -private: + +private: // methods + void reclaculateGroupId() const; + +private: // data + QString m_group; + mutable bool m_groupIdNeedRecalculation; + mutable QQuickParticleGroupData::ID m_groupId; QQuickDirection m_nullVector; }; diff --git a/src/particles/qquickparticlepainter.cpp b/src/particles/qquickparticlepainter.cpp index 134689713d..556d137fd4 100644 --- a/src/particles/qquickparticlepainter.cpp +++ b/src/particles/qquickparticlepainter.cpp @@ -64,9 +64,13 @@ QT_BEGIN_NAMESPACE If empty, it will paint the default particle group (""). */ -QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) : - QQuickItem(parent), - m_system(0), m_count(0), m_pleaseReset(true), m_window(0) +QQuickParticlePainter::QQuickParticlePainter(QQuickItem *parent) + : QQuickItem(parent) + , m_system(0) + , m_count(0) + , m_pleaseReset(true) + , m_window(0) + , m_groupIdsNeedRecalculation(false) { } @@ -89,11 +93,32 @@ void QQuickParticlePainter::componentComplete() QQuickItem::componentComplete(); } +void QQuickParticlePainter::recalculateGroupIds() const +{ + if (!m_system) { + m_groupIds.clear(); + return; + } + + m_groupIdsNeedRecalculation = false; + m_groupIds.clear(); + + for (const QString &str : groups()) { + QQuickParticleGroupData::ID groupId = m_system->groupIds.value(str, QQuickParticleGroupData::InvalidID); + if (groupId == QQuickParticleGroupData::InvalidID) { + // invalid data, not finished setting up, or whatever. Fallback: do not cache. + m_groupIdsNeedRecalculation = true; + } else { + m_groupIds.append(groupId); + } + } +} void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg) { if (m_system != arg) { m_system = arg; + m_groupIdsNeedRecalculation = true; if (m_system){ m_system->registerParticlePainter(this); reset(); @@ -102,6 +127,16 @@ void QQuickParticlePainter::setSystem(QQuickParticleSystem *arg) } } +void QQuickParticlePainter::setGroups(const QStringList &arg) +{ + if (m_groups != arg) { + m_groups = arg; + m_groupIdsNeedRecalculation = true; + //Note: The system watches this as it has to recalc things when groups change. It will request a reset if necessary + Q_EMIT groupsChanged(arg); + } +} + void QQuickParticlePainter::load(QQuickParticleData* d) { initialize(d->group, d->index); @@ -133,11 +168,6 @@ void QQuickParticlePainter::setCount(int c)//### TODO: some resizeing so that pa reset(); } -int QQuickParticlePainter::count() -{ - return m_count; -} - void QQuickParticlePainter::calcSystemOffset(bool resetPending) { if (!m_system || !parentItem()) diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h index 719dfdb3d8..064ce27fe8 100644 --- a/src/particles/qquickparticlepainter_p.h +++ b/src/particles/qquickparticlepainter_p.h @@ -64,25 +64,40 @@ class QQuickParticlePainter : public QQuickItem Q_PROPERTY(QQuickParticleSystem* system READ system WRITE setSystem NOTIFY systemChanged) Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) +public: // data + typedef QQuickParticleVarLengthArray<QQuickParticleGroupData::ID, 4> GroupIDs; + public: explicit QQuickParticlePainter(QQuickItem *parent = 0); //Data Interface to system void load(QQuickParticleData*); void reload(QQuickParticleData*); void setCount(int c); - int count(); + + int count() const + { + return m_count; + } + void performPendingCommits();//Called from updatePaintNode QQuickParticleSystem* system() const { return m_system; } - QStringList groups() const { return m_groups; } + const GroupIDs &groupIds() const + { + if (m_groupIdsNeedRecalculation) { + recalculateGroupIds(); + } + return m_groupIds; + } + void itemChange(ItemChange, const ItemChangeData &); Q_SIGNALS: @@ -94,14 +109,7 @@ Q_SIGNALS: public Q_SLOTS: void setSystem(QQuickParticleSystem* arg); - void setGroups(const QStringList &arg) - { - if (m_groups != arg) { - m_groups = arg; - //Note: The system watches this as it has to recalc things when groups change. It will request a reset if necessary - Q_EMIT groupsChanged(arg); - } - } + void setGroups(const QStringList &arg); void calcSystemOffset(bool resetPending = false); @@ -130,13 +138,18 @@ protected: friend class QQuickParticleSystem; int m_count; bool m_pleaseReset;//Used by subclasses, but it's a nice optimization to know when stuff isn't going to matter. - QStringList m_groups; QPointF m_systemOffset; QQuickWindow *m_window; -private: +private: // methods + void recalculateGroupIds() const; + +private: // data + QStringList m_groups; QSet<QPair<int,int> > m_pendingCommits; + mutable GroupIDs m_groupIds; + mutable bool m_groupIdsNeedRecalculation; }; QT_END_NAMESPACE diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index e91ab40ffa..8ccdec4792 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -902,22 +902,21 @@ void QQuickParticleSystem::loadPainter(QObject *p) QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p); Q_ASSERT(painter);//XXX - foreach (QQuickParticleGroupData* sg, groupData) - sg->painters.remove(painter); + for (QQuickParticleGroupData* sg : groupData) { + sg->painters.removeOne(painter); + } + int particleCount = 0; if (painter->groups().isEmpty()) {//Uses default particle - QStringList def; - def << QString(); + static QStringList def = QStringList() << QString(); painter->setGroups(def); particleCount += groupData[0]->size(); groupData[0]->painters << painter; } else { - foreach (const QString &group, painter->groups()) { - if (!group.isEmpty() && !groupIds.contains(group)) {//new group - new QQuickParticleGroupData(group, this); - } - particleCount += groupData[groupIds[group]]->size(); - groupData[groupIds[group]]->painters << painter; + for (auto groupId : painter->groupIds()) { + QQuickParticleGroupData *gd = groupData[groupId]; + particleCount += gd->size(); + gd->painters << painter; } } painter->setCount(particleCount); @@ -940,22 +939,23 @@ void QQuickParticleSystem::emittersChanged() } // Populate groups and set sizes. - for (int i = 0; i < m_emitters.count(); ++i) { + for (int i = 0; i < m_emitters.count(); ) { QQuickParticleEmitter *e = m_emitters.at(i); if (!e) { m_emitters.removeAt(i); - i--; continue; } - if (!e->group().isEmpty() && - !groupIds.contains(e->group())) { - new QQuickParticleGroupData(e->group(), this); + int groupId = e->groupId(); + if (groupId == QQuickParticleGroupData::InvalidID) { + groupId = (new QQuickParticleGroupData(e->group(), this))->index; previousSizes << 0; newSizes << 0; } - newSizes[groupIds[e->group()]] += e->particleCount(); + newSizes[groupId] += e->particleCount(); //###: Cull emptied groups? + + ++i; } //TODO: Garbage collection? diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index 9ddbb2ff38..2745e73053 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -65,6 +65,30 @@ QT_BEGIN_NAMESPACE +template<class T, int Prealloc> +class QQuickParticleVarLengthArray: public QVarLengthArray<T, Prealloc> +{ +public: + void insert(const T &element) + { + if (!this->contains(element)) { + this->append(element); + } + } + + bool removeOne(const T &element) + { + for (int i = 0; i < this->size(); ++i) { + if (this->at(i) == element) { + this->remove(i); + return true; + } + } + + return false; + } +}; + class QQuickParticleSystem; class QQuickParticleAffector; class QQuickParticleEmitter; @@ -110,6 +134,10 @@ private: }; class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticleGroupData { +public: // types + typedef int ID; + enum { InvalidID = -1, DefaultGroupID = 0 }; + public: QQuickParticleGroupData(const QString &name, QQuickParticleSystem* sys); ~QQuickParticleGroupData(); @@ -121,8 +149,8 @@ public: void setSize(int newSize); - const int index; - QSet<QQuickParticlePainter*> painters;//TODO: What if they are dynamically removed? + const ID index; + QQuickParticleVarLengthArray<QQuickParticlePainter*, 4> painters;//TODO: What if they are dynamically removed? //TODO: Refactor particle data list out into a separate class QVector<QQuickParticleData*> data; diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp index 89a68a0356..904421c296 100644 --- a/src/particles/qquicktrailemitter.cpp +++ b/src/particles/qquicktrailemitter.cpp @@ -179,7 +179,7 @@ void QQuickTrailEmitter::emitWindow(int timeStamp) qreal sizeAtEnd = m_particleEndSize >= 0 ? m_particleEndSize : m_particleSize; int gId = m_system->groupIds[m_follow]; - int gId2 = m_system->groupIds[m_group]; + int gId2 = groupId(); for (int i=0; i<m_system->groupData[gId]->data.count(); i++) { QQuickParticleData *d = m_system->groupData[gId]->data[i]; if (!d->stillAlive()){ |