Skip to content

Commit

Permalink
Fix performance issues when handling layout changed in Quick item views.
Browse files Browse the repository at this point in the history
When the layout changes, we mark all rows as changed but do not track
where the individual rows get moved.

The only reason why one would want to track the moves is to persist
the current item selection across a layout change. But even the previous
code did not achieve that. I'll create a follow up patch to this one
that also implements this behavior as seen in Qt Widget item views.

Note that removing this code brings a tremendous performance win
on larger models. The repeated calls to _q_itemsMoved triggered O(n^2)
behavior in the number of top items in the model. Even with "only"
tens of thousands of items in the model, a layout change became very
costly and took seconds on a beefy modern desktop machine.

Calling _q_itemsMoved in a loop is bad because it:

- leads to O(N^2) behavior within QQmlChangeSet when merging the small
  moves into the item view's current change set
- potentially triggers tons of binding/property updates when the cached
  model indices are updated in _q_itemsMoved

Removing this slow path, I did not yet find a behavior change to the
previous code. Instead, it just does it all much faster.

Change-Id: I67fa99a1c5d8e05d17497d29391da9458bd9bdd0
Task-number: QTBUG-51638
Reviewed-by: Daniel Vrátil <daniel.vratil@kdab.com>
Reviewed-by: Robin Burchell <robin.burchell@viroteck.net>
  • Loading branch information
milianw authored and tsdgeos committed Apr 26, 2016
1 parent 124e67a commit 84f61dd
Show file tree
Hide file tree
Showing 4 changed files with 1 addition and 43 deletions.
37 changes: 1 addition & 36 deletions src/qml/types/qqmldelegatemodel.cpp
Expand Up @@ -1559,29 +1559,6 @@ bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const
return false;
}

void QQmlDelegateModel::_q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
{
Q_D(QQmlDelegateModel);
if (!d->m_complete)
return;

if (hint == QAbstractItemModel::VerticalSortHint) {
d->m_storedPersistentIndexes.clear();
if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) {
return;
}

for (int i = 0; i < d->m_count; ++i) {
const QModelIndex index = d->m_adaptorModel.aim()->index(i, 0, d->m_adaptorModel.rootIndex);
d->m_storedPersistentIndexes.append(index);
}
} else if (hint == QAbstractItemModel::HorizontalSortHint) {
// Ignored
} else {
// Triggers model reset, no preparations for that are needed
}
}

void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint)
{
Q_D(QQmlDelegateModel);
Expand All @@ -1593,19 +1570,7 @@ void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &par
return;
}

for (int i = 0, c = d->m_storedPersistentIndexes.count(); i < c; ++i) {
const QPersistentModelIndex &index = d->m_storedPersistentIndexes.at(i);
if (i == index.row())
continue;

_q_itemsMoved(i, index.row(), 1);
}

d->m_storedPersistentIndexes.clear();

// layoutUpdate does not necessarily have any move changes, but it can
// also mean data changes. We can't detect what exactly has changed, so
// just emit it for all items
// mark all items as changed
_q_itemsChanged(0, d->m_count, QVector<int>());

} else if (hint == QAbstractItemModel::HorizontalSortHint) {
Expand Down
1 change: 0 additions & 1 deletion src/qml/types/qqmldelegatemodel_p.h
Expand Up @@ -146,7 +146,6 @@ private Q_SLOTS:
void _q_rowsRemoved(const QModelIndex &,int,int);
void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector<int> &);
void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint);
void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint);

private:
Expand Down
2 changes: 0 additions & 2 deletions src/qml/types/qqmldelegatemodel_p_p.h
Expand Up @@ -337,8 +337,6 @@ class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModel
};
QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount];
};

QList<QPersistentModelIndex> m_storedPersistentIndexes;
};

class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter
Expand Down
4 changes: 0 additions & 4 deletions src/qml/util/qqmladaptormodel.cpp
Expand Up @@ -470,8 +470,6 @@ class VDMAbstractItemModelDataType : public VDMModelDelegateDataType
vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
QObject::disconnect(aim, SIGNAL(modelReset()),
vdm, SLOT(_q_modelReset()));
QObject::disconnect(aim, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
vdm, SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
vdm, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
}
Expand Down Expand Up @@ -928,8 +926,6 @@ void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm,
vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()),
vdm, QQmlDelegateModel, SLOT(_q_modelReset()));
qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
vdm, QQmlDelegateModel, SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
vdm, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
} else {
Expand Down

0 comments on commit 84f61dd

Please sign in to comment.