diff options
49 files changed, 661 insertions, 390 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index b7e3e20fd0..04e6efb1e9 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1073,6 +1073,7 @@ bool Codegen::visit(Expression *ast) TailCallBlocker blockTailCalls(this); statement(ast->left); blockTailCalls.unblock(); + clearExprResultName(); // The name only holds for the left part accept(ast->right); return false; } @@ -2537,7 +2538,7 @@ bool Codegen::visit(ObjectPattern *ast) { RegisterScope innerScope(this); - Reference value = expression(p->initializer); + Reference value = expression(p->initializer, name); if (hasError) return false; value.loadInAccumulator(); @@ -2979,7 +2980,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, // already defined return leaveContext(); - _context->name = name; + _context->name = name.isEmpty() ? currentExpr().result().name : name; _module->functions.append(_context); _context->functionIndex = _module->functions.count() - 1; diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 6d5f8c0951..65dc4b97b5 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -199,8 +199,9 @@ public: codegen = cg; } - Reference() : + Reference(const QString &name = QString()) : constant(0), + name(name), isArgOrEval(false), isReadonly(false), isReferenceToConst(false), @@ -418,6 +419,11 @@ protected: bool _trueBlockFollowsCondition = false; public: + explicit Result(const QString &name) + : _result(name) + , _requested(ex) + {} + explicit Result(const Reference &lrvalue) : _result(lrvalue) , _requested(ex) @@ -476,6 +482,10 @@ protected: void setResult(Reference &&result) { _result = std::move(result); } + + void clearResultName() { + _result.name.clear(); + } }; void enterContext(AST::Node *node); @@ -523,12 +533,12 @@ protected: const BytecodeGenerator::Label *iffalse, bool trueBlockFollowsCondition); - inline Reference expression(AST::ExpressionNode *ast) + inline Reference expression(AST::ExpressionNode *ast, const QString &name = QString()) { if (!ast || hasError) return Reference(); - pushExpr(); + pushExpr(name); ast->accept(this); return popResult(); } @@ -716,6 +726,7 @@ protected: inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); } inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); } inline Reference exprResult() const { return m_expressions.back().result(); } + inline void clearExprResultName() { m_expressions.back().clearResultName(); } inline bool exprAccept(Format f) { return m_expressions.back().accept(f); } @@ -723,7 +734,7 @@ protected: inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); } inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); } - inline void pushExpr() { m_expressions.emplace_back(); } + inline void pushExpr(const QString &name = QString()) { m_expressions.emplace_back(name); } inline Result popExpr() { diff --git a/src/qml/doc/snippets/qml/componentCreation.js b/src/qml/doc/snippets/qml/componentCreation.js index 7364139d3d..ea45f18c37 100644 --- a/src/qml/doc/snippets/qml/componentCreation.js +++ b/src/qml/doc/snippets/qml/componentCreation.js @@ -17,7 +17,7 @@ function createSpriteObjects() { //![local] component = Qt.createComponent("Sprite.qml"); - sprite = component.createObject(appWindow, {"x": 100, "y": 100}); + sprite = component.createObject(appWindow, {x: 100, y: 100}); if (sprite == null) { // Error Handling @@ -32,7 +32,7 @@ function createSpriteObjects() { //![finishCreation] function finishCreation() { if (component.status == Component.Ready) { - sprite = component.createObject(appWindow, {"x": 100, "y": 100}); + sprite = component.createObject(appWindow, {x: 100, y: 100}); if (sprite == null) { // Error Handling console.log("Error creating object"); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 99f425293e..0cda6b864a 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -77,8 +77,13 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu primitiveLookup.type = object.type(); switch (primitiveLookup.type) { case Value::Undefined_Type: - case Value::Null_Type: - return engine->throwTypeError(); + case Value::Null_Type: { + Scope scope(engine); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + const QString message = QStringLiteral("Cannot read property '%1' of %2").arg(name->toQString()) + .arg(QLatin1String(primitiveLookup.type == Value::Undefined_Type ? "undefined" : "null")); + return engine->throwTypeError(message); + } case Value::Boolean_Type: primitiveLookup.proto = engine->booleanPrototype()->d(); break; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 94bf1a98ae..7578de4d14 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -120,7 +120,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup { } indexedLookup; struct { Heap::InternalClass *ic; - Heap::QObjectWrapper *staticQObject; + Heap::InternalClass *qmlTypeIc; // only used when lookup goes through QQmlTypeWrapper QQmlPropertyCache *propertyCache; QQmlPropertyData *propertyData; } qobjectLookup; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index f3351f6da0..7b885a9e9e 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -292,7 +292,6 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject))); const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); lookup->qobjectLookup.ic = That->internalClass(); - lookup->qobjectLookup.staticQObject = nullptr; lookup->qobjectLookup.propertyCache = ddata->propertyCache; lookup->qobjectLookup.propertyCache->addref(); lookup->qobjectLookup.propertyData = propertyData; @@ -325,7 +324,6 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); const QObjectWrapper *That = static_cast<const QObjectWrapper *>(val->objectValue()); lookup->qobjectLookup.ic = That->internalClass(); - lookup->qobjectLookup.staticQObject = nullptr; lookup->qobjectLookup.propertyCache = ddata->propertyCache; lookup->qobjectLookup.propertyCache->addref(); lookup->qobjectLookup.propertyData = propertyData; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8b7de89d5b..8a4adfe69a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -872,7 +872,6 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E } lookup->qobjectLookup.ic = This->internalClass(); - lookup->qobjectLookup.staticQObject = nullptr; lookup->qobjectLookup.propertyCache = ddata->propertyCache; lookup->qobjectLookup.propertyCache->addref(); lookup->qobjectLookup.propertyData = property; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 795bf241f2..ac9cad2bdb 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -231,8 +231,7 @@ inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionE if (!o || o->internalClass != lookup->qobjectLookup.ic) return revertLookup(); - const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject : - static_cast<const Heap::QObjectWrapper *>(o); + const Heap::QObjectWrapper *This = static_cast<const Heap::QObjectWrapper *>(o); QObject *qobj = This->object(); if (QQmlData::wasDeleted(qobj)) return QV4::Encode::undefined(); diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index a9c8ac66bd..4e901721cb 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -87,12 +87,29 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const { HeapBasePtr b; +#ifdef __ia64 +// Restore bits 49-47 to bits 63-61, undoing the workaround explained in +// setM below. + quint64 _tmp; + + _tmp = _val & (7L << 47); // 0x3800000000000 + _tmp = (_tmp << 14) | (_val ^ _tmp); + memcpy(&b, &_tmp, 8); +#else memcpy(&b, &_val, 8); +#endif return b; } QML_NEARLY_ALWAYS_INLINE void setM(HeapBasePtr b) { memcpy(&_val, &b, 8); +#ifdef __ia64 +// On ia64, bits 63-61 in a 64-bit pointer are used to store the virtual region +// number. Since this implementation is not 64-bit clean, we move bits 63-61 +// to bits 49-47 and hope for the best. This is undone in *m(), above. + _val |= ((_val & (7L << 61)) >> 14); + _val &= ((1L << 50)-1); +#endif } #elif QT_POINTER_SIZE == 4 QML_NEARLY_ALWAYS_INLINE HeapBasePtr m() const diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 0c947b541b..4ad9057ced 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -1110,6 +1110,23 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T } break; ./ +UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON; +UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->typeModifier = stringRef(3); + node->propertyToken = loc(2); + node->typeModifierToken = loc(3); + node->typeToken = loc(5); + node->identifierToken = loc(7); + node->semicolonToken = loc(8); + sym(1).Node = node; + } break; +./ + UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON; UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON; /. @@ -1221,6 +1238,34 @@ UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T } break; ./ +UiObjectMember: T_READONLY T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET; +/. + case $rule_number: { + AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7)); + node->isReadonlyMember = true; + node->readonlyToken = loc(1); + node->typeModifier = stringRef(3); + node->propertyToken = loc(2); + node->typeModifierToken = loc(3); + node->typeToken = loc(5); + node->identifierToken = loc(7); + node->semicolonToken = loc(8); // insert a fake ';' before ':' + + AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(7)); + propertyName->identifierToken = loc(7); + propertyName->next = 0; + + AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(10).UiArrayMemberList->finish()); + binding->colonToken = loc(8); + binding->lbracketToken = loc(9); + binding->rbracketToken = loc(11); + + node->binding = binding; + + sym(1).Node = node; + } break; +./ + UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer; /. case $rule_number: { @@ -1773,10 +1818,6 @@ PropertyDefinition: PropertyName T_COLON AssignmentExpression_In; /. case $rule_number: { AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Expression); - if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) { - if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName)) - f->name = driver->newStringRef(sym(1).PropertyName->asString()); - } if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) { if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName)) c->name = driver->newStringRef(sym(1).PropertyName->asString()); diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 7b3f89e943..f84a1b2109 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -608,9 +608,12 @@ Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versi template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) { - QObject *mutableObj = const_cast<QObject *>(obj); - return qmlAttachedPropertiesObject( - mutableObj, qmlAttachedPropertiesFunction(mutableObj, &T::staticMetaObject), create); + // We don't need a concrete object to resolve the function. As T is a C++ type, it and all its + // super types should be registered as CppType (or not at all). We only need the object and its + // QML engine to resolve composite types. Therefore, the function is actually a static property + // of the C++ type system and we can cache it here for improved performance on further lookups. + static const auto func = qmlAttachedPropertiesFunction(nullptr, &T::staticMetaObject); + return qmlAttachedPropertiesObject(const_cast<QObject *>(obj), func, create); } inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 1b7a433a84..facd79d211 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -76,7 +76,7 @@ void QQmlApplicationEnginePrivate::init() &QCoreApplication::exit, Qt::QueuedConnection); #if QT_CONFIG(translation) QTranslator* qtTranslator = new QTranslator; - if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath), QLatin1String(".qm"))) QCoreApplication::installTranslator(qtTranslator); translators << qtTranslator; #endif @@ -90,10 +90,10 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc")) return; - QFileInfo fi(rootFile.toLocalFile()); + QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(rootFile)); QTranslator *translator = new QTranslator; - if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"))) { + if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"), QLatin1String(".qm"))) { QCoreApplication::installTranslator(translator); translators << translator; } else { @@ -180,6 +180,9 @@ void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) \list \li Connecting Qt.quit() to QCoreApplication::quit() \li Automatically loads translation files from an i18n directory adjacent to the main QML file. + \list + \li Translation files must have "qml_" prefix e.g. qml_ja_JP.qm. + \endlist \li Automatically sets an incubation controller if the scene contains a QQuickWindow. \li Automatically sets a \c QQmlFileSelector as the url interceptor, applying file selectors to all QML files and assets. diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 04debc0615..89b632f87b 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1206,7 +1206,7 @@ static void QQmlComponent_setQmlParent(QObject *me, QObject *parent) \js var component = Qt.createComponent("Button.qml"); if (component.status == Component.Ready) - component.createObject(parent, {"x": 100, "y": 100}); + component.createObject(parent, {x: 100, y: 100}); \endjs Dynamically created instances can be deleted with the \c destroy() method. diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index a5b92d14f7..c6858780a1 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1193,7 +1193,8 @@ QQmlPropertyCache *QQmlMetaType::propertyCache(const QQmlType &type, int minorVe void QQmlMetaType::unregisterType(int typeIndex) { QQmlMetaTypeDataPtr data; - if (const QQmlTypePrivate *d = data->types.value(typeIndex).priv()) { + const QQmlType type = data->types.value(typeIndex); + if (const QQmlTypePrivate *d = type.priv()) { removeQQmlTypePrivate(data->idToType, d); removeQQmlTypePrivate(data->nameToType, d); removeQQmlTypePrivate(data->urlToType, d); @@ -1203,6 +1204,7 @@ void QQmlMetaType::unregisterType(int typeIndex) module->remove(d); data->clearPropertyCachesForMinorVersion(typeIndex); data->types[typeIndex] = QQmlType(); + data->undeletableTypes.remove(type); } } diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 9db089c330..57c4eec879 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -454,16 +454,16 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, if (!includeEnums || !name->startsWithUpper()) { QQmlData *ddata = QQmlData::get(qobjectSingleton, false); if (ddata && ddata->propertyCache) { - ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); if (property) { - lookup->qobjectLookup.ic = This->internalClass(); - lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject()); + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + lookup->qobjectLookup.qmlTypeIc = This->internalClass(); + lookup->qobjectLookup.ic = val->objectValue()->internalClass(); lookup->qobjectLookup.propertyCache = ddata->propertyCache; lookup->qobjectLookup.propertyCache->addref(); lookup->qobjectLookup.propertyData = property; - lookup->getter = QV4::QObjectWrapper::lookupGetter; - return lookup->getter(lookup, engine, *This); + lookup->getter = QQmlTypeWrapper::lookupSingletonProperty; + return lookup->getter(lookup, engine, *object); } // Fall through to base implementation } @@ -485,6 +485,39 @@ bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine return Object::virtualResolveLookupSetter(object, engine, lookup, value); } +ReturnedValue QQmlTypeWrapper::lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [l, engine, &object]() { + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(l, engine, object); + }; + + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); + if (!o || o->internalClass != l->qobjectLookup.qmlTypeIc) + return revertLookup(); + + Heap::QQmlTypeWrapper *This = static_cast<Heap::QQmlTypeWrapper *>(o); + + QQmlType type = This->type(); + if (!type.isValid()) + return revertLookup(); + + if (!type.isQObjectSingleton() && !type.isCompositeSingleton()) + return revertLookup(); + + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + QObject *qobjectSingleton = e->singletonInstance<QObject *>(type); + Q_ASSERT(qobjectSingleton); + + Scope scope(engine); + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, qobjectSingleton)); + return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index c797a4ac10..6b51f421b3 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -114,6 +114,8 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static ReturnedValue lookupSingletonProperty(Lookup *l, ExecutionEngine *engine, const Value &base); + protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index cff9cb11a4..f7a91c8b81 100644 --- a/src/qml/qmldirparser/qqmldirparser_p.h +++ b/src/qml/qmldirparser/qqmldirparser_p.h @@ -76,12 +76,24 @@ public: QString typeNamespace() const; void setTypeNamespace(const QString &s); + static void checkNonRelative(const char *item, const QString &typeName, const QString &fileName) + { + if (fileName.startsWith(QLatin1Char('/')) || fileName.contains(QLatin1Char(':'))) { + qWarning() << item << typeName + << "is specified with non-relative URL" << fileName << "in a qmldir file." + << "URLs in qmldir files should be relative to the qmldir file's directory."; + } + } + struct Plugin { Plugin() {} Plugin(const QString &name, const QString &path) - : name(name), path(path) {} + : name(name), path(path) + { + checkNonRelative("Plugin", name, path); + } QString name; QString path; @@ -93,7 +105,10 @@ public: Component(const QString &typeName, const QString &fileName, int majorVersion, int minorVersion) : typeName(typeName), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion), - internal(false), singleton(false) {} + internal(false), singleton(false) + { + checkNonRelative("Component", typeName, fileName); + } QString typeName; QString fileName; @@ -108,7 +123,10 @@ public: Script() {} Script(const QString &nameSpace, const QString &fileName, int majorVersion, int minorVersion) - : nameSpace(nameSpace), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion) {} + : nameSpace(nameSpace), fileName(fileName), majorVersion(majorVersion), minorVersion(minorVersion) + { + checkNonRelative("Script", nameSpace, fileName); + } QString nameSpace; QString fileName; diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc index b0e45fadae..8edc5cb0b6 100644 --- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc @@ -31,72 +31,68 @@ \section1 Scene Graph Adaptations in Qt Quick -Originally Qt Quick always relied on OpenGL (OpenGL ES 2.0 or OpenGL 2.0) for parsing -the scene graph and rendering the results to a render target. From Qt 5.8 onwards -Qt Quick also supports rendering in software and with Direct3D 12. +Originally, Qt Quick always relied on OpenGL (OpenGL ES 2.0 or OpenGL 2.0) to parse the scene graph +and render the results to a render target. From Qt 5.8 onwards, Qt Quick also supports rendering in +software and with Direct3D 12. -\section1 Switching Between the Adaptation Used by the Application +\target Switching Between the Adaptation Used by the Application +\section1 Switch Between Adaptations in Your Application -The default rendering backend is still OpenGL, or - in Qt builds with disabled OpenGL support - -the software renderer. This can be overridden either by using an environment variable -or a C++ API. The former consists of setting the \c{QT_QUICK_BACKEND} or the -legacy \c{QMLSCENE_DEVICE} environment variable before launching applications. -The latter is done by calling QQuickWindow::setSceneGraphBackend() early in the -application's main() function. - -The supported backends are the following +The default rendering backend is still OpenGL, but in Qt builds with OpenGL support disabled, the +default is the software renderer. You can override this in one of two ways: \list + \li Use an environment variable - Set the \c{QT_QUICK_BACKEND} or the legacy + \c{QMLSCENE_DEVICE} environment variable before launching applications. + \li Use a C++ API - Call QQuickWindow::setSceneGraphBackend() early on in the application's + main() function. +\endlist -\li OpenGL - Requested by the string \c{""} or the enum value QSGRendererInterface::OpenGL. - -\li Software - Requested by the string \c{"software"} or the enum value QSGRendererInterface::Software. - -\li Direct3D 12 - Requested by the string \c{"d3d12"} or the enum value QSGRendererInterface::Direct3D12. - -\li OpenVG - Requested by the string \c{"openvg"} or the enum value QSGRendererInterface::OpenVG. +The following backends are supported: +\list + \li OpenGL - Request with the \c{""} string or the QSGRendererInterface::OpenGL enum value. + \li Software - Request with the \c{"software"} string or the QSGRendererInterface::Software + enum value. + \li Direct3D 12 - Request with the \c{"d3d12"} string or the QSGRendererInterface::Direct3D12 + enum value. + \li OpenVG - Request with the \c{"openvg"} string or the QSGRendererInterface::OpenVG enum + value. \endlist -When in doubt which backend is in use, enable basic scenegraph information -logging via the \c{QSG_INFO} environment variable or the -\c{qt.scenegraph.general} logging category. This will result in printing some -information during application startup onto the debug output. +To find out which backend is in use, you can enable basic scene graph information logging via the +\c{QSG_INFO} environment variable or the \c{qt.scenegraph.general} logging category. This results +in some information being printed onto the debug output, during application startup. -\note Adaptations other than OpenGL will typically come with a set of -limitations since they are unlikely to provide a feature set 100% compatible -with OpenGL. However, they may provide their own specific advantages in certain -areas. Refer to the sections below for more information on the various -adaptations. +\note Typically, adaptations other than OpenGL come with a set of limitations as they are unlikely + to provide a feature set that's 100% compatible with OpenGL. However, these adaptations may + provide their own specific advantages in certain areas. For more information on the various + adaptations, refer to the sections below. \section1 OpenGL ES 2.0 and OpenGL 2.0 Adaptation -The default adaptation capable of providing the full Qt Quick 2 feature -set is the OpenGL adaptation. All of the details of the OpenGL -adaptation can are available here: -\l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation} +The OpenGL adaptation is the default adaptation, which is capable of providing the full Qt Quick 2 +feature set. For more details, see +\l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation}. \section1 Software Adaptation -The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that -uses the raster paint engine to render the contents of the scene graph. The -details for this adaptation are available here: -\l{qtquick-visualcanvas-adaptations-software.html}{Software Adaptation} +The Software adaptation is an alternative renderer for \l{Qt Quick} 2 that uses the raster paint +engine to render the contents of the scene graph. For more details, see +\l{qtquick-visualcanvas-adaptations-software.html}{Software Adaptation}. \section1 Direct3D 12 (experimental) -The Direct3D 12 adaptation is an alternative renderer for \l {Qt Quick} 2 when -running on Windows 10, both for Win32 and UWP applications. The details for -this adaptation are available here: -\l{qtquick-visualcanvas-adaptations-d3d12.html}{Direct3D 12 Adaptation} +The Direct3D 12 adaptation is an alternative renderer for \l{Qt Quick} 2 when running on Windows +10, both for Win32 and UWP applications. For more details, see +\l{qtquick-visualcanvas-adaptations-d3d12.html}{Direct3D 12 Adaptation}. \section1 OpenVG -The OpenVG adaptation is an alternative renderer for \l {Qt Quick} 2 that will -renderer the contents of the scene graph using OpenVG commands to provide -hardware-acclerated 2D vector and raster graphics. The details for this -adaptation are available here: -\l{qtquick-visualcanvas-adaptations-openvg.html}{OpenVG Adaptation} +The OpenVG adaptation is an alternative renderer for \l{Qt Quick} 2 that renders the contents of +the scene graph using OpenVG commands to provide hardware-accelerated 2D vector and raster +graphics. For more details, see +\l{qtquick-visualcanvas-adaptations-openvg.html}{OpenVG Adaptation}. */ @@ -104,47 +100,44 @@ adaptation are available here: \title Qt Quick Software Adaptation \page qtquick-visualcanvas-adaptations-software.html -The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that -uses the Raster paint engine to render the contents of the scene graph instead -of OpenGL. As a result of not using OpenGL to render the scene graph, some -features and optimizations are no longer available. Most Qt Quick 2 -applications will run without modification though any attempts to use -unsupported features will be ignored. By using the Software adaptation it is -possible to run Qt Quick 2 applications on hardware and platforms that do not -have OpenGL support. - -The Software adaptation was previously known as the Qt Quick 2D Renderer. -However, unlike the 2D Renderer, the new, integrated version supports partial -updates. This means that the full update of the window or screen contents is -now avoided, and only the changed areas get flushed. This can significantly -improve performance for many applications. +The Software adaptation is an alternative renderer for \l {Qt Quick} 2 that uses the Raster paint +engine to render the contents of the scene graph, instead of OpenGL. Consequently, some features +and optimizations are not available. Most Qt Quick 2 applications can run without any modification, +but any attempts to use unsupported features are ignored. By using the Software adaptation, it is +possible to run Qt Quick 2 applications on hardware and platforms that do not have OpenGL support. + +The Software adaptation was previously known as the Qt Quick 2D Renderer. However, unlike the 2D +Renderer, this new, integrated version supports partial updates. This means that a full update +of the window or screen contents is now avoided; only the changed areas are flushed. Partial +updates can significantly improve performance for many applications. \section2 Shader Effects -ShaderEffect components in QtQuick 2 can not be rendered by the Software adptation. + +ShaderEffect components in QtQuick 2 cannot be rendered by the Software adaptation. \section2 Qt Graphical Effects Module -\l {Qt Graphical Effects} uses ShaderEffect items to render effects. If you use -graphical effects from this module, then you should not hide the source -item so that the original item can still be rendered. + +\l {Qt Graphical Effects} uses ShaderEffect items to render effects. If you use graphical effects +from this module, then you should not hide the source item so that the original item can still be +rendered. \section2 Particle Effects -It is not possible to render particle effects with the Software adaptation. Whenever -possible, remove particles completely from the scene. Otherwise they will still -require some processing, even though they are not visible. + +It is not possible to render particle effects with the Software adaptation. Whenever possible, +remove particles completely from the scene. Otherwise, they will still require some processing, +even though they are not visible. \section2 Rendering Text -The text rendering with the Software adaptation is based on software -rasterization and does not respond as well to transformations such as scaling -as when using OpenGL. The quality is similar to choosing \l [QML] {Text::renderType} -{Text.NativeRendering} with \l [QML] {Text} items. -\section2 Qt Multimedia VideoOutput -The VideoOutput item of the Qt Multimedia module is not supported with the software -adaptation. This is because VideoOutput uses the QVideoRendererControl item which -requires custom QSGGeometryNode behavior that is only present in the default OpenGL -adaptation. +The text rendering with the Software adaptation is based on software rasterization and does not +respond as well to transformations such as scaling, compared to when using OpenGL. The quality is +similar to choosing \l [QML] {Text::renderType}{Text.NativeRendering} with \l [QML] {Text} items. +\section2 Qt Multimedia VideoOutput +The Qt Multimedia module's VideoOutput item is not supported with the Software adaptation. This +is because VideoOutput uses the QVideoRendererControl item which requires custom QSGGeometryNode +behavior, which is only present in the default OpenGL adaptation. */ @@ -152,251 +145,228 @@ adaptation. \title Qt Quick Direct3D 12 Adaptation \page qtquick-visualcanvas-adaptations-d3d12.html -The Direct3D 12 adaptation for Windows 10 (both Win32 (\c windows platform -plugin) and UWP (\c winrt platform plugin)) is shipped as a dynamically loaded -plugin. It will not be functional on earlier Windows versions. The building of -the plugin is enabled automatically whenever the necessary D3D and DXGI -develpoment files are present. In practice this currently means Visual Studio -2015 and newer. +The Direct3D 12 adaptation for Windows 10, both in Win32 (\c windows platform plugin) and in UWP +(\c winrt platform plugin), is shipped as a dynamically loaded plugin. This adaptation doesn't work +on earlier Windows versions. Building this plugin is enabled automatically, whenever the necessary +D3D and DXGI develpoment files are present. In practice, this currently means Visual Studio 2015 +and newer. -The adaptation is available both in normal, OpenGL-enabled Qt builds and also -when Qt was configured with \c{-no-opengl}. However, it is never the default, -meaning the user or the application has to explicitly request it by setting the -\c{QT_QUICK_BACKEND} environment variable to \c{d3d12} or by calling -QQuickWindow::setSceneGraphBackend(). +The adaptation is available both in normal, OpenGL-enabled Qt builds, and also when Qt is +configured with \c{-no-opengl}. However, it's never the default, meaning that the user or the +application has to explicitly request it by setting the \c{QT_QUICK_BACKEND} environment variable +to \c{d3d12} or by calling QQuickWindow::setSceneGraphBackend(). \section2 Motivation -This experimental adaptation is the first Qt Quick backend focusing on a -modern, lower-level graphics API in combination with a windowing system -interface different from the traditional approaches used in combination with -OpenGL. - -It also allows better integration with Windows, Direct3D being the primary -vendor-supported solution. This means that there are fewer problems anticipated -with drivers, operations like window resizes, and special events like graphics -device loss caused by device resets or graphics driver updates. - -Performance-wise the general expectation is a somewhat lower CPU usage compared -to OpenGL due to lower driver overhead, and a higher GPU utilization with less -wasted idle time. The backend does not heavily utilize threads yet, which means -there are opportunities for further improvements in the future, for example to -further optimize image loading. - -The D3D12 backend also introduces support for pre-compiled shaders. All the -backend's own shaders (used by the built-in materials on which the Rectangle, -Image, Text, etc. QML types are built) are compiled to D3D shader bytecode when -compiling Qt. Applications using ShaderEffect items can chose to ship bytecode -either in regular files or via the Qt resource system, or use HLSL source -strings. Unlike OpenGL, the compilation for the latter is properly threaded, -meaning shader compilation will not block the application and its user +This experimental adaptation is the first Qt Quick backend that focuses on a modern, lower-level +graphics API in combination with a windowing system interface that's different from the traditional +approaches used in combination with OpenGL. + +This adaptation also allows better integration with Windows, as Direct3D is the primary +vendor-supported solution. Consequently, there are fewer problems anticipated with drivers, +operations like window resizes, and special events like graphics device loss caused by device +resets or graphics driver updates. + +Performance-wise, the general expectation is a somewhat lower CPU usage compared to OpenGL, due to +lower driver overhead, and a higher GPU utilization with less idle time wastage. The backend +doesn't heavily utilize threads yet, which means there are opportunities for further improvements +in the future, for example to further optimize image loading. + +The D3D12 backend also introduces support for pre-compiled shaders. All the backend's own shaders +(used by the built-in materials on which the Rectangle, Image, Text, and other QML types are built +with) are compiled to D3D shader bytecode when you compile Qt. Applications using ShaderEffect +items can choose to ship bytecode either in regular files, via the Qt resource system, or use +High Level Shading Language for DirectX (HLSL) source strings. Unlike OpenGL, the compilation for +HLSL is properly threaded, meaning shader compilation won't block the application and its user interface. \section2 Graphics Adapters -The plugin does not necessarily require hardware acceleration. Using WARP, the -Direct3D software rasterizer, is also an option. By default the first adapter -providing hardware acceleration is chosen. To override this, in order to use -another graphics adapter or to force the usage of the software rasterizer, set -the environment variable \c{QT_D3D_ADAPTER_INDEX} to the index of the adapter. -The discovered adapters are printed at startup when \c{QSG_INFO} or the logging -category \c{qt.scenegraph.general} is enabled. +The plugin does not necessarily require hardware acceleration. You can also use WARP, the Direct3D +software rasterizer. By default, the first adapter providing hardware acceleration is chosen. To +override this and use another graphics adapter or to force the use of the software rasterizer, set +the \c{QT_D3D_ADAPTER_INDEX} environment variable to the index of the adapter. The adapters +discovered are printed at startup when \c{QSG_INFO} or the \c{qt.scenegraph.general} logging +category is enabled. \section2 Troubleshooting -When encountering issues, always set the \c{QSG_INFO} and \c{QT_D3D_DEBUG} -environment variables to 1 in order to get debug and warning messages printed -on the debug output. The latter enables the Direct3D debug layer. Note that the -debug layer should not be enabled in production use since it can significantly -impact performance (CPU load) due to increased API overhead. +If you encounter issues, always set the \c{QSG_INFO} and \c{QT_D3D_DEBUG} environment variables +to \c 1, to get debug and warning messages printed on the debug output. \c{QT_D3D_DEBUG} enables +the Direct3D debug layer. + +\note The debug layer shouldn't be enabled in production use, since it can significantly impact +performance (CPU load) due to increased API overhead. \section2 Render Loops -By default the D3D12 adaptation uses a single-threaded render loop similar to -OpenGL's \c windows render loop. There is also a threaded variant available, that -can be requested by setting the \c{QSG_RENDER_LOOP} environment variable to \c -threaded. However, due to conceptual limitations in DXGI, the windowing system -interface, the threaded loop is prone to deadlocks when multiple QQuickWindow -or QQuickView instances are shown. Therefore the default is the single-threaded -loop for the time being. This means that with the D3D12 backend applications -are expected to move their work from the main (GUI) thread out to worker -threads, instead of expecting Qt to keep the GUI thread responsive and suitable -for heavy, blocking operations. - -See the \l{qtquick-visualcanvas-scenegraph.html}{Scene Graph page} for more -information on render loops and -\l{https://msdn.microsoft.com/en-us/library/windows/desktop/ee417025(v=vs.85).aspx#multithreading_and_dxgi}{the -MSDN page for DXGI} regarding the issues with multithreading. +By default, the D3D12 adaptation uses a single-threaded render loop similar to OpenGL's \c windows +render loop. A threaded variant is also available, that you can request by setting the +\c{QSG_RENDER_LOOP} environment variable to \c threaded. However, due to conceptual limitations in +DXGI, the windowing system interface, the threaded loop is prone to deadlocks when multiple +QQuickWindow or QQuickView instances are shown. Consequently, for the time being, the default is +the single-threaded loop. This means that with the D3D12 backend, applications are expected to move +their work from the main (GUI) thread out to worker threads, instead of expecting Qt to keep the +GUI thread responsive and suitable for heavy, blocking operations. + +For more information see \l{qtquick-visualcanvas-scenegraph.html}{Qt Quick Scene Graph} for +details on render loops and +\l{https://docs.microsoft.com/en-us/windows/desktop/direct3darticles/dxgi-best-practices#multithreading-and-dxgi}{Multithreading and DXGI} +regarding the issues with multithreading. \section2 Renderer -The scenegraph renderer in the D3D12 adaptation does not currently perform any -batching. This is less of an issue, unlike OpenGL, because state changes are -not presenting any problems in the first place. The simpler renderer logic can -also lead to lower CPU overhead in some cases. The trade-offs between the -various approaches are currently under research. +The scene graph renderer in the D3D12 adaptation currently doesn't perform any batching. This is +less of an issue, unlike OpenGL, because state changes don't present any problems in the first +place. The simpler renderer logic can also lead to lower CPU overhead in some cases. The trade-offs +between the various approaches are currently under research. \section2 Shader Effects -The ShaderEffect QML type is fully functional with the D3D12 adaptation as well. -However, the interpretation of the fragmentShader and vertexShader properties is -different than with OpenGL. +The ShaderEffect QML type is fully functional with the D3D12 adaptation as well. However, the +interpretation of the fragmentShader and vertexShader properties is different than with OpenGL. -With D3D12, these strings can either be an URL for a local file or a file in -the resource system, or a HLSL source string. The former indicates that the -file in question contains pre-compiled D3D shader bytecode generated by the -\c fxc tool, or, alternatively, HLSL source code. The type of the file is detected -automatically. This means that the D3D12 backend supports all options from -GraphicsInfo.shaderCompilationType and GraphicsInfo.shaderSourceType. +With D3D12, these strings can either be a URL for a local file, a file in the resource system, +or an HLSL source string. Using a URL for a local file or a file in the resource system +indicates that the file in question contains pre-compiled D3D shader bytecode generated by the +\c fxc tool, or, alternatively, HLSL source code. The type of file is detected automatically. +This means that the D3D12 backend supports all options from GraphicsInfo.shaderCompilationType +and GraphicsInfo.shaderSourceType. -Unlike OpenGL, there is a QFileSelector with the extra selector \c hlsl used -whenever opening a file. This allows easy creation of ShaderEffect items that -are functional across both backends, for example by placing the GLSL source -code into \c{shaders/effect.frag}, the HLSL source code or - preferably - -pre-compiled bytecode into \c{shaders/+hlsl/effect.frag}, while simply writing -\c{fragmentShader: "qrc:shaders/effect.frag"} in QML. - -See the ShaderEffect documentation for more details. +Unlike OpenGL, whenever you open a file, there is a QFileSelector with the extra \c hlsl selector +used. This provides easy creation of ShaderEffect items that are functional across both backends, +for example by placing the GLSL source code into \c{shaders/effect.frag}, the HLSL source code or +- preferably - pre-compiled bytecode into \c{shaders/+hlsl/effect.frag}, while simply writing +\c{fragmentShader: "qrc:shaders/effect.frag"} in QML. For more details, see ShaderEffect. \section2 Multisample Render Targets -The Direct3D 12 adaptation ignores the QSurfaceFormat set on the QQuickWindow -or QQuickView (or set via QSurfaceFormat::setDefaultFormat()), with two -exceptions: QSurfaceFormat::samples() and QSurfaceFormat::alphaBufferSize() are -still taken into account. When the samples value is greater than 1, multisample -offscreen render targets will be created with the specified sample count and a -quality of the maximum supported quality level. The backend automatically -performs resolving into the non-multisample swapchain buffers after each frame. +The Direct3D 12 adaptation ignores the QSurfaceFormat set on the QQuickWindow or QQuickView, or +set via QSurfaceFormat::setDefaultFormat(), with two exceptions: QSurfaceFormat::samples() and +QSurfaceFormat::alphaBufferSize() are still taken into account. When the sample value is greater +than 1, multisample offscreen render targets will be created with the specified sample count at +the maximum supported quality level. The backend automatically performs resolving into the +non-multisample swapchain buffers after each frame. \section2 Semi-transparent Windows -When the alpha channel is enabled either via -QQuickWindow::setDefaultAlphaBuffer() or by setting alphaBufferSize to a -non-zero value in the window's QSurfaceFormat or in the global format managed -by QSurfaceFormat::setDefaultFormat(), the D3D12 backend will create a -swapchain for composition and go through DirectComposition since the flip model -swapchain (which is mandatory) would not support transparency otherwise. +When the alpha channel is enabled either via QQuickWindow::setDefaultAlphaBuffer() or by setting +alphaBufferSize to a non-zero value in the window's QSurfaceFormat or in the global format managed +by QSurfaceFormat::setDefaultFormat(), the D3D12 backend will create a swapchain for composition +and go through DirectComposition. This is necessary, because the mandatory flip model swapchain +wouldn't support transparency otherwise. -It is therefore important not to unneccessarily request an alpha channel. When -the alphaBufferSize is 0 or the default -1, all these extra steps can be -avoided and the traditional window-based swapchain is sufficient. +Therefore, it's important not to unneccessarily request an alpha channel. When the alphaBufferSize +is 0 or the default -1, all these extra steps can be avoided and the traditional window-based +swapchain is sufficient. -This is not relevant on WinRT because there the backend always uses a -composition swapchain which is associated with the ISwapChainPanel that backs -QWindow on that platform. +On WinRT, this isn't relevant because the backend there always uses a composition swapchain which +is associated with the ISwapChainPanel that backs QWindow on that platform. \section2 Mipmaps -Mipmap generation is supported and handled transparently to the applications -via a built-in compute shader, but is experimental and only supports -power-of-two images at the moment. Textures of other size will work too, but -this involves a QImage-based scaling on the CPU first. Therefore avoid enabling -mipmapping for NPOT images whenever possible. +Mipmap generation is supported and handled transparently to the applications via a built-in compute +shader. However, at the moment, this feature is experimental and only supports power-of-two images. +Textures of other size will work too, but this involves a QImage-based scaling on the CPU first. +Therefore, avoid enabling mipmapping for Non-Power-Of-Two (NPOT) images whenever possible. \section2 Image Formats -When creating textures via the C++ scenegraph APIs like -QQuickWindow::createTextureFromImage(), 32-bit formats will not involve any -conversion, they will map directly to the corresponding \c{R8G8B8A8_UNORM} or -\c{B8G8R8A8_UNORM} format. Everything else will trigger a QImage-based format -conversion on the CPU first. +When creating textures via C++ scene graph APIs like QQuickWindow::createTextureFromImage(), 32-bit +formats won't involve any conversion, they'll map directly to the corresponding \c{R8G8B8A8_UNORM} +or \c{B8G8R8A8_UNORM} format. Everything else will trigger a QImage-based format conversion on the +CPU first. \section2 Unsupported Features -Particles and some other OpenGL-dependent utilities, like -QQuickFramebufferObject, are not currently supported. +Particles and some other OpenGL-dependent utilities, like QQuickFramebufferObject, are currently +not supported. -Like with the \l{qtquick-visualcanvas-adaptations-software.html}{Software -adaptation}, text is always rendered using the native method. Distance -field-based text rendering is not currently implemented. +Like with \l{qtquick-visualcanvas-adaptations-software.html}{Software adaptation}, text is always +rendered using the native method. Distance field-based text rendering is currently not implemented. -The shader sources in the \l {Qt Graphical Effects} module have not been ported -to any format other than the OpenGL 2.0 compatible one, meaning the QML types -provided by that module are not currently functional with the D3D12 backend. +The shader sources in the \l {Qt Graphical Effects} module have not been ported to any format other +than the OpenGL 2.0 compatible one, meaning that the QML types provided by that module are currently +not functional with the D3D12 backend. -Texture atlases are not currently in use. +Texture atlases are currently not in use. -The renderer may lack support for certain minor features, for example drawing -points and lines with a width other than 1. +The renderer may lack support for certain minor features, such as drawing points and lines with a +width other than 1. -Custom Qt Quick items using custom scenegraph nodes can be problematic. -Materials are inherently tied to the graphics API. Therefore only items using -the utility rectangle and image nodes are functional across all adaptations. +Custom Qt Quick items using custom scene graph nodes can be problematic because materials are +inherently tied to the graphics API. Therefore, only items that use the utility rectangle and image +nodes are functional across all adaptations. -QQuickWidget and its underlying OpenGL-based compositing architecture is not -supported. If mixing with QWidget-based user interfaces is desired, use -QWidget::createWindowContainer() to embed the native window of the QQuickWindow -or QQuickView. +QQuickWidget and its underlying OpenGL-based compositing architecture is not supported. If you need +to mix with QWidget-based user interfaces, use QWidget::createWindowContainer() to embed the native +window of the QQuickWindow or QQuickView. -Finally, rendering via QSGEngine and QSGAbstractRenderer is not feasible with -the D3D12 adaptation at the moment. +Finally, rendering via QSGEngine and QSGAbstractRenderer is not feasible with the D3D12 adaptation +at the moment. \section2 Related APIs -To integrate custom Direct3D 12 rendering, use QSGRenderNode in combination -with QSGRendererInterface. This approach does not rely on OpenGL contexts or -API specifics like framebuffers, and allows exposing the graphics device and -command buffer from the adaptation. It is not necessarily suitable for easy -integration of all types of content, in particular true 3D, so it will likely -get complemented by an alternative to QQuickFramebufferObject in future -releases. +To integrate custom Direct3D 12 rendering, use QSGRenderNode in combination with +QSGRendererInterface. This approach doesn't rely on OpenGL contexts or API specifics like +framebuffers, and allows exposing the graphics device and command buffer from the adaptation. It's +not necessarily suitable for easy integration of all types of content, in particular true 3D, so +it'll likely get complemented by an alternative to QQuickFramebufferObject in future releases. -To perform runtime decisions based on the adaptation in use, use -QSGRendererInterface from C++ and GraphicsInfo from QML. They can also be used -to check the level of shader support (shading language, compilation approach). +To perform runtime decisions based on the adaptation, use QSGRendererInterface from C++ and +GraphicsInfo from QML. They can also be used to check the level of shader support: shading +language, compilation approach, and so on. -When creating custom items, use the new QSGRectangleNode and QSGImageNode -classes. These replace the now deprecated QSGSimpleRectNode and -QSGSimpleTextureNode. Unlike their predecessors, the new classes are -interfaces, and implementations are created via the factory functions -QQuickWindow::createRectangleNode() and QQuickWindow::createImageNode(). +When creating custom items, use the new QSGRectangleNode and QSGImageNode classes. These replace +the now deprecated QSGSimpleRectNode and QSGSimpleTextureNode. Unlike their predecessors, these new +classes are interfaces, and implementations are created via the QQuickWindow::createRectangleNode() +and QQuickWindow::createImageNode() factory functions. \section2 Advanced Configuration -The D3D12 adaptation can keep multiple frames in flight, similarly to modern -game engines. This is somewhat different from the traditional render - swap - -wait for vsync model and allows better GPU utilization at the expense of higher -resource usage. This means that the renderer will be a number of frames ahead -of what is displayed on the screen. +The D3D12 adaptation can keep multiple frames in flight, similar to modern game engines. This is +somewhat different from the traditional "render - swap - wait for vsync" model and allows for +better GPU utilization at the expense of higher resource use. This means that the renderer will +be a number of frames ahead of what is displayed on the screen. -For a discussion of flip model swap chains and the typical configuration -parameters, refer to -\l{https://software.intel.com/en-us/articles/sample-application-for-direct3d-12-flip-model-swap-chains}{this -article}. +For a discussion of flip model swap chains and the typical configuration parameters, refer to +\l{https://software.intel.com/en-us/articles/sample-application-for-direct3d-12-flip-model-swap-chains} +{Sample Application for Direct3D 12 Flip Model Swap Chains}. -Vertical synchronization is always enabled, meaning Present() is invoked with -an interval of 1. +Vertical synchronization is always enabled, meaning Present() is invoked with an interval of 1. The configuration can be changed by setting the following environment variables: -\list - -\li \c{QT_D3D_BUFFER_COUNT} - The number of swap chain buffers in range 2 - 4. -The default value is 3. - -\li \c{QT_D3D_FRAME_COUNT} - The number of frames prepared without blocking in -range 1 - 4. Note that Present will start blocking after queuing 3 frames -(regardless of \c{QT_D3D_BUFFER_COUNT}), unless the waitable object is in use. -Note that every additional frame increases GPU resource usage since geometry -and constant buffer data will have to be duplicated, and involves more -bookkeeping on the CPU side. The default value is 2. - -\li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} - When set to a value between 1 -and 16, the frame latency is set to the specified value. This changes the limit -for Present() and will trigger a wait for an available swap chain buffer when -beginning each frame. Refer to the article above for a detailed discussion. -This is considered experimental for now and the default value is 0 (disabled). - -\li \c{QT_D3D_BLOCKING_PRESENT} - When set to a non-zero value, there will be -CPU-side wait for the GPU to finish its work after each call to Present. This -effectively kills all parallelism but makes the behavior resemble the -traditional swap-blocks-for-vsync model, and can therefore be useful in some -special cases. This is not the same as setting the frame count to 1 because -that still avoids blocking after Present, and may block only when starting to -prepare the next frame (or may not block at all depending on the time gap -between the frames). By default blocking present is disabled. - -\endlist +\table + \header + \li Environment variable + \li Description + \row + \li \c{QT_D3D_BUFFER_COUNT} + \li The number of swap chain buffers in range 2 - 4. The default value is 3. + \row + \li \c{QT_D3D_FRAME_COUNT} + \li The number of frames prepared without blocking in range 1 - 4. The default value is 2. + Present() starts blocking after queuing 3 frames (regardless of + \c{QT_D3D_BUFFER_COUNT}), unless the waitable object is in use. Every additional frame + increases GPU resource usage since geometry and constant buffer data needs to be + duplicated, and involves more bookkeeping on the CPU side. + \row + \li \c{QT_D3D_WAITABLE_SWAP_CHAIN_MAX_LATENCY} + \li The frame latency in range 1 - 16. The default value is 0 (disabled). + Changes the limit for Present() and triggers a wait for an available swap chain buffer + when beginning each frame. For a detailed discussion, see the article linked above. + \note Currently, this behavior is experimental. + \row + \li \c{QT_D3D_BLOCKING_PRESENT} + \li The time the CPU should wait, a non-zero value, for the GPU to finish its work after + each call to Present(). The default value is 0 (disabled). This behavior effectively + kills all parallelism but makes the behavior resemble the traditional + swap-blocks-for-vsync model, which can be useful in some special cases. However, this + behavior is not the same as setting the frame count to 1 because that still avoids + blocking after Present(), and may only block when starting to prepare the next frame + (or may not block at all depending on the time gap between the frames). +\endtable */ @@ -404,68 +374,72 @@ between the frames). By default blocking present is disabled. \title Qt Quick OpenVG Adaptation \page qtquick-visualcanvas-adaptations-openvg.html -The OpenVG adaptation is an alternative renderer for \l {Qt Quick} 2 that will -renderer the contents of the scene graph using OpenVG commands to provide -hardware-acclerated 2D vector and raster graphics. Much like the Software -adaptation, some features and optimizations are no longer available. Most -Qt Quick 2 applications will run without modification though any attempts to -use unsupported features will be ignored. +The OpenVG adaptation is an alternative renderer for \l{Qt Quick} 2 that renders the contents of +the scene graph using OpenVG commands to provide hardware accelerated 2D vector and raster +graphics. Much like the \l{qtquick-visualcanvas-adaptations-software.html}{Software Adaptation}, +some features and optimizations are no longer available. Most Qt Quick 2 applications will run +without modification though any attempts to use unsupported features will be ignored. \section2 EGL Requirement -Unlike the defualt OpenGL Renderer, there is no built in support for acquiring -an OpenVG context. This means that the renderer has the responsbility of -requesting and managing the the current context. To do this EGL has to be used -directly in the OpenVG renderer. This means that the OpenVG renderer is only -usable with platform plugins that support creating QWindows with support for -QSurfaceFormat::OpenVG. From this window, the renderer can get an EGLSurface -which can be used with an EGLContext to render OpenVG content. + +Unlike the default OpenGL Renderer, there is no built-in support to acquire an OpenVG context. +This means that the renderer is responsible for requesting and managing the the current context. +To do this, you use EGL directly in the OpenVG renderer. Consequently, the OpenVG renderer can only +be used with platform plugins that support creating QWindows with support for +QSurfaceFormat::OpenVG. From this window, the renderer can get an EGLSurface which can then be used +with an EGLContext to render OpenVG content. \section2 Renderer -The OpenVG Renderer works by using the OpenVG API to send commands and data to -a Vector GPU which will render the scenegraph in an accelerated manner, offloading -graphics rendering from the CPU. Many operations like the rendering of rectangles -and fonts glyphs ideal for OpenVG because these can be represented as paths which -are stroked and filled. Rendering scenegraph items that would typically involve -textures are handled in the OpenVG renderer by using VGImage. In addition when -rendering to offscreen surfaces (like when using Layers), the scene subtree is -rendered to a VGImage which can be reused in the scene. + +The OpenVG Renderer uses the OpenVG API to send commands and data to a Vector GPU that renders the +scene graph in an accelerated manner, offloading graphics rendering from the CPU. Many operations +like the rendering of rectangles and font glyphs are ideal for OpenVG because they can be +represented as paths which are stroked and filled. Rendering scene graph items that would typically +involve textures are handled in the OpenVG renderer using VGImage. Additionally, when you render +to offscreen surfaces (like with Layers), the scene subtree is rendered to a VGImage which can be +reused in the scene. \section2 Render Loop -The OpenVG Renderer mirrors the behavior of the Basic render loop and will execute -all OpenVG commands in a single thread. -See the \l{qtquick-visualcanvas-scenegraph.html}{Scene Graph page} for more -information on render loops +The OpenVG Renderer mirrors the behavior of the Basic render loop and it runs all OpenVG commands +in a single thread. + +For more information on render loops, see +\l{qtquick-visualcanvas-scenegraph.html}{Qt Quick Scene Graph}. \section2 Shader Effects -ShaderEffect components in QtQuick 2 can not be rendered by the OpenVG adaptation. -While it is possible to user ShaderEffectSource and QML Item Layers (which are both -offscreen surfaces), it is not actually possible to apply shader effects to them -via the ShaderEffect item. This is because OpenVG lacks an API for applying per -vertex and per fragment shader operations. It may be possible however to take -advantage of Image Filter operations in the OpenVG API to get similar effects to -what is provided by ShaderEffects in custom items. To integrate custom OpenVG -rendering, use QSGRenderNode in combination with QSGRendererInterface. + +ShaderEffect components in QtQuick 2 can't be rendered by the OpenVG adaptation. While it's +possible to use ShaderEffectSource and QML Item Layers (which are both offscreen surfaces), it's +not possible to apply shader effects to them via the ShaderEffect item. This is because OpenVG +lacks an API for applying per vertex and per fragment shader operations. However, you may be able +to take advantage of Image Filter operations in the OpenVG API to get effects that are similar to +what ShaderEffects provides in custom items. To integrate custom OpenVG rendering, use +QSGRenderNode in combination with QSGRendererInterface. \section2 Qt Graphical Effects Module -\l {Qt Graphical Effects} uses ShaderEffect items to render effects. If you use -graphical effects from this module, then you should not hide the source -item so that the original item can still be rendered. + +\l {Qt Graphical Effects} uses ShaderEffect items to render effects. If you use graphical effects +from this module, then you shouldn't hide the source item so that the original item can still be +rendered. \section2 Particle Effects -It is not possible to render particle effects with the OpenVG adaptation. Whenever -possible, remove particles completely from the scene. Otherwise they will still -require some processing, even though they are not visible. + +It's not possible to render particle effects with the OpenVG adaptation. Whenever possible, remove +particles completely from the scene. Otherwise they'll still require some processing, even though +they are not visible. \section2 Rendering Text -The text rendering with the OpenVG adaptation is based on rendering the glpyh -paths, and does not use the distance fields technique used by the OpenGL backend. + +Text rendering with the OpenVG adaptation is based on rendering the glyph paths, and doesn't use +the distance fields technique, unlike with the OpenGL backend. \section2 Perspective Transforms -The OpenVG API does not allow paths to be transformed with non-affine transforms, -while it is possible with Qt Quick. This means that rendering components using -paths like Rectangles and Text, when applying perspective transforms the OpenVG -backend will first render to a VGImage before applying transformations. This uses -more memory at runtime and is a slower path so avoid doing this if necessary. + +The OpenVG API doesn't allow paths to be transformed with non-affine transforms, but it's +possible with Qt Quick. Consquently, when you render components using paths like Rectangles and +Text while applying perspective transforms, the OpenVG backend first renders to a VGImage before +applying transformations. This behavior uses more memory at runtime and takes more time; avoid it +if possible. */ diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 6638fbd3e8..fecfa5fd2d 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -191,7 +191,7 @@ public: void resetFirstItemPosition(qreal pos = 0.0) override; void adjustFirstItem(qreal forwards, qreal backwards, int changeBeforeVisible) override; - void createHighlight() override; + void createHighlight(bool onDestruction = false) override; void updateHighlight() override; void resetHighlightPosition() override; @@ -696,9 +696,8 @@ void QQuickGridViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int gridItem->setPosition(gridItem->colPos(), gridItem->rowPos() + ((moveCount / columns) * rowSize())); } -void QQuickGridViewPrivate::createHighlight() +void QQuickGridViewPrivate::createHighlight(bool onDestruction) { - Q_Q(QQuickGridView); bool changed = false; if (highlight) { if (trackedItem == highlight) @@ -714,6 +713,10 @@ void QQuickGridViewPrivate::createHighlight() changed = true; } + if (onDestruction) + return; + + Q_Q(QQuickGridView); if (currentItem) { QQuickItem *item = createHighlightItem(); if (item) { diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 1f8a0de72b..431cb80a75 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -162,7 +162,7 @@ QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent) QQuickItemView::~QQuickItemView() { Q_D(QQuickItemView); - d->clear(); + d->clear(true); if (d->ownModel) delete d->model; delete d->header; @@ -1662,7 +1662,7 @@ void QQuickItemViewPrivate::updateCurrent(int modelIndex) releaseItem(oldCurrentItem); } -void QQuickItemViewPrivate::clear() +void QQuickItemViewPrivate::clear(bool onDestruction) { Q_Q(QQuickItemView); currentChanges.reset(); @@ -1683,7 +1683,7 @@ void QQuickItemViewPrivate::clear() currentItem = nullptr; if (oldCurrentItem) emit q->currentItemChanged(); - createHighlight(); + createHighlight(onDestruction); trackedItem = nullptr; if (requestedIndex >= 0) { @@ -2352,15 +2352,16 @@ void QQuickItemView::destroyingItem(QObject *object) bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) { Q_Q(QQuickItemView); - if (!item || !model) + if (!item) return true; if (trackedItem == item) trackedItem = nullptr; item->trackGeometry(false); - QQmlInstanceModel::ReleaseFlags flags = model->release(item->item); - if (item->item) { - if (flags == 0) { + QQmlInstanceModel::ReleaseFlags flags = {}; + if (model && item->item) { + flags = model->release(item->item); + if (!flags) { // item was not destroyed, and we no longer reference it. QQuickItemPrivate::get(item->item)->setCulled(true); unrequestedItems.insert(item->item, model->indexOf(item->item, q)); diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 0f1594f904..860605991b 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -163,7 +163,7 @@ public: int mapFromModel(int modelIndex) const; virtual void init(); - virtual void clear(); + virtual void clear(bool onDestruction=false); virtual void updateViewport(); void regenerate(bool orientationChanged=false); @@ -327,7 +327,7 @@ protected: virtual bool hasStickyHeader() const { return false; } virtual bool hasStickyFooter() const { return false; } - virtual void createHighlight() = 0; + virtual void createHighlight(bool onDestruction = false) = 0; virtual void updateHighlight() = 0; virtual void resetHighlightPosition() = 0; virtual bool movingFromHighlight() { return false; } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 81d019a26d..146917c679 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -81,7 +81,7 @@ public: FxViewItem *snapItemAt(qreal pos); void init() override; - void clear() override; + void clear(bool onDestruction) override; bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer) override; bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo) override; @@ -98,7 +98,7 @@ public: void adjustFirstItem(qreal forwards, qreal backwards, int) override; void updateSizeChangesBeforeVisiblePos(FxViewItem *item, ChangeResult *removeResult) override; - void createHighlight() override; + void createHighlight(bool onDestruction = false) override; void updateHighlight() override; void resetHighlightPosition() override; bool movingFromHighlight() override; @@ -575,7 +575,7 @@ void QQuickListViewPrivate::init() ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize); } -void QQuickListViewPrivate::clear() +void QQuickListViewPrivate::clear(bool onDestruction) { for (int i = 0; i < sectionCacheSize; ++i) { delete sectionCache[i]; @@ -587,7 +587,7 @@ void QQuickListViewPrivate::clear() releaseSectionItem(nextSectionItem); nextSectionItem = nullptr; lastVisibleSection = QString(); - QQuickItemViewPrivate::clear(); + QQuickItemViewPrivate::clear(onDestruction); } FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item) @@ -634,7 +634,7 @@ void QQuickListViewPrivate::initializeViewItem(FxViewItem *item) bool QQuickListViewPrivate::releaseItem(FxViewItem *item) { if (!item || !model) - return true; + return QQuickItemViewPrivate::releaseItem(item); QPointer<QQuickItem> it = item->item; QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached); @@ -876,9 +876,8 @@ void QQuickListViewPrivate::updateSizeChangesBeforeVisiblePos(FxViewItem *item, QQuickItemViewPrivate::updateSizeChangesBeforeVisiblePos(item, removeResult); } -void QQuickListViewPrivate::createHighlight() +void QQuickListViewPrivate::createHighlight(bool onDestruction) { - Q_Q(QQuickListView); bool changed = false; if (highlight) { if (trackedItem == highlight) @@ -896,6 +895,10 @@ void QQuickListViewPrivate::createHighlight() changed = true; } + if (onDestruction) + return; + + Q_Q(QQuickListView); if (currentItem) { QQuickItem *item = createHighlightItem(); if (item) { diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp index 0cbabaa170..bc1f787b81 100644 --- a/src/quick/items/qquickopenglshadereffect.cpp +++ b/src/quick/items/qquickopenglshadereffect.cpp @@ -221,7 +221,7 @@ QQuickOpenGLShaderEffectCommon::~QQuickOpenGLShaderEffectCommon() clearSignalMappers(shaderType); } -void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QObject *obj, Key::ShaderType shaderType) +void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType) { for (int i = 0; i < uniformData[shaderType].size(); ++i) { if (signalMappers[shaderType].at(i) == 0) @@ -229,11 +229,12 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QObject *obj, Key const UniformData &d = uniformData[shaderType].at(i); auto mapper = signalMappers[shaderType].at(i); void *a = mapper; - QObjectPrivate::disconnect(obj, mapper->signalIndex(), &a); + QObjectPrivate::disconnect(item, mapper->signalIndex(), &a); if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) { QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value)); if (source) { - QQuickItemPrivate::get(source)->derefWindow(); + if (item->window()) + QQuickItemPrivate::get(source)->derefWindow(); QObject::disconnect(source, SIGNAL(destroyed(QObject*)), host, SLOT(sourceDestroyed(QObject*))); } } diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 3087c1eb0b..0c2adadc62 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -89,7 +89,7 @@ struct Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectCommon ~QQuickOpenGLShaderEffectCommon(); - void disconnectPropertySignals(QObject *item, Key::ShaderType shaderType); + void disconnectPropertySignals(QQuickItem *item, Key::ShaderType shaderType); void connectPropertySignals(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType); void updateParseLog(bool ignoreAttributes); void lookThroughShaderCode(QQuickItem *item, const QMetaObject *itemMetaObject, Key::ShaderType shaderType, const QByteArray &code); diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index f96ebebcd6..3ccd2a76f7 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -505,7 +505,7 @@ void QQuickOpenGLShaderEffectNode::markDirtyTexture() Q_EMIT dirtyTexture(); } -void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(const QObject *object) +void QQuickOpenGLShaderEffectNode::textureProviderDestroyed(QObject *object) { Q_ASSERT(material()); static_cast<QQuickOpenGLShaderEffectMaterial *>(material())->invalidateTextureProvider(object); diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h index 6d68ba87b9..705b8d4f47 100644 --- a/src/quick/items/qquickopenglshadereffectnode_p.h +++ b/src/quick/items/qquickopenglshadereffectnode_p.h @@ -159,7 +159,7 @@ Q_SIGNALS: private Q_SLOTS: void markDirtyTexture(); - void textureProviderDestroyed(const QObject *object); + void textureProviderDestroyed(QObject *object); }; QT_END_NAMESPACE diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index c8a03aff33..c8c5281089 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -120,16 +120,17 @@ QQuickRepeaterPrivate::~QQuickRepeaterPrivate() Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects. For example, it cannot be used to repeat QtObjects: - \code - //bad code + + \qml + // bad code: Item { - Can't repeat QtObject as it doesn't derive from Item. + // Can't repeat QtObject as it doesn't derive from Item. Repeater { model: 10 QtObject {} } } - \endcode + \endqml */ /*! diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 05d9e5e36d..3721731f68 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -515,6 +515,20 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent) m_impl = new QQuickGenericShaderEffect(this, this); } +QQuickShaderEffect::~QQuickShaderEffect() +{ + // Delete the implementations now, while they still have have + // valid references back to us. +#if QT_CONFIG(opengl) + auto *glImpl = m_glImpl; + m_glImpl = nullptr; + delete glImpl; +#endif + auto *impl = m_impl; + m_impl = nullptr; + delete impl; +} + /*! \qmlproperty string QtQuick::ShaderEffect::fragmentShader diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index cabad930fc..c5bddc40d2 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -92,6 +92,7 @@ public: Q_ENUM(Status) QQuickShaderEffect(QQuickItem *parent = nullptr); + ~QQuickShaderEffect() override; QByteArray fragmentShader() const; void setFragmentShader(const QByteArray &code); diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 9583ef4231..30f6350a30 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -868,6 +868,9 @@ void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable() void QQuickTableViewPrivate::forceLayout() { + if (loadedItems.isEmpty()) + return; + clearEdgeSizeCache(); RebuildOptions rebuildOptions = RebuildOption::LayoutOnly; diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index caef24293a..ac8093e10b 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -1403,7 +1403,7 @@ QVariant QQuickTextControl::inputMethodQuery(Qt::InputMethodQuery property, QVar case Qt::ImAnchorPosition: return QVariant(d->cursor.anchor() - block.position()); case Qt::ImAbsolutePosition: - return QVariant(d->cursor.anchor()); + return QVariant(d->cursor.position()); case Qt::ImTextAfterCursor: { int maxLength = argument.isValid() ? argument.toInt() : 1024; diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp index c434563c90..d8efda1ecc 100644 --- a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp +++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp @@ -258,7 +258,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, xs[1].tx = innerSourceRect.left(); xs += 2; } - if (innerTargetRect.width() != 0) { + if (innerTargetRect.width() != 0 && hTiles > 0) { xs[0].x = innerTargetRect.left(); xs[0].tx = innerSourceRect.x() + (subSourceRect.left() - floorLeft) * innerSourceRect.width(); ++xs; @@ -299,7 +299,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, ys[1].ty = innerSourceRect.top(); ys += 2; } - if (innerTargetRect.height() != 0) { + if (innerTargetRect.height() != 0 && vTiles > 0) { ys[0].y = innerTargetRect.top(); ys[0].ty = innerSourceRect.y() + (subSourceRect.top() - floorTop) * innerSourceRect.height(); ++ys; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index c18ba4226c..232ef77324 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -770,12 +770,15 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop() QSGThreadedRenderLoop::~QSGThreadedRenderLoop() { + qDeleteAll(pendingRenderContexts); delete sg; } QSGRenderContext *QSGThreadedRenderLoop::createRenderContext(QSGContext *sg) const { - return sg->createRenderContext(); + auto context = sg->createRenderContext(); + pendingRenderContexts.insert(context); + return context; } void QSGThreadedRenderLoop::maybePostPolishRequest(Window *w) @@ -935,7 +938,10 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) Window win; win.window = window; win.actualWindowFormat = window->format(); - win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context); + auto renderContext = QQuickWindowPrivate::get(window)->context; + // The thread assumes ownership, so we don't need to delete it later. + pendingRenderContexts.remove(renderContext); + win.thread = new QSGRenderThread(this, renderContext); win.updateDuringSync = false; win.forceRenderPass = true; // also covered by polishAndSync(inExpose=true), but doesn't hurt m_windows << win; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index 32bfcb7148..b8fae8e8da 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -124,6 +124,9 @@ private: QSGContext *sg; + // Set of contexts that have been created but are now owned by + // a rendering thread yet, as the window has never been exposed. + mutable QSet<QSGRenderContext*> pendingRenderContexts; QAnimationDriver *m_animation_driver; QList<Window> m_windows; diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 6970553fc6..e96f71a551 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -1314,9 +1314,13 @@ void QQuickWidget::mouseDoubleClickEvent(QMouseEvent *e) void QQuickWidget::showEvent(QShowEvent *) { Q_D(QQuickWidget); + bool shouldTriggerUpdate = true; + if (!d->useSoftwareRenderer) { d->createContext(); + if (d->offscreenWindow->openglContext()) { + shouldTriggerUpdate = false; d->render(true); // render() may have led to a QQuickWindow::update() call (for // example, having a scene with a QQuickFramebufferObject::Renderer @@ -1329,11 +1333,12 @@ void QQuickWidget::showEvent(QShowEvent *) d->updatePending = false; update(); } - } else { - triggerUpdate(); } } + if (shouldTriggerUpdate) + triggerUpdate(); + // note offscreenWindow is "QQuickOffScreenWindow" instance d->offscreenWindow->setVisible(true); if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) diff --git a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp index c7f8ec1118..f08f3c1da7 100644 --- a/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp +++ b/tests/auto/qml/debugger/qqmlpreview/tst_qqmlpreview.cpp @@ -322,7 +322,7 @@ void tst_QQmlPreview::zoom() for (auto testZoomFactor : {2.0f, 1.5f, 0.5f}) { m_client->triggerZoom(testZoomFactor); - verifyZoomFactor(m_process, baseZoomFactor * testZoomFactor); + verifyZoomFactor(m_process, testZoomFactor); } m_client->triggerZoom(-1.0f); diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 0e0d70845b..4c04afe886 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -712,8 +712,6 @@ language/statements/generators/yield-identifier-non-strict.js sloppyFails language/statements/let/block-local-closure-set-before-initialization.js fails language/statements/let/function-local-closure-set-before-initialization.js fails language/statements/let/global-closure-set-before-initialization.js fails -language/statements/throw/S12.13_A2_T6.js strictFails -language/statements/try/S12.14_A18_T6.js strictFails language/statements/try/scope-catch-block-lex-open.js fails language/statements/variable/binding-resolution.js sloppyFails language/statements/with/unscopables-inc-dec.js sloppyFails diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.qm b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.qm Binary files differnew file mode 100644 index 0000000000..8e3c4967c2 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.qm diff --git a/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.ts b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.ts new file mode 100644 index 0000000000..51a204be3e --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/i18n/qml.ts @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS> +<TS version="2.1" language="" sourcelanguage="en"> +<context> + <name>loadTranslation</name> + <message> + <source>translate it</source> + <translation>translated</translation> + </message> +</context> +</TS> diff --git a/tests/auto/qml/qqmlapplicationengine/data/loadTranslation.qml b/tests/auto/qml/qqmlapplicationengine/data/loadTranslation.qml new file mode 100644 index 0000000000..bba4cab8d6 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/loadTranslation.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property string translation: qsTr('translate it') +} diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index ce654dc45e..a9c28a0911 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -50,6 +50,9 @@ private slots: void application(); void applicationProperties(); void removeObjectsWhenDestroyed(); + void loadTranslation_data(); + void loadTranslation(); + private: QString buildDir; QString srcDir; @@ -241,6 +244,30 @@ void tst_qqmlapplicationengine::removeObjectsWhenDestroyed() QCOMPARE(test->rootObjects().size(), 0); } +void tst_qqmlapplicationengine::loadTranslation_data() +{ + QTest::addColumn<QUrl>("qmlUrl"); + QTest::addColumn<QString>("translation"); + + QTest::newRow("local file") << testFileUrl("loadTranslation.qml") + << QStringLiteral("translated"); + QTest::newRow("qrc") << QUrl(QLatin1String("qrc:///data/loadTranslation.qml")) + << QStringLiteral("translated"); +} + +void tst_qqmlapplicationengine::loadTranslation() +{ + QFETCH(QUrl, qmlUrl); + QFETCH(QString, translation); + + QQmlApplicationEngine test(qmlUrl); + QVERIFY(!test.rootObjects().isEmpty()); + + QObject *rootObject = test.rootObjects().first(); + QVERIFY(rootObject); + + QCOMPARE(rootObject->property("translation").toString(), translation); +} QTEST_MAIN(tst_qqmlapplicationengine) diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro index 18c38a80b6..88d07f2b62 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro @@ -5,6 +5,9 @@ macx:CONFIG -= app_bundle SOURCES += tst_qqmlapplicationengine.cpp TESTDATA += data/* +RESOURCES += tst_qqmlapplicationengine.qrc include (../../shared/util.pri) QT += core-private gui-private qml-private network testlib + +TRANSLATIONS = data/i18n/qml_ja.ts diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.qrc b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.qrc new file mode 100644 index 0000000000..de79d665a3 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>data/loadTranslation.qml</file> + <file>data/i18n/qml.qm</file> + </qresource> +</RCC> diff --git a/tests/auto/qml/qqmlecmascript/data/SingletonLookupTest.qml b/tests/auto/qml/qqmlecmascript/data/SingletonLookupTest.qml new file mode 100644 index 0000000000..3166ab647d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/SingletonLookupTest.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 +import Test.Singletons 1.0 + +QtObject { + property Component singletonAccessor : Component { + QtObject { + property var singletonHolder; + property int result: singletonHolder.testVar + } + } + + property int firstLookup: singletonAccessor.createObject(this, { singletonHolder: CppSingleton1 }).result; + property int secondLookup: singletonAccessor.createObject(this, { singletonHolder: CppSingleton2 }).result; +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 5ae9a6b038..5b73ffbe1d 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -369,6 +369,7 @@ private slots: void intMinDividedByMinusOne(); void undefinedPropertiesInObjectWrapper(); void hugeRegexpQuantifiers(); + void singletonTypeWrapperLookup(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -9007,6 +9008,55 @@ void tst_qqmlecmascript::hugeRegexpQuantifiers() QVERIFY(value.isRegExp()); } +struct CppSingleton1 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int testVar MEMBER testVar CONSTANT) +public: + const int testVar = 0; +}; + +struct CppSingleton2 : public QObject +{ + Q_OBJECT + Q_PROPERTY(int testVar MEMBER testVar CONSTANT) +public: + const int testVar = 1; +}; + +void tst_qqmlecmascript::singletonTypeWrapperLookup() +{ + QQmlEngine engine; + + auto singletonTypeId1 = qmlRegisterSingletonType<CppSingleton1>("Test.Singletons", 1, 0, "CppSingleton1", + [](QQmlEngine *, QJSEngine *) -> QObject * { + return new CppSingleton1; + }); + + auto singletonTypeId2 = qmlRegisterSingletonType<CppSingleton2>("Test.Singletons", 1, 0, "CppSingleton2", + [](QQmlEngine *, QJSEngine *) -> QObject * { + return new CppSingleton2; + }); + + auto cleanup = qScopeGuard([&]() { + QQmlMetaType::unregisterType(singletonTypeId1); + QQmlMetaType::unregisterType(singletonTypeId2); + }); + + QQmlComponent component(&engine, testFileUrl("SingletonLookupTest.qml")); + QScopedPointer<QObject> test(component.create()); + QVERIFY2(!test.isNull(), qPrintable(component.errorString())); + + auto singleton1 = engine.singletonInstance<CppSingleton1*>(singletonTypeId1); + QVERIFY(singleton1); + + auto singleton2 = engine.singletonInstance<CppSingleton2*>(singletonTypeId2); + QVERIFY(singleton2); + + QCOMPARE(test->property("firstLookup").toInt(), singleton1->testVar); + QCOMPARE(test->property("secondLookup").toInt(), singleton2->testVar); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index fead8c4ebc..f16e96a385 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -54,6 +54,7 @@ private slots: void noSubstitutionTemplateLiteral(); void templateLiteral(); void leadingSemicolonInClass(); + void templatedReadonlyProperty(); private: QStringList excludedDirs; @@ -289,6 +290,15 @@ void tst_qqmlparser::leadingSemicolonInClass() QVERIFY(parser.parseProgram()); } +void tst_qqmlparser::templatedReadonlyProperty() +{ + QQmlJS::Engine engine; + QQmlJS::Lexer lexer(&engine); + lexer.setCode(QLatin1String("A { readonly property list<B> listfoo: [ C{} ] }"), 1); + QQmlJS::Parser parser(&engine); + QVERIFY(parser.parse()); +} + QTEST_MAIN(tst_qqmlparser) #include "tst_qqmlparser.moc" diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index a1b2b64ae2..9f7ede44cf 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -1491,11 +1491,11 @@ void tst_QQuickPathView::undefinedPath() // QPainterPath warnings are only received if QT_NO_DEBUG is not defined if (QLibraryInfo::isDebugBuild()) { - QString warning1("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call"); - QTest::ignoreMessage(QtWarningMsg,qPrintable(warning1)); + QRegularExpression warning1("^QPainterPath::moveTo:.*ignoring call$"); + QTest::ignoreMessage(QtWarningMsg, warning1); - QString warning2("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call"); - QTest::ignoreMessage(QtWarningMsg,qPrintable(warning2)); + QRegularExpression warning2("^QPainterPath::lineTo:.*ignoring call$"); + QTest::ignoreMessage(QtWarningMsg, warning2); } QQmlComponent c(&engine, testFileUrl("undefinedpath.qml")); diff --git a/tests/auto/quick/scenegraph/tst_scenegraph.cpp b/tests/auto/quick/scenegraph/tst_scenegraph.cpp index 063358c795..b4abdd6fe2 100644 --- a/tests/auto/quick/scenegraph/tst_scenegraph.cpp +++ b/tests/auto/quick/scenegraph/tst_scenegraph.cpp @@ -564,7 +564,10 @@ bool tst_SceneGraph::isRunningOnOpenGL() bool retval = false; QQuickView dummy; dummy.show(); - QTest::qWaitForWindowExposed(&dummy); + if (!QTest::qWaitForWindowExposed(&dummy)) { + [](){ QFAIL("Could not show a QQuickView"); }(); + return false; + } if (dummy.rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL) retval = true; dummy.hide(); diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index fd5c3653ad..691dfd1bc6 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -449,9 +449,6 @@ void tst_qquickwidget::reparentToNewWindow() qqw->setParent(&window2); qqw->show(); - if (QGuiApplication::platformName() == QLatin1String("offscreen")) - QEXPECT_FAIL("", "afterRendering not emitted after reparenting on offscreen", Continue); - QTRY_VERIFY(afterRenderingSpy.size() > 0); QImage img = qqw->grabFramebuffer(); |