diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-09-12 01:00:34 +0200 |
---|---|---|
committer | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-09-12 01:00:34 +0200 |
commit | 2cf7a378ede8a6ab3172646e88b54efefa5c5931 (patch) | |
tree | 975afbc814981abebb0a01fa711e620b1c294abd /src | |
parent | 08113c55190a76d8a3586eb2e383ae9c2bd516c7 (diff) | |
parent | 1909f99aac4a14d70ffcc004fc9e16c154485c25 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: Ia585e9fec3f68a3f9a7d27a65b825e499d468125
Diffstat (limited to 'src')
28 files changed, 271 insertions, 116 deletions
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 33fe912692..c52cf417a6 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -150,7 +150,7 @@ public: qmlRegisterType<QuickTestEvent>(uri,1,0,"TestEvent"); qmlRegisterType<QuickTestEvent>(uri,1,2,"TestEvent"); qmlRegisterType<QuickTestUtil>(uri,1,0,"TestUtil"); - qmlRegisterAnonymousType<QQuickTouchEventSequence>(uri); + qmlRegisterAnonymousType<QQuickTouchEventSequence>(uri, 1); // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward qmlRegisterModule(uri, 1, QT_VERSION_MINOR); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 7578de4d14..31c90b31f6 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -151,6 +151,19 @@ struct Q_QML_PRIVATE_EXPORT Lookup { quintptr reserved3; ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); } qmlContextGlobalLookup; + struct { + Heap::Object *qmlTypeWrapper; + quintptr unused2; + } qmlTypeLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + ReturnedValue encodedEnumValue; + } qmlEnumValueLookup; + struct { + Heap::InternalClass *ic; + Heap::Object *qmlScopedEnumWrapper; + } qmlScopedEnumWrapperLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index c832bff051..e2d3b98ff6 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -248,11 +248,15 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r return lookup->qmlContextPropertyGetter(lookup, v4, base); } } - return QQmlTypeWrapper::create(v4, scopeObject, r.type); + result = QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { - return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); + result = QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); } - Q_ASSERT(!"Unreachable"); + if (lookup) { + lookup->qmlTypeLookup.qmlTypeWrapper = static_cast<Heap::Object*>(result->heapObject()); + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupType; + } + return result->asReturnedValue(); } // Fall through @@ -659,6 +663,27 @@ ReturnedValue QQmlContextWrapper::lookupInParentContextHierarchy(Lookup *l, Exec return Encode::undefined(); } +ReturnedValue QQmlContextWrapper::lookupType(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + Scoped<QmlContext> qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QObject *scopeObject = qmlContext->qmlScope(); + if (scopeObject && QQmlData::wasDeleted(scopeObject)) + return QV4::Encode::undefined(); + + Heap::Object *heapObject = l->qmlTypeLookup.qmlTypeWrapper; + if (static_cast<Heap::QQmlTypeWrapper *>(heapObject)->object != scopeObject) { + l->qmlTypeLookup.qmlTypeWrapper = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + } + + return Value::fromHeapObject(heapObject).asReturnedValue(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 4c8287ef2f..e3e7239fe5 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -112,6 +112,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInParentContextHierarchy(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupType(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 8a7cbdfb2a..aaa198c62a 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -2050,7 +2050,7 @@ ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp) double b = base.toNumber(); double e = exp.toNumber(); if (qt_is_inf(e) && (b == 1 || b == -1)) - return Encode(qt_snan()); + return Encode(qt_qnan()); return Encode(pow(b,e)); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b4c34d60fa..4d099d2e0f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -1247,7 +1247,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, double base = left.toNumber(); double exp = ACC.toNumber(); if (qIsInf(exp) && (base == 1 || base == -1)) - acc = Encode(qSNaN()); + acc = Encode(qQNaN()); else acc = Encode(pow(base,exp)); MOTH_END_INSTR(Exp) diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index a93b012c70..6295345fa4 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -102,7 +102,7 @@ class QQmlPropertyValueInterceptor; void Q_QML_EXPORT qmlClearTypeRegistrations(); template<typename T> -int qmlRegisterAnonymousType(const char *uri, int versionMajor=1) +int qmlRegisterAnonymousType(const char *uri, int versionMajor) { QML_GETTYPENAMES @@ -136,7 +136,7 @@ int qmlRegisterAnonymousType(const char *uri, int versionMajor=1) template<typename T> QT_DEPRECATED_VERSION_X_5_14("Use qmlRegisterAnonymousType instead") int qmlRegisterType() { - return qmlRegisterAnonymousType<T>(""); + return qmlRegisterAnonymousType<T>("", 1); } int Q_QML_EXPORT qmlRegisterTypeNotAvailable(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& message); diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 136159993a..96891af416 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -98,11 +98,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ @@ -137,11 +137,11 @@ QT_BEGIN_NAMESPACE static int signalIdx = -1; \ static int methodIdx = -1; \ if (signalIdx < 0) { \ - Q_ASSERT(((int)(*signal) - '0') == QSIGNAL_CODE); \ + Q_ASSERT((int(*signal) - '0') == QSIGNAL_CODE); \ signalIdx = SenderType::staticMetaObject.indexOfSignal(signal+1); \ } \ if (methodIdx < 0) { \ - int code = ((int)(*method) - '0'); \ + int code = (int(*method) - '0'); \ Q_ASSERT(code == QSLOT_CODE || code == QSIGNAL_CODE); \ if (code == QSLOT_CODE) \ methodIdx = ReceiverType::staticMetaObject.indexOfSlot(method+1); \ diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 2c641d3845..c2674b402a 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -739,9 +739,16 @@ QQmlType QQmlMetaType::typeForUrl(const QString &urlString, const QUrl url = QQmlTypeLoader::normalize(QUrl(urlString)); QQmlMetaTypeDataPtr data; - QQmlType ret(data->urlToType.value(url)); - if (ret.isValid() && ret.sourceUrl() == url) - return ret; + { + QQmlType ret(data->urlToType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } + { + QQmlType ret(data->urlToNonFileImportType.value(url)); + if (ret.isValid() && ret.sourceUrl() == url) + return ret; + } const int dot = qualifiedType.indexOf(QLatin1Char('.')); const QString typeName = dot < 0 diff --git a/src/qml/qml/qqmlpropertycachecreator_p.h b/src/qml/qml/qqmlpropertycachecreator_p.h index 9c7a69d571..def4480198 100644 --- a/src/qml/qml/qqmlpropertycachecreator_p.h +++ b/src/qml/qml/qqmlpropertycachecreator_p.h @@ -256,17 +256,8 @@ inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContaine Q_ASSERT(typeRef); QQmlType qmltype = typeRef->type; if (!qmltype.isValid()) { - QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex); - if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) { - if (qmltype.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } + imports->resolveType(stringAt(context.instantiatingBinding->propertyNameIndex), + &qmltype, nullptr, nullptr, nullptr); } const QMetaObject *attachedMo = qmltype.attachedPropertiesType(enginePrivate); diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 252ff26a64..3615749da1 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -154,10 +154,10 @@ bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, in return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; } -QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const +QQmlType QQmlTypePrivate::resolveCompositeBaseType(QQmlEnginePrivate *engine) const { Q_ASSERT(isComposite()); - if (!engine || !d) + if (!engine) return QQmlType(); QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl())); if (td.isNull() || !td->isComplete()) @@ -167,7 +167,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const return QQmlMetaType::qmlType(mo); } -QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) const +QQmlPropertyCache *QQmlTypePrivate::compositePropertyCache(QQmlEnginePrivate *engine) const { // similar logic to resolveCompositeBaseType Q_ASSERT(isComposite()); @@ -262,24 +262,30 @@ void QQmlTypePrivate::init() const lock.unlock(); } -void QQmlTypePrivate::initEnums(const QQmlPropertyCache *cache) const +void QQmlTypePrivate::initEnums(QQmlEnginePrivate *engine) const { - if ((isEnumFromBaseSetup || !baseMetaObject) - && (isEnumFromCacheSetup || !cache)) { + const QQmlPropertyCache *cache = (!isEnumFromCacheSetup && isComposite()) + ? compositePropertyCache(engine) + : nullptr; + + const QMetaObject *metaObject = !isEnumFromCacheSetup + ? baseMetaObject // beware: It could be a singleton type without metaobject + : nullptr; + + if (!cache && !metaObject) return; - } init(); QMutexLocker lock(QQmlMetaType::typeRegistrationLock()); - if (!isEnumFromCacheSetup && cache) { + if (cache) { insertEnumsFromPropertyCache(cache); isEnumFromCacheSetup = true; } - if (!isEnumFromBaseSetup && baseMetaObject) { // could be singleton type without metaobject - insertEnums(baseMetaObject); + if (metaObject) { + insertEnums(metaObject); isEnumFromBaseSetup = true; } } @@ -548,7 +554,7 @@ bool QQmlType::isInterface() const bool QQmlType::isComposite() const { - return d && (d->regType == CompositeType || d->regType == CompositeSingletonType); + return d && d->isComposite(); } bool QQmlType::isCompositeSingleton() const @@ -617,7 +623,7 @@ QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction(QQmlEnginePrivat QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesFunction(engine); } @@ -630,7 +636,7 @@ const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) c QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesType(engine); } @@ -649,7 +655,7 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const QQmlType base; if (d->regType == CompositeType) - base = resolveCompositeBaseType(engine); + base = d->resolveCompositeBaseType(engine); return base.attachedPropertiesId(engine); } #endif @@ -689,24 +695,16 @@ int QQmlType::index() const QUrl QQmlType::sourceUrl() const { - if (d) { - if (d->regType == CompositeType) - return d->extraData.fd->url; - else if (d->regType == CompositeSingletonType) - return d->extraData.sd->singletonInstanceInfo->url; - } - return QUrl(); + return d ? d->sourceUrl() : QUrl(); } int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -721,11 +719,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QHashedCStringRef &name { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; - *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -740,10 +736,9 @@ int QQmlType::enumValue(QQmlEnginePrivate *engine, const QV4::String *name, bool { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->enums.value(name); if (rv) @@ -758,10 +753,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QV4::String *name { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -776,10 +770,9 @@ int QQmlType::scopedEnumIndex(QQmlEnginePrivate *engine, const QString &name, bo { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(name); if (rv) @@ -828,10 +821,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QByteArray &scope { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(QHashedCStringRef(scopedEnumName.constData(), scopedEnumName.length())); if (rv) { @@ -851,10 +843,9 @@ int QQmlType::scopedEnumValue(QQmlEnginePrivate *engine, const QStringRef &scope { Q_ASSERT(ok); if (d) { - const QQmlPropertyCache *cache = isComposite() ? compositePropertyCache(engine) : nullptr; *ok = true; - d->initEnums(cache); + d->initEnums(engine); int *rv = d->scopedEnumIndex.value(QHashedStringRef(scopedEnumName)); if (rv) { diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h index 4dec20600b..ec27b38a73 100644 --- a/src/qml/qml/qqmltype_p.h +++ b/src/qml/qml/qqmltype_p.h @@ -182,11 +182,7 @@ public: }; private: - QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; - int resolveCompositeEnumValue(QQmlEnginePrivate *engine, const QString &name, bool *ok) const; - QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; friend uint qHash(const QQmlType &t, uint seed); - QQmlRefPointer<const QQmlTypePrivate> d; }; diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h index d381e11df4..6a2d961de8 100644 --- a/src/qml/qml/qqmltype_p_p.h +++ b/src/qml/qml/qqmltype_p_p.h @@ -66,10 +66,30 @@ public: QQmlTypePrivate(QQmlType::RegistrationType type); void init() const; - void initEnums(const QQmlPropertyCache *cache = nullptr) const; + void initEnums(QQmlEnginePrivate *engine) const; void insertEnums(const QMetaObject *metaObject) const; void insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const; + QUrl sourceUrl() const + { + switch (regType) { + case QQmlType::CompositeType: + return extraData.fd->url; + case QQmlType::CompositeSingletonType: + return extraData.sd->singletonInstanceInfo->url; + default: + return QUrl(); + } + } + + bool isComposite() const + { + return regType == QQmlType::CompositeType || regType == QQmlType::CompositeSingletonType; + } + + QQmlType resolveCompositeBaseType(QQmlEnginePrivate *engine) const; + QQmlPropertyCache *compositePropertyCache(QQmlEnginePrivate *engine) const; + QQmlType::RegistrationType regType; struct QQmlCppTypeData diff --git a/src/qml/qml/qqmltypecompiler.cpp b/src/qml/qml/qqmltypecompiler.cpp index e7633a1bba..9a6bd73326 100644 --- a/src/qml/qml/qqmltypecompiler.cpp +++ b/src/qml/qml/qqmltypecompiler.cpp @@ -328,18 +328,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio const QmlIR::Object *attachedObj = qmlObjects.at(binding->value.objectIndex); auto *typeRef = resolvedType(binding->propertyNameIndex); QQmlType type = typeRef ? typeRef->type : QQmlType(); - if (!type.isValid()) { - if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) { - if (type.isComposite()) { - QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl()); - Q_ASSERT(tdata); - Q_ASSERT(tdata->isComplete()); - - auto compilationUnit = tdata->compilationUnit(); - type = QQmlMetaType::qmlType(compilationUnit->metaTypeId); - } - } - } + if (!type.isValid()) + imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr); const QMetaObject *attachedType = type.attachedPropertiesType(enginePrivate); if (!attachedType) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3a18bbf7c9..42e7d2c4b4 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -964,8 +964,10 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) { - if (path.isEmpty()) + const QChar nullChar(QChar::Null); + if (path.isEmpty() || path.contains(nullChar) || file.isEmpty() || file.contains(nullChar)) return false; + Q_ASSERT(path.endsWith(QLatin1Char('/'))); if (path.at(0) == QLatin1Char(':')) { // qrc resource diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 931f37b35a..ef4a628a04 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -476,6 +476,34 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, } // Fall through to base implementation } + + if (name->startsWithUpper()) { + bool ok = false; + int value = type.enumValue(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + lookup->qmlEnumValueLookup.ic = This->internalClass(); + lookup->qmlEnumValueLookup.encodedEnumValue + = QV4::Value::fromInt32(value).asReturnedValue(); + lookup->getter = QQmlTypeWrapper::lookupEnumValue; + return lookup->getter(lookup, engine, *object); + } + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(engine->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper( + scope, engine->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + + lookup->qmlScopedEnumWrapperLookup.ic = This->internalClass(); + lookup->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper + = static_cast<Heap::Object*>(enumWrapper->heapObject()); + lookup->getter = QQmlTypeWrapper::lookupScopedEnum; + return enumWrapper.asReturnedValue(); + } + // Fall through to base implementation + } // Fall through to base implementation } return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); @@ -519,6 +547,34 @@ ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngin return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlTypeWrapper::lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlEnumValueLookup.ic) { + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return l->qmlEnumValueLookup.encodedEnumValue; +} + +ReturnedValue QQmlTypeWrapper::lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base) +{ + Scope scope(engine); + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, static_cast<Heap::QQmlScopedEnumWrapper *>( + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper)); + + auto *o = static_cast<Heap::Object *>(base.heapObject()); + if (!o || o->internalClass != l->qmlScopedEnumWrapperLookup.ic) { + QQmlType::derefHandle(enumWrapper->d()->typePrivate); + l->qmlScopedEnumWrapperLookup.qmlScopedEnumWrapper = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, base); + } + + return enumWrapper.asReturnedValue(); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 6b51f421b3..7dc3f55310 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -115,6 +115,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupEnumValue(Lookup *l, ExecutionEngine *engine, const Value &base); + static ReturnedValue lookupScopedEnum(Lookup *l, ExecutionEngine *engine, const Value &base); protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 2e213e7dc3..355150b786 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1941,30 +1941,32 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value THROW_GENERIC_ERROR("qsTr(): third argument (n) must be a number"); QString context; - if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { - QString path = ctxt->urlString(); - int lastSlash = path.lastIndexOf(QLatin1Char('/')); - int lastDot = path.lastIndexOf(QLatin1Char('.')); - int length = lastDot - (lastSlash + 1); - context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); - } else { - CppStackFrame *frame = scope.engine->currentStackFrame; - // The first non-empty source URL in the call stack determines the translation context. - while (frame && context.isEmpty()) { - if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) { - const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit); - QString fileName = unit->fileName(); - QUrl url(unit->fileName()); - if (url.isValid() && url.isRelative()) { - context = url.fileName(); - } else { - context = QQmlFile::urlToLocalFileOrQrc(fileName); - if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) - context = fileName; - } - context = QFileInfo(context).baseName(); + CppStackFrame *frame = scope.engine->currentStackFrame; + // The first non-empty source URL in the call stack determines the translation context. + while (frame && context.isEmpty()) { + if (CompiledData::CompilationUnitBase *baseUnit = frame->v4Function->compilationUnit) { + const auto *unit = static_cast<const CompiledData::CompilationUnit *>(baseUnit); + QString fileName = unit->fileName(); + QUrl url(unit->fileName()); + if (url.isValid() && url.isRelative()) { + context = url.fileName(); + } else { + context = QQmlFile::urlToLocalFileOrQrc(fileName); + if (context.isEmpty() && fileName.startsWith(QLatin1String(":/"))) + context = fileName; } - frame = frame->parent; + context = QFileInfo(context).baseName(); + } + frame = frame->parent; + } + + if (context.isEmpty()) { + if (QQmlContextData *ctxt = scope.engine->callingQmlContext()) { + QString path = ctxt->urlString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + int lastDot = path.lastIndexOf(QLatin1Char('.')); + int length = lastDot - (lastSlash + 1); + context = (lastSlash > -1) ? path.mid(lastSlash + 1, (length > -1) ? length : -1) : QString(); } } diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index b96d09996d..864d90274c 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -341,9 +341,15 @@ OpenGL application is allowed to draw. The \l {Scene Graph - OpenGL Under QML} example gives an example on how to use these signals using OpenGL. +The \l {Scene Graph - Direct3D 11 Under QML} example gives an example on +how to use these signals using Direct3D. + The \l {Scene Graph - Metal Under QML} example gives an example on how to use these signals using Metal. +The \l {Scene Graph - Vulkan Under QML} example gives an example on +how to use these signals using Vulkan. + The other alternative, only available for OpenGL currently, is to create a QQuickFramebufferObject, render into it, and let it be displayed in the scene graph as a texture. The \l {Scene Graph - Rendering FBOs} example shows how @@ -352,6 +358,13 @@ and multiple threads to create content to be displayed in the scene graph. The \l {Scene Graph - Rendering FBOs in a thread} examples show how this can be done. +Graphics APIs other than OpenGL can also follow this approach, even though +QQuickFramebufferObject does not currently support them. Creating and rendering +to a texture directly with the underlying API, followed by wrapping and using +this resource in a Qt Quick scene in a custom QQuickItem, is demonstrated in +the \l {Scene Graph - Metal Texture Import} example. That example uses Metal, +the concepts however apply to all other graphics APIs as well. + \warning When mixing OpenGL content with scene graph rendering, it is important the application does not leave the OpenGL context in a state with buffers bound, attributes enabled, special values in the z-buffer diff --git a/src/quick/doc/src/examples.qdoc b/src/quick/doc/src/examples.qdoc index 9034c90eb8..8c31e13a2d 100644 --- a/src/quick/doc/src/examples.qdoc +++ b/src/quick/doc/src/examples.qdoc @@ -171,16 +171,17 @@ Creator. \div {class="doc-column"} \b{Scene Graph} \list - \li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML} + \li \l{Scene Graph - Custom Geometry}{Custom Geometry} \li \l{Scene Graph - Metal Under QML}{Metal Under QML} + \li \l{Scene Graph - Metal Texture Import}{Metal Texture Import} + \li \l{Scene Graph - OpenGL Under QML}{OpenGL Under QML} \li \l{Scene Graph - Direct3D 11 Under QML}{Direct3D 11 Under QML} + \li \l{Scene Graph - Custom Rendering with QSGRenderNode}{Render Node} \li \l{Scene Graph - Painted Item}{Painted Item} - \li \l{Scene Graph - Custom Geometry}{Custom Geometry} \li \l{Scene Graph - Graph}{Graph} \li \l{Scene Graph - Simple Material}{Simple Material} \li \l{Scene Graph - Rendering FBOs}{Rendering FBOs} \li \l{Scene Graph - Rendering FBOs in a thread}{Rendering FBOs in a thread} - \li \l{Scene Graph - Custom Rendering with QSGRenderNode}{Render Node} \endlist \enddiv \enddiv diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 27e2a4572e..6fe2891242 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4193,7 +4193,11 @@ QQmlIncubationController *QQuickWindow::incubationController() const command buffer, before via QSGRendererInterface. Note however that the render pass (or passes) are already recorded at this point and it is not possible to add more commands within the scenegraph's pass. Instead, use - afterRenderPassRecording() for that. + afterRenderPassRecording() for that. This signal has therefore limited use + and is rarely needed in an RHI-based setup. Rather, it is the combination + of beforeRendering() + beforeRenderPassRecording() or beforeRendering() + + afterRenderPassRecording() that is typically used to achieve under- or + overlaying of the custom rendering. \warning This signal is emitted from the scene graph rendering thread. If your slot function needs to finish before execution continues, you must make sure that @@ -4229,7 +4233,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const \note Resource updates (uploads, copies) typically cannot be enqueued from within a render pass. Therefore, more complex user rendering will need to - connect to both the beforeRendering() and this signals. + connect to both beforeRendering() and this signal. \warning This signal is emitted from the scene graph rendering thread. If your slot function needs to finish before execution continues, you must make sure that @@ -4260,7 +4264,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const \note Resource updates (uploads, copies) typically cannot be enqueued from within a render pass. Therefore, more complex user rendering will need to - connect to both the beforeRendering() and this signals. + connect to both beforeRendering() and this signal. \warning This signal is emitted from the scene graph rendering thread. If your slot function needs to finish before execution continues, you must make sure that @@ -4773,6 +4777,17 @@ const QQuickWindow::GraphicsStateInfo *QQuickWindow::graphicsStateInfo() because the scene graph performs the necessary steps implicitly for render nodes. + Native graphics objects (such as, graphics device, command buffer or + encoder) are accessible via QSGRendererInterface::getResource(). + + \warning Watch out for the fact that + QSGRendererInterface::CommandListResource may return a different object + between beginExternalCommands() - endExternalCommands(). This can happen + when the underlying implementation provides a dedicated secondary command + buffer for recording external graphics commands within a render pass. + Therefore, always query CommandListResource after calling this function. Do + not attempt to reuse an object from an earlier query. + \note This function has no effect when the scene graph is using OpenGL directly and the RHI graphics abstraction layer is not in use. Refer to resetOpenGLState() in that case. diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index e504fe1c62..bc68199e08 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -134,6 +134,10 @@ QT_BEGIN_NAMESPACE \value VulkanInstanceResource The resource is a pointer to the QVulkanInstance used by the scenegraph, when applicable. + + \value RenderPassResource The resource is a pointer to the render pass used + by the scenegraph, describing the color and depth/stecil attachments and + how they are used. For example, a \c{VkRenderPass *}. */ /*! diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.h b/src/quick/scenegraph/coreapi/qsgrendererinterface.h index 3052c81f6c..7aa7d0e769 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.h +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.h @@ -72,7 +72,8 @@ public: OpenGLContextResource, DeviceContextResource, CommandEncoderResource, - VulkanInstanceResource + VulkanInstanceResource, + RenderPassResource }; enum ShaderType { diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index b6f35b8580..4ccaa91cce 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -68,6 +68,7 @@ QSGDefaultRenderContext::QSGDefaultRenderContext(QSGContext *context) , m_glAtlasManager(nullptr) , m_rhiAtlasManager(nullptr) , m_currentFrameCommandBuffer(nullptr) + , m_currentFrameRenderPass(nullptr) { } @@ -239,6 +240,7 @@ void QSGDefaultRenderContext::beginNextRhiFrame(QSGRenderer *renderer, QRhiRende renderer->setRenderPassRecordingCallbacks(mainPassRecordingStart, mainPassRecordingEnd, callbackUserData); m_currentFrameCommandBuffer = cb; + m_currentFrameRenderPass = rp; } void QSGDefaultRenderContext::renderNextRhiFrame(QSGRenderer *renderer) @@ -250,6 +252,7 @@ void QSGDefaultRenderContext::endNextRhiFrame(QSGRenderer *renderer) { Q_UNUSED(renderer); m_currentFrameCommandBuffer = nullptr; + m_currentFrameRenderPass = nullptr; } /*! diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index 79bc9dd76d..6a3462ae2b 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -62,6 +62,7 @@ QT_BEGIN_NAMESPACE class QRhi; class QRhiCommandBuffer; +class QRhiRenderPassDescriptor; class QOpenGLContext; class QSGMaterialShader; class QSGMaterialRhiShader; @@ -145,6 +146,10 @@ public: // may be null if not in an active frame, but returning null is valid then return m_currentFrameCommandBuffer; } + QRhiRenderPassDescriptor *currentFrameRenderPass() const { + // may be null if not in an active frame, but returning null is valid then + return m_currentFrameRenderPass; + } protected: static QString fontKey(const QRawFont &font); @@ -160,6 +165,7 @@ protected: QSGOpenGLAtlasTexture::Manager *m_glAtlasManager; QSGRhiAtlasTexture::Manager *m_rhiAtlasManager; QRhiCommandBuffer *m_currentFrameCommandBuffer; + QRhiRenderPassDescriptor *m_currentFrameRenderPass; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 08d1c726ab..f15105168e 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -648,7 +648,9 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) } Q_ASSERT(rhi == cd->rhi); - QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain); + // ### the flag should only be set when the app requests it, but there's no way to do that right now + QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass; + QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags); if (frameResult != QRhi::FrameOpSuccess) { if (frameResult == QRhi::FrameOpDeviceLost) qWarning("Device lost"); diff --git a/src/quick/scenegraph/qsgrhisupport.cpp b/src/quick/scenegraph/qsgrhisupport.cpp index a92b6b0c84..e050938cc9 100644 --- a/src/quick/scenegraph/qsgrhisupport.cpp +++ b/src/quick/scenegraph/qsgrhisupport.cpp @@ -279,12 +279,16 @@ QSurface::SurfaceType QSGRhiSupport::windowSurfaceType() const } #if QT_CONFIG(vulkan) -static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, const QRhiNativeHandles *nat, - const QRhiNativeHandles *cbNat) +static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, + const QRhiNativeHandles *nat, + const QRhiNativeHandles *cbNat, + const QRhiNativeHandles *rpNat) { const QRhiVulkanNativeHandles *vknat = static_cast<const QRhiVulkanNativeHandles *>(nat); const QRhiVulkanCommandBufferNativeHandles *maybeVkCbNat = static_cast<const QRhiVulkanCommandBufferNativeHandles *>(cbNat); + const QRhiVulkanRenderPassNativeHandles *maybeVkRpNat = + static_cast<const QRhiVulkanRenderPassNativeHandles *>(rpNat); switch (res) { case QSGRendererInterface::DeviceResource: @@ -298,6 +302,11 @@ static const void *qsgrhi_vk_rifResource(QSGRendererInterface::Resource res, con return nullptr; case QSGRendererInterface::PhysicalDeviceResource: return &vknat->physDev; + case QSGRendererInterface::RenderPassResource: + if (maybeVkRpNat) + return &maybeVkRpNat->renderPass; + else + return nullptr; default: return nullptr; } @@ -376,7 +385,10 @@ const void *QSGRhiSupport::rifResource(QSGRendererInterface::Resource res, const case QRhi::Vulkan: { QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); - return qsgrhi_vk_rifResource(res, nat, cb ? cb->nativeHandles() : nullptr); + QRhiRenderPassDescriptor *rp = rc->currentFrameRenderPass(); + return qsgrhi_vk_rifResource(res, nat, + cb ? cb->nativeHandles() : nullptr, + rp ? rp->nativeHandles() : nullptr); } #endif #if QT_CONFIG(opengl) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 9e34a2b201..9bcb96a65a 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -713,7 +713,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) } Q_ASSERT(rhi == cd->rhi); - QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain); + // ### the flag should only be set when the app requests it, but there's no way to do that right now + QRhi::BeginFrameFlags frameFlags = QRhi::ExternalContentsInPass; + QRhi::FrameOpResult frameResult = rhi->beginFrame(cd->swapchain, frameFlags); if (frameResult != QRhi::FrameOpSuccess) { if (frameResult == QRhi::FrameOpDeviceLost) qWarning("Device lost"); |