From efe1926598c69a09c9365673bba6961a83936d49 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 17 Oct 2017 16:54:22 +0200 Subject: More fine-grained deferred property execution This allows Qt Quick Controls 2 to defer the execution of certain building blocks until needed. For example, a button control can defer its background item so that the default background is not executed at all when replaced by a custom background. First of all, this gives a massive performance boost for customized controls. Secondly, this avoids the most burning issue in QQC2, problems with asynchronous incubation ("Object destroyed during incubation"). Task-number: QTBUG-50992 Change-Id: If3616c9dac70e3a474a20070ad0452874d267164 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmldata_p.h | 3 ++ src/qml/qml/qqmlengine.cpp | 35 ++++++++++++++-- src/qml/qml/qqmlobjectcreator.cpp | 85 +++++++++++++++++++++++++++++++++++---- src/qml/qml/qqmlobjectcreator_p.h | 3 +- 4 files changed, 113 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index d692feb975..63994a392d 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -76,6 +76,7 @@ class QQmlNotifierEndpoint; namespace QV4 { namespace CompiledData { struct CompilationUnit; +struct Binding; } } @@ -216,12 +217,14 @@ public: struct DeferredData { unsigned int deferredIdx; + QMultiHash bindings; QV4::CompiledData::CompilationUnit *compilationUnit;//Not always the same as the other compilation unit QQmlContextData *context;//Could be either context or outerContext }; QV4::CompiledData::CompilationUnit *compilationUnit; QVector deferredData; + void deferData(int objectIndex, QV4::CompiledData::CompilationUnit *, QQmlContextData *); void releaseDeferredData(); QV4::WeakValue jsWrapper; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index d99bec4c52..612c3439c1 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1639,13 +1639,40 @@ void QQmlData::NotifyList::layout() todo = 0; } +void QQmlData::deferData(int objectIndex, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *context) +{ + QQmlData::DeferredData *deferData = new QQmlData::DeferredData; + deferData->deferredIdx = objectIndex; + deferData->compilationUnit = compilationUnit; + deferData->compilationUnit->addref(); + deferData->context = context; + + const QV4::CompiledData::Object *compiledObject = compilationUnit->objectAt(objectIndex); + const QV4::CompiledData::BindingPropertyData &propertyData = compilationUnit->bindingPropertyDataPerObject.at(objectIndex); + + const QV4::CompiledData::Binding *binding = compiledObject->bindingTable(); + for (quint32 i = 0; i < compiledObject->nBindings; ++i, ++binding) { + const QQmlPropertyData *property = propertyData.at(i); + if (property && binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) + deferData->bindings.insert(property->coreIndex(), binding); + } + + deferredData.append(deferData); +} + void QQmlData::releaseDeferredData() { - for (DeferredData *deferData : qAsConst(deferredData)) { - deferData->compilationUnit->release(); - delete deferData; + auto it = deferredData.begin(); + while (it != deferredData.end()) { + DeferredData *deferData = *it; + if (deferData->bindings.isEmpty()) { + deferData->compilationUnit->release(); + delete deferData; + it = deferredData.erase(it); + } else { + ++it; + } } - deferredData.clear(); } void QQmlData::addNotify(int index, QQmlNotifierEndpoint *endpoint) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index b2f1421bcb..3663b06d55 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -233,6 +233,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI return instance; } +// ### unify or keep in sync with populateDeferredBinding() bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData) { QQmlData *declarativeData = QQmlData::get(instance); @@ -283,6 +284,80 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData:: qSwap(_qmlContext, qmlContext); qSwap(_scopeObject, scopeObject); + deferredData->bindings.clear(); + phase = ObjectsCreated; + + return errors.isEmpty(); +} + +// ### unify or keep in sync with populateDeferredProperties() +bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty, QQmlData::DeferredData *deferredData, const QV4::CompiledData::Binding *binding) +{ + Q_ASSERT(binding->flags & QV4::CompiledData::Binding::IsDeferredBinding); + + QObject *instance = qmlProperty.object(); + QQmlData *declarativeData = QQmlData::get(instance); + context = deferredData->context; + sharedState->rootContext = context; + + QObject *bindingTarget = instance; + + QQmlRefPointer cache = declarativeData->propertyCache; + QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(instance); + + QObject *scopeObject = instance; + qSwap(_scopeObject, scopeObject); + + QV4::Scope valueScope(v4); + + Q_ASSERT(topLevelCreator); + if (!sharedState->allJavaScriptObjects) + sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount); + + QV4::QmlContext *qmlContext = static_cast(valueScope.alloc(1)); + + qSwap(_qmlContext, qmlContext); + + qSwap(_propertyCache, cache); + qSwap(_qobject, instance); + + int objectIndex = deferredData->deferredIdx; + qSwap(_compiledObjectIndex, objectIndex); + + const QV4::CompiledData::Object *obj = qmlUnit->objectAt(_compiledObjectIndex); + qSwap(_compiledObject, obj); + + qSwap(_ddata, declarativeData); + qSwap(_bindingTarget, bindingTarget); + qSwap(_vmeMetaObject, vmeMetaObject); + + QQmlListProperty savedList; + qSwap(_currentList, savedList); + + const QQmlPropertyData &property = QQmlPropertyPrivate::get(qmlProperty)->core; + + if (property.isQList()) { + void *argv[1] = { (void*)&_currentList }; + QMetaObject::metacall(_qobject, QMetaObject::ReadProperty, property.coreIndex(), argv); + } else if (_currentList.object) { + _currentList = QQmlListProperty(); + } + + setPropertyBinding(&property, binding); + + qSwap(_currentList, savedList); + + qSwap(_vmeMetaObject, vmeMetaObject); + qSwap(_bindingTarget, bindingTarget); + qSwap(_ddata, declarativeData); + qSwap(_compiledObject, obj); + qSwap(_compiledObjectIndex, objectIndex); + qSwap(_qobject, instance); + qSwap(_propertyCache, cache); + + qSwap(_qmlContext, qmlContext); + qSwap(_scopeObject, scopeObject); + phase = ObjectsCreated; return errors.isEmpty(); @@ -1341,14 +1416,8 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject * qSwap(_propertyCache, cache); qSwap(_vmeMetaObject, vmeMetaObject); - if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) { - QQmlData::DeferredData *deferData = new QQmlData::DeferredData; - deferData->deferredIdx = _compiledObjectIndex; - deferData->compilationUnit = compilationUnit; - deferData->compilationUnit->addref(); - deferData->context = context; - _ddata->deferredData.append(deferData); - } + if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) + _ddata->deferData(_compiledObjectIndex, compilationUnit, context); if (_compiledObject->nFunctions > 0) setupFunctions(); diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h index 0c2d427c58..e2371cb4f1 100644 --- a/src/qml/qml/qqmlobjectcreator_p.h +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -81,7 +81,7 @@ struct QQmlObjectCreatorSharedState : public QSharedData QRecursionNode recursionNode; }; -class QQmlObjectCreator +class Q_QML_PRIVATE_EXPORT QQmlObjectCreator { Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator) public: @@ -90,6 +90,7 @@ public: QObject *create(int subComponentIndex = -1, QObject *parent = 0, QQmlInstantiationInterrupt *interrupt = 0); bool populateDeferredProperties(QObject *instance, QQmlData::DeferredData *deferredData); + bool populateDeferredBinding(const QQmlProperty &qmlProperty, QQmlData::DeferredData *deferredData, const QV4::CompiledData::Binding *binding); QQmlContextData *finalize(QQmlInstantiationInterrupt &interrupt); void cancel(QObject *object); void clear(); -- cgit v1.2.3