summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2022-10-25 07:48:13 +0200
committerPaul Lemire <paul.lemire@kdab.com>2022-11-02 06:08:08 +0000
commitede970dc36837f403285b744ce1558778c4e01d5 (patch)
tree6a2ace32c0d0a713cf167ed759ff315969d3708a
parent611de02eccc5e742513545eb0a0925dd3b8d0561 (diff)
Quick3DNodeInstantiator: fix crash when using async
Something in the asynchronous creation of elements in QtQuick has been changed leading to elements of the instantiator being created out of order. QQmlInstantiator and friends had been updated at the time when the QtQuick changes were made but Quick3DNodeInstantiator was left out. This patch incorporate changes that have been made into QQmlInstantiator to correct out of order node creation. Task-number: QTBUG-56368 Change-Id: I2aa7499e6dc08be329cd42ded7377acd4088e0c7 Reviewed-by: Mike Krus <mike.krus@kdab.com> (cherry picked from commit de7fc9d250db39185151b0a100519800f42556fd) Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp40
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/data/createMultipleAsync.qml13
-rw-r--r--tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp45
3 files changed, 90 insertions, 8 deletions
diff --git a/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp b/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp
index cc50af786..e9aa6d080 100644
--- a/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp
+++ b/src/quick3d/quick3d/items/quick3dnodeinstantiator.cpp
@@ -73,6 +73,7 @@ public:
#endif
void _q_createdItem(int, QObject *);
void _q_modelUpdated(const QQmlChangeSet &, bool);
+ QObject *modelObject(int index, bool async);
bool m_componentComplete:1;
bool m_effectiveReset:1;
@@ -81,6 +82,7 @@ public:
#if QT_CONFIG(qml_delegate_model)
bool m_ownModel:1;
#endif
+ int m_requestedIndex;
QVariant m_model;
QQmlInstanceModel *m_instanceModel;
QQmlComponent *m_delegate;
@@ -99,6 +101,7 @@ Quick3DNodeInstantiatorPrivate::Quick3DNodeInstantiatorPrivate()
#if QT_CONFIG(qml_delegate_model)
, m_ownModel(false)
#endif
+ , m_requestedIndex(-1)
, m_model(QVariant(1))
, m_instanceModel(0)
, m_delegate(0)
@@ -129,6 +132,14 @@ void Quick3DNodeInstantiatorPrivate::clear()
emit q->objectChanged();
}
+QObject *Quick3DNodeInstantiatorPrivate::modelObject(int index, bool async)
+{
+ m_requestedIndex = index;
+ QObject *o = m_instanceModel->object(index, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
+ m_requestedIndex = -1;
+ return o;
+}
+
void Quick3DNodeInstantiatorPrivate::regenerate()
{
Q_Q(Quick3DNodeInstantiator);
@@ -146,8 +157,7 @@ void Quick3DNodeInstantiatorPrivate::regenerate()
}
for (int i = 0; i < m_instanceModel->count(); i++) {
- QObject *object = m_instanceModel->object(i, m_async ?
- QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
+ QObject *object = modelObject(i, m_async);
// If the item was already created we won't get a createdItem
if (object)
_q_createdItem(i, object);
@@ -161,8 +171,19 @@ void Quick3DNodeInstantiatorPrivate::_q_createdItem(int idx, QObject *item)
Q_Q(Quick3DNodeInstantiator);
if (m_objects.contains(item)) //Case when it was created synchronously in regenerate
return;
+ if (m_requestedIndex != idx) // Asynchronous creation, reference the object |
+ (void)m_instanceModel->object(idx);
static_cast<QNode *>(item)->setParent(q->parentNode());
- m_objects.insert(idx, item);
+ if (m_objects.size() < idx + 1) {
+ int modelCount = m_instanceModel->count();
+ if (m_objects.capacity() < modelCount)
+ m_objects.reserve(modelCount);
+ m_objects.resize(idx + 1);
+ }
+ if (QObject *o = m_objects.at(idx))
+ m_instanceModel->release(o);
+ m_objects.replace(idx, item);
+
if (m_objects.count() == 1)
emit q->objectChanged();
emit q->objectAdded(idx, item);
@@ -213,11 +234,14 @@ void Quick3DNodeInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &change
QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId);
m_objects = m_objects.mid(0, index) + movedObjects + m_objects.mid(index);
} else for (int i = 0; i < insert.count; ++i) {
- int modelIndex = index + i;
- QObject *obj = m_instanceModel->object(modelIndex, m_async ?
- QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
- if (obj)
- _q_createdItem(modelIndex, obj);
+ if (insert.index <= m_objects.count())
+ m_objects.insert(insert.index, insert.count, nullptr);
+ for (int i = 0; i < insert.count; ++i) {
+ int modelIndex = index + i;
+ QObject *obj = modelObject(modelIndex, m_async);
+ if (obj)
+ _q_createdItem(modelIndex, obj);
+ }
}
difference += insert.count;
}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultipleAsync.qml b/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultipleAsync.qml
new file mode 100644
index 000000000..be6c46582
--- /dev/null
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/data/createMultipleAsync.qml
@@ -0,0 +1,13 @@
+import QtQml 2.1
+import Qt3D.Core 2.0
+
+Entity {
+ NodeInstantiator {
+ model: 10
+ asynchronous: true
+ delegate: Entity {
+ property bool success: true
+ property int idx: index
+ }
+ }
+}
diff --git a/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp b/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp
index 501b3d9bf..1e7d6c593 100644
--- a/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp
+++ b/tests/auto/quick3d/quick3dnodeinstantiator/tst_quick3dnodeinstantiator.cpp
@@ -39,6 +39,7 @@
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlincubator.h>
using namespace Qt3DCore::Quick;
@@ -55,6 +56,8 @@ private slots:
void activeProperty();
void intModelChange();
void createAndRemove();
+ void asynchronous_data();
+ void asynchronous();
};
void tst_quick3dnodeinstantiator::createNone()
@@ -251,6 +254,48 @@ void tst_quick3dnodeinstantiator::createAndRemove()
QCOMPARE(object->property("datum").toString(), names[i]);
}
}
+
+void tst_quick3dnodeinstantiator::asynchronous_data()
+{
+ QTest::addColumn<bool>("asyncIncubator");
+ QTest::addColumn<QString>("fileName");
+
+ QTest::newRow("Asynchronous Instantiator") << false << "createMultipleAsync.qml";
+ QTest::newRow("Nested-asynchronous Instantiator") << true << "createMultiple.qml";
+}
+
+void tst_quick3dnodeinstantiator::asynchronous()
+{
+ QFETCH(bool, asyncIncubator);
+ QFETCH(QString, fileName);
+
+ QQmlEngine engine;
+ QQmlIncubationController incubationController;
+ engine.setIncubationController(&incubationController);
+ QQmlComponent component(&engine, testFileUrl(fileName));
+ QQmlIncubator incubator(asyncIncubator ? QQmlIncubator::Asynchronous : QQmlIncubator::Synchronous);
+ component.create(incubator);
+ while (!incubator.isReady())
+ incubationController.incubateFor(10);
+ QObject *rootObject = incubator.object();
+ QVERIFY(rootObject != nullptr);
+ while (incubationController.incubatingObjectCount() > 0)
+ incubationController.incubateFor(10);
+ Quick3DNodeInstantiator *instantiator = rootObject->findChild<Quick3DNodeInstantiator *>();
+ QVERIFY(instantiator != nullptr);
+ QCOMPARE(instantiator->isActive(), true);
+ QCOMPARE(instantiator->count(), 10);
+
+ for (int i=0; i<10; i++) {
+ QObject *object = instantiator->objectAt(i);
+ QVERIFY(object);
+ QCOMPARE(object->parent(), rootObject);
+ QCOMPARE(object->property("success").toBool(), true);
+ QCOMPARE(object->property("idx").toInt(), i);
+ }
+}
+
+
QTEST_MAIN(tst_quick3dnodeinstantiator)
#include "tst_quick3dnodeinstantiator.moc"