aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2016-03-03 14:54:31 +0100
committerErik Verbruggen <erik.verbruggen@theqtcompany.com>2016-03-04 13:54:50 +0000
commit5a9734c0d071b7490b3ef7edf5f080786dc89f79 (patch)
treeef8d18707d2f65df718ea785745d079ff3558fb8
parent554566788f585caaef14d929cee6557ad929e06e (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>
-rw-r--r--src/particles/qquickcustomparticle.cpp11
-rw-r--r--src/particles/qquickimageparticle.cpp55
-rw-r--r--src/particles/qquickitemparticle.cpp38
-rw-r--r--src/particles/qquickparticleemitter.cpp12
-rw-r--r--src/particles/qquickparticleemitter_p.h19
-rw-r--r--src/particles/qquickparticlepainter.cpp46
-rw-r--r--src/particles/qquickparticlepainter_p.h37
-rw-r--r--src/particles/qquickparticlesystem.cpp32
-rw-r--r--src/particles/qquickparticlesystem_p.h32
-rw-r--r--src/particles/qquicktrailemitter.cpp2
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()){