diff options
116 files changed, 3915 insertions, 1757 deletions
diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp index 54a0a3d402..da5a525ff0 100644 --- a/src/imports/shapes/qquickshape.cpp +++ b/src/imports/shapes/qquickshape.cpp @@ -142,6 +142,8 @@ QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams() QQuickShapePathPrivate::QQuickShapePathPrivate() : dirty(DirtyAll) { + // Set this QQuickPath to be a ShapePath + isShapePath = true; } QQuickShapePath::QQuickShapePath(QObject *parent) diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp index aaf32f6d19..3c3142cce8 100644 --- a/src/imports/statemachine/signaltransition.cpp +++ b/src/imports/statemachine/signaltransition.cpp @@ -109,7 +109,7 @@ void SignalTransition::setSignal(const QJSValue &signal) m_signal = signal; - QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(QQmlEngine::contextForObject(this)->engine()); + QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle(); QV4::Scope scope(jsEngine); QObject *sender; @@ -169,7 +169,7 @@ void SignalTransition::connectTriggered() const QV4::CompiledData::Binding *binding = m_bindings.at(0); Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script); - QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(QQmlEngine::contextForObject(this)->engine()); + QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle(); QV4::Scope scope(jsEngine); QV4::Scoped<QV4::QObjectMethod> qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal)); Q_ASSERT(qobjectSignal); diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 00e9592557..ab84d83ff7 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -103,7 +103,7 @@ public Q_SLOTS: } QQmlEngine *engine = qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *v4 = engine->handle(); QV4::Scope scope(v4); QV4::ScopedValue s(scope, v4->newString(name)); return QQmlV4Handle(s); @@ -116,7 +116,7 @@ public Q_SLOTS: QQmlV4Handle callerFile(int frameIndex = 0) const { QQmlEngine *engine = qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *v4 = engine->handle(); QV4::Scope scope(v4); QVector<QV4::StackFrame> stack = v4->stackTrace(frameIndex + 2); @@ -129,7 +129,7 @@ public Q_SLOTS: int callerLine(int frameIndex = 0) const { QQmlEngine *engine = qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *v4 = engine->handle(); QVector<QV4::StackFrame> stack = v4->stackTrace(frameIndex + 2); if (stack.size() > frameIndex + 1) diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp index a013e8cf69..d14810a01b 100644 --- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp +++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp @@ -923,8 +923,7 @@ QQmlV4Handle QQuickXmlListModel::get(int index) const return QQmlV4Handle(Encode::undefined()); QQmlEngine *engine = qmlContext(this)->engine(); - QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); - ExecutionEngine *v4engine = QV8Engine::getV4(v8engine); + ExecutionEngine *v4engine = engine->handle(); Scope scope(v4engine); Scoped<Object> o(scope, v4engine->newObject()); ScopedString name(scope); diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp index e152c436db..53557e1d0b 100644 --- a/src/particles/qquickcustomaffector.cpp +++ b/src/particles/qquickcustomaffector.cpp @@ -148,7 +148,7 @@ void QQuickCustomAffector::affectSystem(qreal dt) dt = 1.0; QQmlEngine *qmlEngine = ::qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine->handle()); + QV4::ExecutionEngine *v4 = qmlEngine->handle(); QV4::Scope scope(v4); QV4::ScopedArrayObject array(scope, v4->newArrayObject(toAffect.size())); diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp index d4e6552d95..d17c8fc2ba 100644 --- a/src/particles/qquickparticleemitter.cpp +++ b/src/particles/qquickparticleemitter.cpp @@ -486,7 +486,7 @@ void QQuickParticleEmitter::emitWindow(int timeStamp) if (isEmitConnected()) { QQmlEngine *qmlEngine = ::qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine->handle()); + QV4::ExecutionEngine *v4 = qmlEngine->handle(); QV4::Scope scope(v4); //Done after emitParticle so that the Painter::load is done first, this allows you to customize its static variables diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index cc7d9edbc8..5e613c484a 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -526,7 +526,7 @@ void QQuickParticleData::clone(const QQuickParticleData& other) QQmlV4Handle QQuickParticleData::v4Value(QQuickParticleSystem* particleSystem) { if (!v8Datum) - v8Datum = new QQuickV4ParticleData(QQmlEnginePrivate::getV8Engine(qmlEngine(particleSystem)), this, particleSystem); + v8Datum = new QQuickV4ParticleData(qmlEngine(particleSystem)->handle(), this, particleSystem); return v8Datum->v4Value(); } diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp index 49ee728bfd..16b87f0e51 100644 --- a/src/particles/qquicktrailemitter.cpp +++ b/src/particles/qquicktrailemitter.cpp @@ -267,7 +267,7 @@ void QQuickTrailEmitter::emitWindow(int timeStamp) if (isEmitConnected() || isEmitFollowConnected()) { QQmlEngine *qmlEngine = ::qmlEngine(this); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine->handle()); + QV4::ExecutionEngine *v4 = qmlEngine->handle(); QV4::Scope scope(v4); QV4::ScopedArrayObject array(scope, v4->newArrayObject(toEmit.size())); diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp index 52acd3d371..fb674e1b64 100644 --- a/src/particles/qquickv4particledata.cpp +++ b/src/particles/qquickv4particledata.cpp @@ -510,12 +510,12 @@ QV4ParticleDataDeletable::~QV4ParticleDataDeletable() V4_DEFINE_EXTENSION(QV4ParticleDataDeletable, particleV4Data); -QQuickV4ParticleData::QQuickV4ParticleData(QV8Engine* engine, QQuickParticleData* datum, QQuickParticleSystem *system) +QQuickV4ParticleData::QQuickV4ParticleData(QV4::ExecutionEngine* v4, QQuickParticleData* datum, + QQuickParticleSystem *system) { - if (!engine || !datum) + if (!v4 || !datum) return; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope scope(v4); QV4ParticleDataDeletable *d = particleV4Data(scope.engine); QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QV4ParticleData>(datum, system)); diff --git a/src/particles/qquickv4particledata_p.h b/src/particles/qquickv4particledata_p.h index d73e3b644d..3d682ab297 100644 --- a/src/particles/qquickv4particledata_p.h +++ b/src/particles/qquickv4particledata_p.h @@ -61,7 +61,7 @@ class QQuickParticleData; class QQuickParticleSystem; class QQuickV4ParticleData { public: - QQuickV4ParticleData(QV8Engine*, QQuickParticleData*, QQuickParticleSystem *system); + QQuickV4ParticleData(QV4::ExecutionEngine*, QQuickParticleData*, QQuickParticleSystem *system); ~QQuickV4ParticleData(); QQmlV4Handle v4Value() const; private: diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 9514204392..80da09d718 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -771,7 +771,7 @@ bool QQmlEngineDebugServiceImpl::setMethodBody(int objectId, const QString &meth QQmlVMEMetaObject *vmeMetaObject = QQmlVMEMetaObject::get(object); Q_ASSERT(vmeMetaObject); // the fact we found the property above should guarentee this - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(object)->handle()); + QV4::ExecutionEngine *v4 = qmlEngine(object)->handle(); QV4::Scope scope(v4); int lineNumber = 0; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index fca811cb28..99a57be5d6 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -702,7 +702,7 @@ void QV4DebugServiceImpl::engineAdded(QJSEngine *engine) { QMutexLocker lock(&m_configMutex); if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *ee = engine->handle(); if (QQmlDebugConnector *server = QQmlDebugConnector::instance()) { if (ee) { QV4Debugger *debugger = new QV4Debugger(ee); @@ -720,7 +720,7 @@ void QV4DebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) { QMutexLocker lock(&m_configMutex); if (engine){ - const QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + const QV4::ExecutionEngine *ee = engine->handle(); if (ee) { QV4Debugger *debugger = qobject_cast<QV4Debugger *>(ee->debugger()); if (debugger) diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index eeedb59ce6..ba33de9714 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -713,7 +713,7 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeAdded(QJSEngine *engine) { TRACE_PROTOCOL("Adding engine" << engine); if (engine) { - QV4::ExecutionEngine *ee = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *ee = engine->handle(); TRACE_PROTOCOL("Adding execution engine" << ee); if (ee) { NativeDebugger *debugger = new NativeDebugger(this, ee); @@ -729,7 +729,7 @@ void QQmlNativeDebugServiceImpl::engineAboutToBeRemoved(QJSEngine *engine) { TRACE_PROTOCOL("Removing engine" << engine); if (engine) { - QV4::ExecutionEngine *executionEngine = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *executionEngine = engine->handle(); const auto debuggersCopy = m_debuggers; for (NativeDebugger *debugger : debuggersCopy) { if (debugger->engine() == executionEngine) diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp index a2bc4c09a3..1c4daba6af 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp @@ -126,7 +126,7 @@ void QQmlProfilerServiceImpl::engineAboutToBeAdded(QJSEngine *engine) = new QQmlProfilerAdapter(this, &(enginePrivate->typeLoader)); addEngineProfiler(compileAdapter, engine); } - QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, QV8Engine::getV4(engine->handle())); + QV4ProfilerAdapter *v4Adapter = new QV4ProfilerAdapter(this, engine->handle()); addEngineProfiler(v4Adapter, engine); QQmlConfigurableDebugService<QQmlProfilerService>::engineAboutToBeAdded(engine); } diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 061edb3dcf..df1f00f2f7 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -296,7 +296,7 @@ SignalHandlerConverter::SignalHandlerConverter(QQmlTypeCompiler *typeCompiler) , imports(typeCompiler->imports()) , customParsers(typeCompiler->customParserCache()) , resolvedTypes(typeCompiler->resolvedTypes) - , illegalNames(QV8Engine::get(QQmlEnginePrivate::get(typeCompiler->enginePrivate()))->illegalNames()) + , illegalNames(typeCompiler->enginePrivate()->v8engine()->illegalNames()) , propertyCaches(typeCompiler->propertyCaches()) { } diff --git a/src/qml/debugger/qqmlprofilerdefinitions_p.h b/src/qml/debugger/qqmlprofilerdefinitions_p.h index 91d0376837..f84a2c44e2 100644 --- a/src/qml/debugger/qqmlprofilerdefinitions_p.h +++ b/src/qml/debugger/qqmlprofilerdefinitions_p.h @@ -69,6 +69,7 @@ struct QQmlProfilerDefinitions { PixmapCacheEvent, SceneGraphFrame, MemoryAllocation, + DebugMessage, MaximumMessage }; @@ -161,6 +162,26 @@ struct QQmlProfilerDefinitions { MaximumInputEventType }; + + static ProfileFeature featureFromRangeType(RangeType range) + { + switch (range) { + case Painting: + return ProfilePainting; + case Compiling: + return ProfileCompiling; + case Creating: + return ProfileCreating; + case Binding: + return ProfileBinding; + case HandlingSignal: + return ProfileHandlingSignal; + case Javascript: + return ProfileJavaScript; + default: + return MaximumProfileFeature; + } + } }; QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index fffb190513..020a83f837 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -290,8 +290,9 @@ QJSEngine::QJSEngine() QJSEngine::QJSEngine(QObject *parent) : QObject(*new QJSEnginePrivate, parent) - , d(new QV8Engine(this)) + , m_v4Engine(new QV4::ExecutionEngine) { + m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); checkForApplicationInstance(); QJSEnginePrivate::addToDebugServer(this); @@ -302,8 +303,9 @@ QJSEngine::QJSEngine(QObject *parent) */ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) : QObject(dd, parent) - , d(new QV8Engine(this)) + , m_v4Engine(new QV4::ExecutionEngine) { + m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); checkForApplicationInstance(); } @@ -317,11 +319,12 @@ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) QJSEngine::~QJSEngine() { QJSEnginePrivate::removeFromDebugServer(this); - delete d; + delete m_v4Engine->v8Engine; + delete m_v4Engine; } /*! - \fn QV8Engine *QJSEngine::handle() const + \fn QV4::ExecutionEngine *QJSEngine::handle() const \internal */ @@ -338,7 +341,7 @@ QJSEngine::~QJSEngine() */ void QJSEngine::collectGarbage() { - d->m_v4Engine->memoryManager->runGC(); + m_v4Engine->memoryManager->runGC(); } #if QT_DEPRECATED_SINCE(5, 6) @@ -395,12 +398,12 @@ void QJSEngine::installTranslatorFunctions(const QJSValue &object) void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSValue &object) { QV4::ExecutionEngine *otherEngine = QJSValuePrivate::engine(&object); - if (otherEngine && otherEngine != d->m_v4Engine) { + if (otherEngine && otherEngine != m_v4Engine) { qWarning("QJSEngine: Trying to install extensions from a different engine"); return; } - QV4::Scope scope(d->m_v4Engine); + QV4::Scope scope(m_v4Engine); QV4::ScopedObject obj(scope); QV4::Value *val = QJSValuePrivate::getValue(&object); if (val) @@ -441,7 +444,7 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal */ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { - QV4::ExecutionEngine *v4 = d->m_v4Engine; + QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); QV4::ScopedValue result(scope); @@ -473,9 +476,9 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in */ QJSValue QJSEngine::newObject() { - QV4::Scope scope(d->m_v4Engine); - QV4::ScopedValue v(scope, d->m_v4Engine->newObject()); - return QJSValue(d->m_v4Engine, v->asReturnedValue()); + QV4::Scope scope(m_v4Engine); + QV4::ScopedValue v(scope, m_v4Engine->newObject()); + return QJSValue(m_v4Engine, v->asReturnedValue()); } /*! @@ -485,12 +488,12 @@ QJSValue QJSEngine::newObject() */ QJSValue QJSEngine::newArray(uint length) { - QV4::Scope scope(d->m_v4Engine); - QV4::ScopedArrayObject array(scope, d->m_v4Engine->newArrayObject()); + QV4::Scope scope(m_v4Engine); + QV4::ScopedArrayObject array(scope, m_v4Engine->newArrayObject()); if (length < 0x1000) array->arrayReserve(length); array->setArrayLengthUnchecked(length); - return QJSValue(d->m_v4Engine, array.asReturnedValue()); + return QJSValue(m_v4Engine, array.asReturnedValue()); } /*! @@ -516,7 +519,7 @@ QJSValue QJSEngine::newArray(uint length) QJSValue QJSEngine::newQObject(QObject *object) { Q_D(QJSEngine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(d); + QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); if (object) { QQmlData *ddata = QQmlData::get(object, true); @@ -543,7 +546,7 @@ QJSValue QJSEngine::newQObject(QObject *object) QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { Q_D(QJSEngine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(d); + QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); return QJSValue(v4, v->asReturnedValue()); @@ -571,10 +574,9 @@ QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { */ QJSValue QJSEngine::globalObject() const { - Q_D(const QJSEngine); - QV4::Scope scope(d->m_v4Engine); - QV4::ScopedValue v(scope, d->m_v4Engine->globalObject); - return QJSValue(d->m_v4Engine, v->asReturnedValue()); + QV4::Scope scope(m_v4Engine); + QV4::ScopedValue v(scope, m_v4Engine->globalObject); + return QJSValue(m_v4Engine, v->asReturnedValue()); } /*! @@ -583,10 +585,9 @@ QJSValue QJSEngine::globalObject() const */ QJSValue QJSEngine::create(int type, const void *ptr) { - Q_D(QJSEngine); - QV4::Scope scope(d->m_v4Engine); + QV4::Scope scope(m_v4Engine); QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, ptr)); - return QJSValue(d->m_v4Engine, v->asReturnedValue()); + return QJSValue(m_v4Engine, v->asReturnedValue()); } /*! @@ -726,7 +727,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e) { - return e->v8Engine->publicEngine()->d_func(); + return e->jsEngine()->d_func(); } QJSEnginePrivate::~QJSEnginePrivate() diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 913757107f..89642b6f20 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -52,8 +52,6 @@ QT_BEGIN_NAMESPACE -class QV8Engine; - template <typename T> inline T qjsvalue_cast(const QJSValue &); @@ -111,7 +109,7 @@ public: void installExtensions(Extensions extensions, const QJSValue &object = QJSValue()); - QV8Engine *handle() const { return d; } + QV4::ExecutionEngine *handle() const { return m_v4Engine; } private: QJSValue create(int type, const void *ptr); @@ -124,10 +122,9 @@ protected: QJSEngine(QJSEnginePrivate &dd, QObject *parent = nullptr); private: - QV8Engine *d; + QV4::ExecutionEngine *m_v4Engine; Q_DISABLE_COPY(QJSEngine) Q_DECLARE_PRIVATE(QJSEngine) - friend class QV8Engine; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QJSEngine::Extensions) diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index b9c06d6569..259df976c5 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1554,6 +1554,11 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } +ReturnedValue ExecutionEngine::global() +{ + return globalObject->asReturnedValue(); +} + // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 44f57299bb..c048f5f68d 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -494,6 +494,8 @@ public: #endif } + QV4::ReturnedValue global(); + private: #if QT_CONFIG(qml_debug) QScopedPointer<QV4::Debugging::Debugger> m_debugger; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 11a1e6edee..a968aa908d 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -84,7 +84,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr b->QQmlJavaScriptExpression::setContext(QQmlContextData::get(ctxt ? ctxt : scriptPrivate->context)); b->setScopeObject(obj ? obj : scriptPrivate->scope); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::get(b->context()->engine)->v4engine(); + QV4::ExecutionEngine *v4 = b->context()->engine->handle(); if (runtimeFunction) { QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxtdata, b->scopeObject())); @@ -158,13 +158,13 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) DeleteWatcher watcher(this); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); - QV4::Scope scope(ep->v4engine()); + QQmlEngine *engine = context()->engine; + QV4::Scope scope(engine->handle()); if (canUseAccessor()) flags.setFlag(QQmlPropertyData::BypassInterceptor); - QQmlBindingProfiler prof(ep->profiler, function()); + QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function()); doUpdate(watcher, flags, scope); if (!watcher.wasDeleted()) @@ -351,7 +351,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, bool isUndefined, QQmlPropertyData::WriteFlags flags) { QQmlEngine *engine = context()->engine; - QV8Engine *v8engine = QQmlEnginePrivate::getV8Engine(engine); + QV4::ExecutionEngine *v4engine = engine->handle(); int type = valueTypeData.isValid() ? valueTypeData.propType() : core.propType(); @@ -362,13 +362,13 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, if (isUndefined) { } else if (core.isQList()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QObject *> >()); + value = v4engine->toVariant(result, qMetaTypeId<QList<QObject *> >()); } else if (result.isNull() && core.isQObject()) { value = QVariant::fromValue((QObject *)0); } else if (core.propType() == qMetaTypeId<QList<QUrl> >()) { - value = QQmlPropertyPrivate::resolvedUrlSequence(QV8Engine::getV4(v8engine)->toVariant(result, qMetaTypeId<QList<QUrl> >()), context()); + value = QQmlPropertyPrivate::resolvedUrlSequence(v4engine->toVariant(result, qMetaTypeId<QList<QUrl> >()), context()); } else if (!isVarProperty && type != qMetaTypeId<QJSValue>()) { - value = QV8Engine::getV4(v8engine)->toVariant(result, type); + value = v4engine->toVariant(result, type); } if (hasError()) { @@ -397,7 +397,7 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, return false; } QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue( - QJSValue(QV8Engine::getV4(v8engine), result.asReturnedValue())), + QJSValue(v4engine, result.asReturnedValue())), context(), flags); } else if (isUndefined) { const QLatin1String typeName(QMetaType::typeName(type) @@ -455,12 +455,13 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, QVariant QQmlBinding::evaluate() { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context()->engine); + QQmlEngine *engine = context()->engine; + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); ep->referenceScarceResources(); bool isUndefined = false; - QV4::Scope scope(ep->v4engine()); + QV4::Scope scope(engine->handle()); QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined)); ep->dereferenceScarceResources(); diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 1d7a37fc99..501184b630 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -74,8 +74,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, { init(ctxt, scope); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); - QV4::ExecutionEngine *v4 = ep->v4engine(); + QV4::ExecutionEngine *v4 = engine()->handle(); QString function; @@ -123,7 +122,7 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, // It's important to call init first, because m_index gets remapped in case of cloned signals. init(ctxt, scope); - QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); + QV4::ExecutionEngine *engine = ctxt->engine->handle(); QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames(); if (!signalParameters.isEmpty()) { @@ -182,8 +181,10 @@ void QQmlBoundSignalExpression::evaluate(void **a) if (!expressionFunctionValid()) return; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); - QV4::Scope scope(ep->v4engine()); + QQmlEngine *qmlengine = engine(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlengine); + QV4::ExecutionEngine *v4 = qmlengine->handle(); + QV4::Scope scope(v4); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. @@ -215,7 +216,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) if (!*reinterpret_cast<void* const *>(a[ii + 1])) jsCall->args[ii] = QV4::Primitive::nullValue(); else - jsCall->args[ii] = QV4::QObjectWrapper::wrap(ep->v4engine(), *reinterpret_cast<QObject* const *>(a[ii + 1])); + jsCall->args[ii] = QV4::QObjectWrapper::wrap(v4, *reinterpret_cast<QObject* const *>(a[ii + 1])); } else { jsCall->args[ii] = scope.engine->fromVariant(QVariant(type, a[ii + 1])); } @@ -233,8 +234,9 @@ void QQmlBoundSignalExpression::evaluate(const QList<QVariant> &args) if (!expressionFunctionValid()) return; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine()); - QV4::Scope scope(ep->v4engine()); + QQmlEngine *qmlengine = engine(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlengine); + QV4::Scope scope(qmlengine->handle()); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 97e2019ef1..481507946d 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1441,8 +1441,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) // XXX used by QSGLoader void QQmlComponentPrivate::initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - QV4::ExecutionEngine *v4engine = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4engine = engine->handle(); QV4::Scope scope(v4engine); QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(v4engine, toCreate)); diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp index 32b25344ae..e6e2be91f6 100644 --- a/src/qml/qml/qqmlcontext.cpp +++ b/src/qml/qml/qqmlcontext.cpp @@ -837,7 +837,7 @@ const QV4::IdentifierHash &QQmlContextData::propertyNames() const if (typeCompilationUnit) propertyNameCache = typeCompilationUnit->namedObjectsPerComponent(componentObjectIndex); else - propertyNameCache = QV4::IdentifierHash(QV8Engine::getV4(engine)); + propertyNameCache = QV4::IdentifierHash(engine->handle()); } return propertyNameCache; } diff --git a/src/qml/qml/qqmldelayedcallqueue_p.h b/src/qml/qml/qqmldelayedcallqueue_p.h index b3d361581d..47e211829c 100644 --- a/src/qml/qml/qqmldelayedcallqueue_p.h +++ b/src/qml/qml/qqmldelayedcallqueue_p.h @@ -60,7 +60,6 @@ QT_BEGIN_NAMESPACE -class QV8Engine; class QQmlDelayedCallQueue : public QObject { Q_OBJECT diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4bc3c7d141..80ec44e1e4 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2072,7 +2072,7 @@ void QQmlEnginePrivate::cleanupScarceResources() // note that the actual SRD is owned by the JS engine, // so we cannot delete the SRD; but we can free the // memory used by the variant in the SRD. - QV4::ExecutionEngine *engine = QV8Engine::getV4(v8engine()); + QV4::ExecutionEngine *engine = v4engine(); while (QV4::ExecutionEngine::ScarceResourceData *sr = engine->scarceResources.first()) { sr->data = QVariant(); engine->scarceResources.remove(sr); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 74e232fd84..f12409ed9a 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -150,8 +150,8 @@ public: QQmlDelayedError *erroredBindings; int inProgressCreations; - QV8Engine *v8engine() const { return q_func()->handle(); } - QV4::ExecutionEngine *v4engine() const { return QV8Engine::getV4(q_func()->handle()); } + QV8Engine *v8engine() const { return q_func()->handle()->v8Engine; } + QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); } QQuickWorkerScriptEngine *getWorkerScriptEngine(); QQuickWorkerScriptEngine *workerScriptEngine; @@ -295,7 +295,7 @@ inline void QQmlEnginePrivate::dereferenceScarceResources() // expression must have completed. We can safely release the // scarce resources. if (Q_LIKELY(scarceResourcesRefCount == 0)) { - QV4::ExecutionEngine *engine = QV8Engine::getV4(v8engine()); + QV4::ExecutionEngine *engine = v4engine(); if (Q_UNLIKELY(!engine->scarceResources.isEmpty())) { cleanupScarceResources(); } @@ -385,14 +385,14 @@ QV8Engine *QQmlEnginePrivate::getV8Engine(QQmlEngine *e) { Q_ASSERT(e); - return e->d_func()->v8engine(); + return e->handle()->v8Engine; } QV4::ExecutionEngine *QQmlEnginePrivate::getV4Engine(QQmlEngine *e) { Q_ASSERT(e); - return e->d_func()->v4engine(); + return e->handle(); } QQmlEnginePrivate *QQmlEnginePrivate::get(QQmlEngine *e) @@ -428,9 +428,7 @@ QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) { - if (!e->v8Engine) - return 0; - QQmlEngine *qmlEngine = e->v8Engine->engine(); + QQmlEngine *qmlEngine = e->qmlEngine(); if (!qmlEngine) return 0; return get(qmlEngine); diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 35dbaccbbe..3ba0afc7bd 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -74,7 +74,7 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, const QString &expr, QOb void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFunction, QObject *me) { expressionFunctionValid = true; - QV4::ExecutionEngine *engine = QQmlEnginePrivate::getV4Engine(ctxt->engine); + QV4::ExecutionEngine *engine = ctxt->engine->handle(); QV4::Scope scope(engine); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(engine->rootContext(), ctxt, me)); setupFunction(qmlContext, runtimeFunction); @@ -266,13 +266,14 @@ QVariant QQmlExpressionPrivate::value(bool *isUndefined) return QVariant(); } - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(q->engine()); + QQmlEngine *engine = q->engine(); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); QVariant rv; ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. { - QV4::Scope scope(QV8Engine::getV4(ep->v8engine())); + QV4::Scope scope(engine->handle()); QV4::ScopedValue result(scope, v4value(isUndefined)); if (!hasError()) rv = scope.engine->toVariant(result, -1); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 006611e089..cd35ab93c6 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -183,7 +183,7 @@ void QQmlJavaScriptExpression::refresh() QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(bool *isUndefined) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_context->engine); + QV4::ExecutionEngine *v4 = m_context->engine->handle(); QV4::Scope scope(v4); QV4::JSCallData jsCall(scope); @@ -217,7 +217,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b if (notifyOnValueChanged()) capture.guards.copyAndClearPrepend(activeGuards); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4 = m_context->engine->handle(); callData->thisObject = v4->globalObject; if (scopeObject()) { QV4::ReturnedValue scope = QV4::QObjectWrapper::wrap(v4, scopeObject()); @@ -414,7 +414,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4 = engine->handle(); QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, scopeObject)); @@ -444,7 +444,7 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * QQmlEngine *engine = ctxt->engine; QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4 = engine->handle(); QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, qmlScope)); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 40864de366..b324386bf5 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1579,15 +1579,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da if (uri && !typeName.isEmpty()) { QString nameSpace = QString::fromUtf8(uri); - if (!data->typeRegistrationNamespace.isEmpty()) { - // We can only install types into the registered namespace - if (nameSpace != data->typeRegistrationNamespace) { - QString failure(QCoreApplication::translate("qmlRegisterType", - "Cannot install %1 '%2' into unregistered namespace '%3'")); - data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName).arg(nameSpace)); - return false; - } - } else if (data->typeRegistrationNamespace != nameSpace) { + if (data->typeRegistrationNamespace.isEmpty() && !nameSpace.isEmpty()) { // Is the target namespace protected against further registrations? if (data->protectedNamespaces.contains(nameSpace)) { QString failure(QCoreApplication::translate("qmlRegisterType", diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 7616c5e38b..f9aba69986 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -114,7 +114,7 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext) { parentContext = providedParentContext; engine = parentContext->engine; - v4 = QV8Engine::getV4(engine); + v4 = engine->handle(); if (compilationUnit && !compilationUnit->engine) compilationUnit->linkToEngine(v4); @@ -1025,7 +1025,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *property, con } else if (property->propType() == QMetaType::QVariant) { if (property->isVarProperty()) { QV4::Scope scope(v4); - QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), createdSubObject)); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(engine->handle(), createdSubObject)); _vmeMetaObject->setVMEProperty(property->coreIndex(), wrappedObject); } else { QVariant value = QVariant::fromValue(createdSubObject); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index c05126817e..0be83f6ba6 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1535,7 +1535,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob) bool QQmlTypeLoader::Blob::isDebugging() const { - return QV8Engine::getV4(typeLoader()->engine())->debugger() != 0; + return typeLoader()->engine()->handle()->debugger() != 0; } bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors) @@ -2108,7 +2108,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() if (isDebugging()) return false; - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine()); + QV4::ExecutionEngine *v4 = typeLoader()->engine()->handle(); if (!v4) return false; @@ -2452,7 +2452,7 @@ bool QQmlTypeData::loadFromSource() m_document.reset(new QmlIR::Document(isDebugging())); m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); QQmlEngine *qmlEngine = typeLoader()->engine(); - QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); + QmlIR::IRBuilder compiler(qmlEngine->handle()->v8Engine->illegalNames()); QString sourceError; const QString source = m_backupSourceCode.readAll(&sourceError); @@ -2841,9 +2841,7 @@ void QQmlScriptData::initialize(QQmlEngine *engine) Q_ASSERT(engine); Q_ASSERT(!hasEngine()); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); - QV8Engine *v8engine = ep->v8engine(); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(v8engine); + QV4::ExecutionEngine *v4 = engine->handle(); m_program = new QV4::Script(v4, 0, m_precompiledScript); @@ -2859,7 +2857,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent Q_ASSERT(parentCtxt && parentCtxt->engine); QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(parentCtxt->engine); + QV4::ExecutionEngine *v4 = parentCtxt->engine->handle(); QV4::Scope scope(v4); bool shared = m_precompiledScript->data->flags & QV4::CompiledData::Unit::IsSharedLibrary; diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 281d64ac79..73cb20dc7f 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -926,12 +926,14 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * id -= plainSignals; if (id < methodCount) { - if (!ctxt->engine) + QQmlEngine *engine = ctxt->engine; + if (!engine) return -1; // We can't run the method - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine); + QV4::ExecutionEngine *v4 = engine->handle(); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. - QV4::Scope scope(ep->v4engine()); + QV4::Scope scope(v4); QV4::ScopedFunctionObject function(scope, method(id)); @@ -951,7 +953,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * const unsigned int parameterCount = function->formalParameterCount(); QV4::JSCallData jsCallData(scope, parameterCount); - *jsCallData->thisObject = ep->v8engine()->global(); + *jsCallData->thisObject = v4->global(); for (uint ii = 0; ii < parameterCount; ++ii) jsCallData->args[ii] = scope.engine->fromVariant(*(QVariant *)a[ii + 1]); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index f2b396ad00..44ad174f37 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1116,8 +1116,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va } }; - QV8Engine *v8engine = scope.engine->v8Engine; - QQmlEngine *engine = v8engine->engine(); + QQmlEngine *engine = scope.engine->qmlEngine(); QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); @@ -1244,8 +1243,7 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va if (argc < 1 || argc > 3) THROW_GENERIC_ERROR("Qt.createComponent(): Invalid arguments"); - QV8Engine *v8engine = scope.engine->v8Engine; - QQmlEngine *engine = v8engine->engine(); + QQmlEngine *engine = scope.engine->qmlEngine(); QQmlContextData *context = scope.engine->callingQmlContext(); Q_ASSERT(context); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 2947f7870a..2bf623f144 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -123,9 +123,10 @@ static void restoreJSValue(QDataStream &stream, void *data) } } -QV8Engine::QV8Engine(QJSEngine* qq) +QV8Engine::QV8Engine(QJSEngine *qq, QV4::ExecutionEngine *v4) : q(qq) , m_engine(0) + , m_v4Engine(v4) , m_xmlHttpRequestData(0) { #ifdef Q_PROCESSOR_X86_32 @@ -146,8 +147,6 @@ QV8Engine::QV8Engine(QJSEngine* qq) QMetaType::registerConverter<QJSValue, QStringList>(convertJSValueToVariantType<QStringList>); QMetaType::registerStreamOperators(qMetaTypeId<QJSValue>(), saveJSValue, restoreJSValue); - m_v4Engine = new QV4::ExecutionEngine; - m_v4Engine->v8Engine = this; m_delayedCallQueue.init(m_v4Engine); QV4::QObjectWrapper::initializeBindings(m_v4Engine); @@ -162,8 +161,6 @@ QV8Engine::~QV8Engine() qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); m_xmlHttpRequestData = 0; #endif - - delete m_v4Engine; } #if QT_CONFIG(qml_network) @@ -288,11 +285,6 @@ void QV8Engine::setEngine(QQmlEngine *engine) initQmlGlobalObject(); } -QV4::ReturnedValue QV8Engine::global() -{ - return m_v4Engine->globalObject->asReturnedValue(); -} - void QV8Engine::startTimer(const QString &timerName) { if (!m_time.isValid()) diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 98b182147c..eb2b8b474c 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -153,12 +153,11 @@ class Q_QML_PRIVATE_EXPORT QV8Engine { friend class QJSEngine; public: - static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } // static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } - static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle()->m_v4Engine; } + static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle(); } static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; } - QV8Engine(QJSEngine* qq); + QV8Engine(QJSEngine* qq, QV4::ExecutionEngine *v4); virtual ~QV8Engine(); // This enum should be in sync with QQmlEngine::ObjectOwnership @@ -172,7 +171,6 @@ public: void setEngine(QQmlEngine *engine); QQmlEngine *engine() { return m_engine; } QJSEngine *publicEngine() { return q; } - QV4::ReturnedValue global(); QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 4e4a9353cb..ca19d68948 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -128,7 +128,8 @@ public: QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4); ~QQmlDelegateModelEngineData(); - QV4::ReturnedValue array(QV8Engine *engine, const QVector<QQmlChangeSet::Change> &changes); + QV4::ReturnedValue array(QV4::ExecutionEngine *engine, + const QVector<QQmlChangeSet::Change> &changes); QV4::PersistentValue changeProto; }; @@ -325,7 +326,7 @@ void QQmlDelegateModel::componentComplete() } d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); + d->m_context->engine()->handle(), this, groupNames); d->m_compositor.setGroupCount(d->m_groupCount); d->m_compositor.setDefaultGroups(defaultGroups); @@ -1481,7 +1482,7 @@ void QQmlDelegateModelPrivate::emitChanges() return; m_transaction = true; - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); + QV4::ExecutionEngine *engine = m_context->engine()->handle(); for (int i = 1; i < m_groupCount; ++i) QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); m_transaction = false; @@ -1676,10 +1677,10 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const //============================================================================ QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) + QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames) : model(model) , groupCount(groupNames.count() + 1) - , v8Engine(engine) + , v4Engine(engine) , metaObject(0) , groupNames(groupNames) { @@ -1720,37 +1721,36 @@ void QQmlDelegateModelItemMetaType::initializeMetaObject() void QQmlDelegateModelItemMetaType::initializePrototype() { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(v8Engine); - QV4::Scope scope(v4); + QV4::Scope scope(v4Engine); - QV4::ScopedObject proto(scope, v4->newObject()); + QV4::ScopedObject proto(scope, v4Engine->newObject()); proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, 0); proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups); QV4::ScopedString s(scope); QV4::ScopedProperty p(scope); - s = v4->newString(QStringLiteral("isUnresolved")); + s = v4Engine->newString(QStringLiteral("isUnresolved")); QV4::ScopedFunctionObject f(scope); QV4::ExecutionContext *global = scope.engine->rootContext(); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - s = v4->newString(QStringLiteral("inItems")); + s = v4Engine->newString(QStringLiteral("inItems")); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - s = v4->newString(QStringLiteral("inPersistedItems")); + s = v4Engine->newString(QStringLiteral("inPersistedItems")); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - s = v4->newString(QStringLiteral("itemsIndex")); + s = v4Engine->newString(QStringLiteral("itemsIndex")); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - s = v4->newString(QStringLiteral("persistedItemsIndex")); + s = v4Engine->newString(QStringLiteral("persistedItemsIndex")); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); @@ -1758,19 +1758,19 @@ void QQmlDelegateModelItemMetaType::initializePrototype() for (int i = 2; i < groupNames.count(); ++i) { QString propertyName = QLatin1String("in") + groupNames.at(i); propertyName.replace(2, 1, propertyName.at(2).toUpper()); - s = v4->newString(propertyName); + s = v4Engine->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::set_member))); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } for (int i = 2; i < groupNames.count(); ++i) { const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - s = v4->newString(propertyName); + s = v4Engine->newString(propertyName); p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); p->setSetter(0); proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); } - modelItemProto.set(v4, proto); + modelItemProto.set(v4Engine, proto); } int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const @@ -1787,7 +1787,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const { int groupFlags = 0; - QV4::Scope scope(QV8Engine::getV4(v8Engine)); + QV4::Scope scope(v4Engine); QV4::ScopedString s(scope, groups); if (s) { @@ -1907,7 +1907,7 @@ void QV4::Heap::QQmlDelegateModelItemObject::destroy() QQmlDelegateModelItem::QQmlDelegateModelItem( QQmlDelegateModelItemMetaType *metaType, int modelIndex) - : v4(QV8Engine::getV4(metaType->v8Engine)) + : v4(metaType->v4Engine) , metaType(metaType) , contextData(0) , object(0) @@ -2245,13 +2245,13 @@ bool QQmlDelegateModelGroupPrivate::isChangedConnected() IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &)); } -void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) +void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) { Q_Q(QQmlDelegateModelGroup); if (isChangedConnected() && !changeSet.isEmpty()) { - QV4::Scope scope(QV8Engine::getV4(engine)); - QV4::ScopedValue removed(scope, engineData(scope.engine)->array(engine, changeSet.removes())); - QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(engine, changeSet.inserts())); + QV4::Scope scope(v4); + QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes())); + QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts())); emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted)); } if (changeSet.difference() != 0) @@ -2480,8 +2480,7 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index) if (model->m_cacheMetaType->modelItemProto.isUndefined()) model->m_cacheMetaType->initializePrototype(); - QV8Engine *v8 = model->m_cacheMetaType->v8Engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(v8); + QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; QV4::Scope scope(v4); QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem)); QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); @@ -3347,9 +3346,9 @@ QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() { } -QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV8Engine *engine, const QVector<QQmlChangeSet::Change> &changes) +QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV4::ExecutionEngine *v4, + const QVector<QQmlChangeSet::Change> &changes) { - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Scope scope(v4); QV4::ScopedObject o(scope, QQmlDelegateModelGroupChangeArray::create(v4, changes)); return o.asReturnedValue(); diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 537937df57..7b60bcddc0 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -69,7 +69,7 @@ class QQmlDelegateModelAttachedMetaObject; class QQmlDelegateModelItemMetaType : public QQmlRefCount { public: - QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); ~QQmlDelegateModelItemMetaType(); void initializeMetaObject(); @@ -80,7 +80,7 @@ public: QPointer<QQmlDelegateModel> model; const int groupCount; - QV8Engine * const v8Engine; + QV4::ExecutionEngine * const v4Engine; QQmlDelegateModelAttachedMetaObject *metaObject; const QStringList groupNames; QV4::PersistentValue modelItemProto; @@ -220,7 +220,7 @@ public: void setModel(QQmlDelegateModel *model, Compositor::Group group); bool isChangedConnected(); - void emitChanges(QV8Engine *engine); + void emitChanges(QV4::ExecutionEngine *engine); void emitModelUpdated(bool reset); void createdPackage(int index, QQuickPackage *package); diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 0f04d48bf8..299de8de70 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -274,7 +274,6 @@ QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementInde bool ListModel::sync(ListModel *src, ListModel *target) { // Sanity check - target->m_uid = src->m_uid; bool hasChanges = false; @@ -391,11 +390,8 @@ bool ListModel::sync(ListModel *src, ListModel *target) return hasChanges; } -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) +ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache) : m_layout(layout), m_modelCache(modelCache) { - if (uid == -1) - uid = uidCounter.fetchAndAddOrdered(1); - m_uid = uid; } void ListModel::destroy() @@ -403,7 +399,6 @@ void ListModel::destroy() for (const auto &destroyer : remove(0, elements.count())) destroyer(); - m_uid = -1; m_layout = 0; if (m_modelCache && m_modelCache->m_primary == false) delete m_modelCache; @@ -508,7 +503,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, 0, -1); + ListModel *subModel = new ListModel(r.subLayout, 0); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { @@ -589,7 +584,7 @@ void ListModel::set(int elementIndex, QV4::Object *object) } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, 0, -1); + ListModel *subModel = new ListModel(r.subLayout, 0); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { @@ -1170,7 +1165,7 @@ QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElem if (srcSubModel) { if (targetSubModel == 0) { - targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); + targetSubModel = new ListModel(targetRole.subLayout, 0); target->setListPropertyFast(targetRole, targetSubModel); } if (ListModel::sync(srcSubModel, targetSubModel)) @@ -1329,7 +1324,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d QV4::Scope scope(a->engine()); QV4::ScopedObject o(scope); - ListModel *subModel = new ListModel(role.subLayout, 0, -1); + ListModel *subModel = new ListModel(role.subLayout, 0); int arrayLength = a->getLength(); for (int j=0 ; j < arrayLength ; ++j) { o = a->getIndexed(j); @@ -1788,11 +1783,10 @@ QQmlListModel::QQmlListModel(QObject *parent) m_mainThread = true; m_primary = true; m_agent = 0; - m_uid = uidCounter.fetchAndAddOrdered(1); m_dynamicRoles = false; m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this, -1); + m_listModel = new ListModel(m_layout, this); m_engine = 0; } @@ -1821,7 +1815,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen m_dynamicRoles = orig->m_dynamicRoles; m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); + m_listModel = new ListModel(m_layout, this); if (m_dynamicRoles) sync(orig, this); @@ -1871,7 +1865,7 @@ QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) QV4::ExecutionEngine *QQmlListModel::engine() const { if (m_engine == 0) { - m_engine = QQmlEnginePrivate::get(qmlEngine(this))->v4engine(); + m_engine = qmlEngine(this)->handle(); } return m_engine; @@ -1883,7 +1877,6 @@ bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target) bool hasChanges = false; - target->m_uid = src->m_uid; target->m_roles = src->m_roles; // Build hash of elements <-> uid for each of the lists @@ -2611,7 +2604,7 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp if (role.type == ListLayout::Role::List) { subModel = model->getListProperty(outterElementIndex, role); if (subModel == 0) { - subModel = new ListModel(role.subLayout, 0, -1); + subModel = new ListModel(role.subLayout, 0); QVariant vModel = QVariant::fromValue(subModel); model->setOrCreateProperty(outterElementIndex, elementName, vModel); } @@ -2638,7 +2631,7 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp QString scriptStr = binding->valueAsScriptString(qmlUnit); if (definesEmptyList(scriptStr)) { const ListLayout::Role &role = model->getOrCreateListRole(elementName); - ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); + ListModel *emptyModel = new ListModel(role.subLayout, 0); value = QVariant::fromValue(emptyModel); } else if (binding->isFunctionExpression()) { QQmlBinding::Identifier id = binding->value.compiledScriptIndex; @@ -2689,7 +2682,7 @@ void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::Compila { QQmlListModel *rv = static_cast<QQmlListModel *>(obj); - rv->m_engine = QV8Engine::getV4(qmlEngine(rv)); + rv->m_engine = qmlEngine(rv)->handle(); const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data; diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 18b7b8bb22..cbb12caa20 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -143,7 +143,6 @@ private: QVector<class DynamicRoleModelNode *> m_modelObjects; QVector<QString> m_roles; - int m_uid; struct ElementSync { @@ -154,8 +153,6 @@ private: QVector<int> changedRoles; }; - int getUid() const { return m_uid; } - static bool sync(QQmlListModel *src, QQmlListModel *target); static QQmlListModel *createWithOwner(QQmlListModel *newOwner); diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index 65567ab69a..81b9956ecb 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -328,7 +328,7 @@ class ListModel { public: - ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); + ListModel(ListLayout *layout, QQmlListModel *modelCache); ~ListModel() {} void destroy(); @@ -377,8 +377,6 @@ public: void move(int from, int to, int n); - int getUid() const { return m_uid; } - static bool sync(ListModel *src, ListModel *target); QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); @@ -386,7 +384,6 @@ public: private: QPODVector<ListElement *, 4> elements; ListLayout *m_layout; - int m_uid; QQmlListModel *m_modelCache; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 231c0fa810..32c0c86ff6 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -201,7 +201,7 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) -: QV8Engine(0), p(parent) + : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent) #if QT_CONFIG(qml_network) , accessManager(0) #endif @@ -214,6 +214,7 @@ QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() #if QT_CONFIG(qml_network) delete accessManager; #endif + delete m_v4Engine; } void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() @@ -250,7 +251,7 @@ void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() QQuickWorkerScriptEnginePrivate::method_sendMessage)); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = function; - *jsCallData->thisObject = global(); + *jsCallData->thisObject = m_v4Engine->global(); createsend.set(scope.engine, createsendconstructor->call(jsCallData)); } @@ -267,7 +268,7 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(i QV4::ScopedValue v(scope); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = QV4::Primitive::fromInt32(id); - *jsCallData->thisObject = global(); + *jsCallData->thisObject = m_v4Engine->global(); v = f->call(jsCallData); if (scope.hasException()) v = scope.engine->catchException(); @@ -367,7 +368,7 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d Q_ASSERT(!!qmlContext); QV4::JSCallData jsCallData(scope, 2); - *jsCallData->thisObject = workerEngine->global(); + *jsCallData->thisObject = v4->global(); jsCallData->args[0] = qmlContext->d()->qml(); // ### jsCallData->args[1] = value; f->call(jsCallData); @@ -730,8 +731,7 @@ bool QQuickWorkerScript::event(QEvent *event) QQmlEngine *engine = qmlEngine(this); if (engine) { WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); - QV4::Scope scope(QV8Engine::getV4(v8engine)); + QV4::Scope scope(engine->handle()); QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine)); emit message(QQmlV4Handle(value)); } diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro index e5f6de3314..2539414d8f 100644 --- a/src/qmldebug/qmldebug.pro +++ b/src/qmldebug/qmldebug.pro @@ -7,15 +7,25 @@ load(qt_module) SOURCES += \ qqmldebugclient.cpp \ qqmldebugconnection.cpp \ + qqmldebugmessageclient.cpp \ qqmlenginecontrolclient.cpp \ - qqmlprofilerclient.cpp + qqmlprofilerclient.cpp \ + qqmlprofilerevent.cpp \ + qqmlprofilereventlocation.cpp \ + qqmlprofilereventtype.cpp \ + qqmlprofilertypedevent.cpp HEADERS += \ qqmldebugclient_p.h \ qqmldebugclient_p_p.h \ qqmldebugconnection_p.h \ + qqmldebugmessageclient_p.h \ qqmlenginecontrolclient_p.h \ qqmlenginecontrolclient_p_p.h \ - qqmleventlocation_p.h \ qqmlprofilerclient_p.h \ - qqmlprofilerclient_p_p.h + qqmlprofilerclient_p_p.h \ + qqmlprofilerevent_p.h \ + qqmlprofilereventlocation_p.h \ + qqmlprofilereventreceiver_p.h \ + qqmlprofilereventtype_p.h \ + qqmlprofilertypedevent_p.h diff --git a/src/qmldebug/qqmldebugmessageclient.cpp b/src/qmldebug/qqmldebugmessageclient.cpp new file mode 100644 index 0000000000..a03c1f8af2 --- /dev/null +++ b/src/qmldebug/qqmldebugmessageclient.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldebugmessageclient_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlDebugMessageClient + \internal + + \brief Client for the debug message service + + The QQmlDebugMessageClient receives debug messages routed through the QML + debug connection via QDebugMessageService. + */ + +QQmlDebugMessageClient::QQmlDebugMessageClient(QQmlDebugConnection *client) + : QQmlDebugClient(QLatin1String("DebugMessages"), client) +{ +} + +void QQmlDebugMessageClient::stateChanged(State state) +{ + emit newState(state); +} + +void QQmlDebugMessageClient::messageReceived(const QByteArray &data) +{ + QDataStream ds(data); + QByteArray command; + ds >> command; + + if (command == "MESSAGE") { + int type; + int line; + QByteArray debugMessage; + QByteArray file; + QByteArray function; + ds >> type >> debugMessage >> file >> line >> function; + QQmlDebugContextInfo info; + info.line = line; + info.file = QString::fromUtf8(file); + info.function = QString::fromUtf8(function); + info.timestamp = -1; + if (!ds.atEnd()) { + QByteArray category; + ds >> category; + info.category = QString::fromUtf8(category); + if (!ds.atEnd()) + ds >> info.timestamp; + } + emit message(QtMsgType(type), QString::fromUtf8(debugMessage), info); + } +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmldebugmessageclient_p.h b/src/qmldebug/qqmldebugmessageclient_p.h new file mode 100644 index 0000000000..75c70044e4 --- /dev/null +++ b/src/qmldebug/qqmldebugmessageclient_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDEBUGMESSAGECLIENT_P_H +#define QQMLDEBUGMESSAGECLIENT_P_H + +#include "qqmldebugclient_p.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QQmlDebugContextInfo +{ + int line; + QString file; + QString function; + QString category; + qint64 timestamp; +}; + +class QQmlDebugMessageClient : public QQmlDebugClient +{ + Q_OBJECT + +public: + explicit QQmlDebugMessageClient(QQmlDebugConnection *client); + + virtual void stateChanged(State state) override; + virtual void messageReceived(const QByteArray &) override; + +signals: + void newState(QQmlDebugClient::State); + void message(QtMsgType, const QString &, const QQmlDebugContextInfo &); + +private: + Q_DISABLE_COPY(QQmlDebugMessageClient) +}; + +QT_END_NAMESPACE + +#endif // QQMLDEBUGMESSAGECLIENT_P_H diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp index 3676bd933c..dca564cd76 100644 --- a/src/qmldebug/qqmlprofilerclient.cpp +++ b/src/qmldebug/qqmlprofilerclient.cpp @@ -43,337 +43,296 @@ QT_BEGIN_NAMESPACE -QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection) : - QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection))) +int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event) { -} + int typeIndex = -1; + if (event.serverTypeId != 0) { + QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(event.serverTypeId); -QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) : - QQmlDebugClient(dd) -{ + if (it != serverTypeIds.constEnd()) { + typeIndex = it.value(); + } else { + typeIndex = eventReceiver->numLoadedEventTypes(); + eventReceiver->addEventType(event.type); + serverTypeIds[event.serverTypeId] = typeIndex; + } + } else { + QHash<QQmlProfilerEventType, int>::ConstIterator it = eventTypeIds.constFind(event.type); + + if (it != eventTypeIds.constEnd()) { + typeIndex = it.value(); + } else { + typeIndex = eventReceiver->numLoadedEventTypes(); + eventReceiver->addEventType(event.type); + eventTypeIds[event.type] = typeIndex; + } + } + return typeIndex; } -QQmlProfilerClientPrivate::QQmlProfilerClientPrivate(QQmlDebugConnection *connection) : - QQmlDebugClientPrivate(QQmlProfilerService::s_key, connection), - features(std::numeric_limits<quint64>::max()) +int QQmlProfilerClientPrivate::resolveStackTop() { + if (rangesInProgress.isEmpty()) + return -1; + + QQmlProfilerTypedEvent &typedEvent = rangesInProgress.top(); + int typeIndex = typedEvent.event.typeIndex(); + if (typeIndex >= 0) + return typeIndex; + + typeIndex = resolveType(typedEvent); + typedEvent.event.setTypeIndex(typeIndex); + while (!pendingMessages.isEmpty() + && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) { + forwardEvents(pendingMessages.dequeue()); + } + forwardEvents(typedEvent.event); + return typeIndex; } -void QQmlProfilerClient::setFeatures(quint64 features) +void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last) { - Q_D(QQmlProfilerClient); - d->features = features; + while (!pendingDebugMessages.isEmpty() + && pendingDebugMessages.front().timestamp() <= last.timestamp()) { + eventReceiver->addEvent(pendingDebugMessages.dequeue()); + } + eventReceiver->addEvent(last); } -void QQmlProfilerClient::sendRecordingStatus(bool record, int engineId, quint32 flushInterval) +void QQmlProfilerClientPrivate::processCurrentEvent() { - Q_D(const QQmlProfilerClient); - - QPacket stream(d->connection->currentDataStreamVersion()); - stream << record << engineId << d->features << flushInterval << true; - sendMessage(stream.data()); + // RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore, + // all ranges are perfectly nested. This is why we can defer the type resolution until either + // the range ends or a child range starts. With only the information in RangeStart we wouldn't + // be able to uniquely identify the event type. + Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ? + currentEvent.type.message() : currentEvent.event.rangeStage(); + switch (rangeStage) { + case RangeStart: + resolveStackTop(); + rangesInProgress.push(currentEvent); + break; + case RangeEnd: { + int typeIndex = resolveStackTop(); + if (typeIndex == -1) + break; + currentEvent.event.setTypeIndex(typeIndex); + while (!pendingMessages.isEmpty()) + forwardEvents(pendingMessages.dequeue()); + forwardEvents(currentEvent.event); + rangesInProgress.pop(); + break; + } + case RangeData: + if (!rangesInProgress.isEmpty()) + rangesInProgress.top().type.setData(currentEvent.type.data()); + break; + case RangeLocation: + if (!rangesInProgress.isEmpty()) + rangesInProgress.top().type.setLocation(currentEvent.type.location()); + break; + case DebugMessage: + currentEvent.event.setTypeIndex(resolveType(currentEvent)); + pendingDebugMessages.enqueue(currentEvent.event); + break; + default: { + int typeIndex = resolveType(currentEvent); + currentEvent.event.setTypeIndex(typeIndex); + if (rangesInProgress.isEmpty()) + eventReceiver->addEvent(currentEvent.event); + else + pendingMessages.enqueue(currentEvent.event); + break; + } + } } -void QQmlProfilerClient::traceStarted(qint64 time, int engineId) +void QQmlProfilerClientPrivate::sendRecordingStatus(int engineId) { - Q_UNUSED(time); - Q_UNUSED(engineId); + Q_Q(QQmlProfilerClient); + QPacket stream(connection->currentDataStreamVersion()); + stream << recording << engineId; // engineId -1 is OK. It means "all of them" + if (recording) { + stream << requestedFeatures << flushInterval; + stream << true; // yes, we support type IDs + } + q->sendMessage(stream.data()); } -void QQmlProfilerClient::traceFinished(qint64 time, int engineId) +QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection, + QQmlProfilerEventReceiver *eventReceiver, + quint64 features) + : QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection, eventReceiver))) { - Q_UNUSED(time); - Q_UNUSED(engineId); + Q_D(QQmlProfilerClient); + setRequestedFeatures(features); + connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded, + this, &QQmlProfilerClient::sendRecordingStatus); } -void QQmlProfilerClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) +QQmlProfilerClient::~QQmlProfilerClient() { - Q_UNUSED(type); - Q_UNUSED(startTime); + //Disable profiling if started by client + //Profiling data will be lost!! + if (isRecording()) + setRecording(false); } -void QQmlProfilerClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &data) +void QQmlProfilerClient::clearEvents() { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(data); + Q_D(QQmlProfilerClient); + d->rangesInProgress.clear(); + d->pendingMessages.clear(); + d->pendingDebugMessages.clear(); + if (d->recordedFeatures != 0) { + d->recordedFeatures = 0; + emit recordedFeaturesChanged(0); + } + emit cleared(); } -void QQmlProfilerClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) +void QQmlProfilerClient::clearAll() { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(location); + Q_D(QQmlProfilerClient); + d->serverTypeIds.clear(); + d->eventTypeIds.clear(); + clearEvents(); } -void QQmlProfilerClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) +void QQmlProfilerClientPrivate::finalize() { - Q_UNUSED(type); - Q_UNUSED(endTime); + while (!rangesInProgress.isEmpty()) { + currentEvent = rangesInProgress.top(); + currentEvent.event.setRangeStage(RangeEnd); + currentEvent.event.setTimestamp(maximumTime); + processCurrentEvent(); + } + while (!pendingDebugMessages.isEmpty()) + eventReceiver->addEvent(pendingDebugMessages.dequeue()); } -void QQmlProfilerClient::animationFrame(qint64 time, int frameRate, int animationCount, - int threadId) + +void QQmlProfilerClient::sendRecordingStatus(int engineId) { - Q_UNUSED(time); - Q_UNUSED(frameRate); - Q_UNUSED(animationCount); - Q_UNUSED(threadId); + Q_D(QQmlProfilerClient); + d->sendRecordingStatus(engineId); } -void QQmlProfilerClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) +bool QQmlProfilerClient::isRecording() const { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); - Q_UNUSED(numericData3); - Q_UNUSED(numericData4); - Q_UNUSED(numericData5); + Q_D(const QQmlProfilerClient); + return d->recording; } -void QQmlProfilerClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &url, int numericData1, - int numericData2) +void QQmlProfilerClient::setRecording(bool v) { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(url); - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); + Q_D(QQmlProfilerClient); + if (v == d->recording) + return; + + d->recording = v; + + if (state() == Enabled) + sendRecordingStatus(); + + emit recordingChanged(v); } -void QQmlProfilerClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) +quint64 QQmlProfilerClient::recordedFeatures() const { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(amount); + Q_D(const QQmlProfilerClient); + return d->recordedFeatures; } -void QQmlProfilerClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) +void QQmlProfilerClient::setRequestedFeatures(quint64 features) { - Q_UNUSED(type); - Q_UNUSED(time); - Q_UNUSED(a); - Q_UNUSED(b); + Q_D(QQmlProfilerClient); + d->requestedFeatures = features; + if (features & static_cast<quint64>(1) << ProfileDebugMessages) { + if (d->messageClient.isNull()) { + d->messageClient.reset(new QQmlDebugMessageClient(connection())); + connect(d->messageClient.data(), &QQmlDebugMessageClient::message, this, + [this](QtMsgType type, const QString &text, const QQmlDebugContextInfo &context) + { + Q_D(QQmlProfilerClient); + d->updateFeatures(ProfileDebugMessages); + d->currentEvent.event.setTimestamp(context.timestamp > 0 ? context.timestamp : 0); + d->currentEvent.event.setTypeIndex(-1); + d->currentEvent.event.setString(text); + d->currentEvent.type = QQmlProfilerEventType( + DebugMessage, MaximumRangeType, type, + QQmlProfilerEventLocation(context.file, context.line, 1)); + d->currentEvent.serverTypeId = 0; + d->processCurrentEvent(); + }); + } + } else { + d->messageClient.reset(); + } } -void QQmlProfilerClient::complete() +void QQmlProfilerClient::setFlushInterval(quint32 flushInterval) { + Q_D(QQmlProfilerClient); + d->flushInterval = flushInterval; } -void QQmlProfilerClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType) +QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) : + QQmlDebugClient(dd) { - Q_UNUSED(messageType); - Q_UNUSED(time); - Q_UNUSED(detailType); + Q_D(QQmlProfilerClient); + connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded, + this, &QQmlProfilerClient::sendRecordingStatus); } -void QQmlProfilerClient::unknownData(QPacket &stream) +bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature) { - Q_UNUSED(stream); + Q_Q(QQmlProfilerClient); + quint64 flag = 1ULL << feature; + if (!(requestedFeatures & flag)) + return false; + if (!(recordedFeatures & flag)) { + recordedFeatures |= flag; + emit q->recordedFeaturesChanged(recordedFeatures); + } + return true; } -inline QQmlProfilerDefinitions::ProfileFeature featureFromRangeType( - QQmlProfilerDefinitions::RangeType range) +void QQmlProfilerClient::stateChanged(State status) { - switch (range) { - case QQmlProfilerDefinitions::Painting: - return QQmlProfilerDefinitions::ProfilePainting; - case QQmlProfilerDefinitions::Compiling: - return QQmlProfilerDefinitions::ProfileCompiling; - case QQmlProfilerDefinitions::Creating: - return QQmlProfilerDefinitions::ProfileCreating; - case QQmlProfilerDefinitions::Binding: - return QQmlProfilerDefinitions::ProfileBinding; - case QQmlProfilerDefinitions::HandlingSignal: - return QQmlProfilerDefinitions::ProfileHandlingSignal; - case QQmlProfilerDefinitions::Javascript: - return QQmlProfilerDefinitions::ProfileJavaScript; - default: - return QQmlProfilerDefinitions::MaximumProfileFeature; + if (status == Enabled) { + sendRecordingStatus(-1); + } else { + Q_D(QQmlProfilerClient); + d->finalize(); } + } void QQmlProfilerClient::messageReceived(const QByteArray &data) { Q_D(QQmlProfilerClient); - QPacket stream(d->connection->currentDataStreamVersion(), data); - // Force all the 1 << <FLAG> expressions to be done in 64 bit, to silence some warnings - const quint64 one = static_cast<quint64>(1); - - qint64 time; - int messageType; - - stream >> time >> messageType; - - if (messageType >= QQmlProfilerDefinitions::MaximumMessage) { - unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, -1); - return; + stream >> d->currentEvent; + + d->maximumTime = qMax(d->currentEvent.event.timestamp(), d->maximumTime); + if (d->currentEvent.type.message() == Complete) { + d->finalize(); + emit complete(d->maximumTime); + } else if (d->currentEvent.type.message() == Event + && d->currentEvent.type.detailType() == StartTrace) { + emit traceStarted(d->currentEvent.event.timestamp(), + d->currentEvent.event.numbers<QList<int>, qint32>()); + } else if (d->currentEvent.type.message() == Event + && d->currentEvent.type.detailType() == EndTrace) { + emit traceFinished(d->currentEvent.event.timestamp(), + d->currentEvent.event.numbers<QList<int>, qint32>()); + } else if (d->updateFeatures(d->currentEvent.type.feature())) { + d->processCurrentEvent(); } - - if (messageType == QQmlProfilerDefinitions::Event) { - int type; - stream >> type; - - QQmlProfilerDefinitions::EventType eventType = - static_cast<QQmlProfilerDefinitions::EventType>(type); - - if (eventType == QQmlProfilerDefinitions::EndTrace) { - int engineId = -1; - if (!stream.atEnd()) - stream >> engineId; - traceFinished(time, engineId); - } else if (eventType == QQmlProfilerDefinitions::AnimationFrame) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileAnimations)) - return; - - int frameRate, animationCount; - int threadId = 0; - stream >> frameRate >> animationCount; - if (!stream.atEnd()) - stream >> threadId; - - animationFrame(time, frameRate, animationCount, threadId); - } else if (type == QQmlProfilerDefinitions::StartTrace) { - int engineId = -1; - if (!stream.atEnd()) - stream >> engineId; - traceStarted(time, engineId); - } else if (eventType == QQmlProfilerDefinitions::Key || - eventType == QQmlProfilerDefinitions::Mouse) { - - if (!(d->features & one << QQmlProfilerDefinitions::ProfileInputEvents)) - return; - - int type; - if (!stream.atEnd()) { - stream >> type; - } else { - type = (eventType == QQmlProfilerDefinitions::Key) ? - QQmlProfilerDefinitions::InputKeyUnknown : - QQmlProfilerDefinitions::InputMouseUnknown; - } - - int a = 0; - if (!stream.atEnd()) - stream >> a; - - int b = 0; - if (!stream.atEnd()) - stream >> b; - - inputEvent(static_cast<QQmlProfilerDefinitions::InputEventType>(type), time, a, b); - } else { - unknownEvent(QQmlProfilerDefinitions::Event, time, type); - } - } else if (messageType == QQmlProfilerDefinitions::Complete) { - complete(); - } else if (messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileSceneGraph)) - return; - - int type; - int count = 0; - qint64 params[5]; - - stream >> type; - while (!stream.atEnd()) - stream >> params[count++]; - - while (count < 5) - params[count++] = 0; - - sceneGraphEvent(static_cast<QQmlProfilerDefinitions::SceneGraphFrameType>(type), time, - params[0], params[1], params[2], params[3], params[4]); - } else if (messageType == QQmlProfilerDefinitions::PixmapCacheEvent) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfilePixmapCache)) - return; - - int type, param1 = 0, param2 = 0; - QString pixUrl; - stream >> type >> pixUrl; - - QQmlProfilerDefinitions::PixmapEventType pixmapEventType = - static_cast<QQmlProfilerDefinitions::PixmapEventType>(type); - - if (pixmapEventType == QQmlProfilerDefinitions::PixmapReferenceCountChanged || - pixmapEventType == QQmlProfilerDefinitions::PixmapCacheCountChanged) { - stream >> param1; - } else if (pixmapEventType == QQmlProfilerDefinitions::PixmapSizeKnown) { - stream >> param1 >> param2; - } - - pixmapCacheEvent(pixmapEventType, time, pixUrl, param1, param2); - } else if (messageType == QQmlProfilerDefinitions::MemoryAllocation) { - if (!(d->features & one << QQmlProfilerDefinitions::ProfileMemory)) - return; - int type; - qint64 delta; - stream >> type >> delta; - memoryAllocation((QQmlProfilerDefinitions::MemoryType)type, time, delta); - } else { - int range; - stream >> range; - - QQmlProfilerDefinitions::RangeType rangeType = - static_cast<QQmlProfilerDefinitions::RangeType>(range); - - if (range >= QQmlProfilerDefinitions::MaximumRangeType || - !(d->features & one << featureFromRangeType(rangeType))) - return; - - qint64 typeId = 0; - if (messageType == QQmlProfilerDefinitions::RangeStart) { - rangeStart(rangeType, time); - if (!stream.atEnd()) { - stream >> typeId; - auto i = d->types.constFind(typeId); - if (i != d->types.constEnd()) { - rangeLocation(rangeType, time, i->location); - rangeData(rangeType, time, i->name); - } - } - } else if (messageType == QQmlProfilerDefinitions::RangeData) { - QString data; - stream >> data; - rangeData(rangeType, time, data); - if (!stream.atEnd()) { - stream >> typeId; - d->types[typeId].name = data; - } - } else if (messageType == QQmlProfilerDefinitions::RangeLocation) { - QQmlEventLocation location; - stream >> location.filename >> location.line; - - if (!stream.atEnd()) - stream >> location.column; - - rangeLocation(rangeType, time, location); - if (!stream.atEnd()) { - stream >> typeId; - d->types[typeId].location = location; - } - } else if (messageType == QQmlProfilerDefinitions::RangeEnd) { - rangeEnd(rangeType, time); - } else { - unknownEvent(static_cast<QQmlProfilerDefinitions::Message>(messageType), time, range); - } - } - - if (!stream.atEnd()) - unknownData(stream); } + QT_END_NAMESPACE #include "moc_qqmlprofilerclient_p.cpp" diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h index a328cad26c..68a32a1a5a 100644 --- a/src/qmldebug/qqmlprofilerclient_p.h +++ b/src/qmldebug/qqmlprofilerclient_p.h @@ -41,7 +41,9 @@ #define QQMLPROFILERCLIENT_P_H #include "qqmldebugclient_p.h" -#include "qqmleventlocation_p.h" +#include "qqmlprofilereventlocation_p.h" +#include "qqmlprofilereventreceiver_p.h" + #include <private/qqmlprofilerdefinitions_p.h> #include <private/qpacket_p.h> @@ -59,52 +61,42 @@ QT_BEGIN_NAMESPACE class QQmlProfilerClientPrivate; -class QQmlProfilerClient : public QQmlDebugClient +class QQmlProfilerClient : public QQmlDebugClient, public QQmlProfilerDefinitions { Q_OBJECT Q_DECLARE_PRIVATE(QQmlProfilerClient) + Q_PROPERTY(bool recording READ isRecording WRITE setRecording NOTIFY recordingChanged) public: - QQmlProfilerClient(QQmlDebugConnection *connection); - void setFeatures(quint64 features); - void sendRecordingStatus(bool record, int engineId = -1, quint32 flushInterval = 0); - -protected: - QQmlProfilerClient(QQmlProfilerClientPrivate &dd); - -private: - void messageReceived(const QByteArray &message) override; + QQmlProfilerClient(QQmlDebugConnection *connection, QQmlProfilerEventReceiver *eventReceiver, + quint64 features = std::numeric_limits<quint64>::max()); + ~QQmlProfilerClient(); - virtual void traceStarted(qint64 time, int engineId); - virtual void traceFinished(qint64 time, int engineId); + bool isRecording() const; + void setRecording(bool); + quint64 recordedFeatures() const; + virtual void messageReceived(const QByteArray &) override; + virtual void stateChanged(State status) override; - virtual void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime); - virtual void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &data); - virtual void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location); - virtual void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime); + void clearEvents(); + void clearAll(); - virtual void animationFrame(qint64 time, int frameRate, int animationCount, int threadId); + void sendRecordingStatus(int engineId = -1); + void setRequestedFeatures(quint64 features); + void setFlushInterval(quint32 flushInterval); - virtual void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - - virtual void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2); - - virtual void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount); +protected: + QQmlProfilerClient(QQmlProfilerClientPrivate &dd); - virtual void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, - int b); +signals: + void complete(qint64 maximumTime); + void traceFinished(qint64 timestamp, const QList<int> &engineIds); + void traceStarted(qint64 timestamp, const QList<int> &engineIds); - virtual void complete(); + void recordingChanged(bool arg); + void recordedFeaturesChanged(quint64 features); - virtual void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType); - virtual void unknownData(QPacket &stream); + void cleared(); }; QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h index 9c44113aa8..cf0145409a 100644 --- a/src/qmldebug/qqmlprofilerclient_p_p.h +++ b/src/qmldebug/qqmlprofilerclient_p_p.h @@ -40,8 +40,15 @@ #ifndef QQMLPROFILERCLIENT_P_P_H #define QQMLPROFILERCLIENT_P_P_H -#include "qqmlprofilerclient_p.h" #include "qqmldebugclient_p_p.h" +#include "qqmldebugmessageclient_p.h" +#include "qqmlenginecontrolclient_p.h" +#include "qqmlprofilerclient_p.h" +#include "qqmlprofilertypedevent_p.h" + +#include <private/qqmlprofilerdefinitions_p.h> + +#include <QtCore/qqueue.h> // // W A R N I N G @@ -56,20 +63,48 @@ QT_BEGIN_NAMESPACE -struct QQmlProfilerRangeType -{ - QQmlEventLocation location; - QString name; -}; - -class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate -{ +class QQmlProfilerClientPrivate : public QQmlDebugClientPrivate, public QQmlProfilerDefinitions { Q_DECLARE_PUBLIC(QQmlProfilerClient) public: - QQmlProfilerClientPrivate(QQmlDebugConnection *connection); - quint64 features; + QQmlProfilerClientPrivate(QQmlDebugConnection *connection, + QQmlProfilerEventReceiver *eventReceiver) + : QQmlDebugClientPrivate(QLatin1String("CanvasFrameRate"), connection) + , eventReceiver(eventReceiver) + , engineControl(new QQmlEngineControlClient(connection)) + , maximumTime(0) + , recording(false) + , requestedFeatures(0) + , recordedFeatures(0) + , flushInterval(0) + { + } + + virtual ~QQmlProfilerClientPrivate() override {} + + void sendRecordingStatus(int engineId); + bool updateFeatures(ProfileFeature feature); + int resolveType(const QQmlProfilerTypedEvent &type); + int resolveStackTop(); + void forwardEvents(const QQmlProfilerEvent &last); + void processCurrentEvent(); + void finalize(); + + QQmlProfilerEventReceiver *eventReceiver; + QScopedPointer<QQmlEngineControlClient> engineControl; + QScopedPointer<QQmlDebugMessageClient> messageClient; + qint64 maximumTime; + bool recording; + quint64 requestedFeatures; + quint64 recordedFeatures; + quint32 flushInterval; - QHash<qint64, QQmlProfilerRangeType> types; + // Reuse the same event, so that we don't have to constantly reallocate all the data. + QQmlProfilerTypedEvent currentEvent; + QHash<QQmlProfilerEventType, int> eventTypeIds; + QHash<qint64, int> serverTypeIds; + QStack<QQmlProfilerTypedEvent> rangesInProgress; + QQueue<QQmlProfilerEvent> pendingMessages; + QQueue<QQmlProfilerEvent> pendingDebugMessages; }; QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilerevent.cpp b/src/qmldebug/qqmlprofilerevent.cpp new file mode 100644 index 0000000000..30ae1c79a1 --- /dev/null +++ b/src/qmldebug/qqmlprofilerevent.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilerevent_p.h" +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) +{ + if (event1.timestamp() != event2.timestamp() || event1.typeIndex() != event2.typeIndex()) + return false; + + // This is not particularly efficient, but we also don't need to do this very often. + return event1.numbers<QVarLengthArray<qint64>>() == event2.numbers<QVarLengthArray<qint64>>(); +} + +bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) +{ + return !(event1 == event2); +} + +enum SerializationType { + OneByte = 0, + TwoByte = 1, + FourByte = 2, + EightByte = 3, + TypeMask = 0x3 +}; + +enum SerializationTypeOffset { + TimestampOffset = 0, + TypeIndexOffset = 2, + DataLengthOffset = 4, + DataOffset = 6 +}; + +template<typename Number> +static inline void readNumbers(QDataStream &stream, Number *data, quint16 length) +{ + for (quint16 i = 0; i != length; ++i) + stream >> data[i]; +} + +template<typename Number> +static inline Number readNumber(QDataStream &stream, qint8 type) +{ + switch (type) { + case OneByte: { + qint8 value; + stream >> value; + return static_cast<Number>(value); + } + case TwoByte: { + qint16 value; + stream >> value; + return static_cast<Number>(value); + } + case FourByte: { + qint32 value; + stream >> value; + return static_cast<Number>(value); + } + case EightByte: { + qint64 value; + stream >> value; + return static_cast<Number>(value); + } + default: + Q_UNREACHABLE(); + return 0; + } +} + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event) +{ + qint8 type; + stream >> type; + + event.m_timestamp = readNumber<qint64>(stream, (type >> TimestampOffset) & TypeMask); + event.m_typeIndex = readNumber<qint32>(stream, (type >> TypeIndexOffset) & TypeMask); + event.m_dataLength = readNumber<quint16>(stream, (type >> DataLengthOffset) & TypeMask); + + uint bytesPerNumber = 1 << ((type >> DataOffset) & TypeMask); + + if (event.m_dataLength * bytesPerNumber > sizeof(event.m_data)) { + event.m_dataType = static_cast<QQmlProfilerEvent::Type>((bytesPerNumber * 8) + | QQmlProfilerEvent::External); + event.m_data.external = malloc(event.m_dataLength * bytesPerNumber); + } else { + event.m_dataType = static_cast<QQmlProfilerEvent::Type>(bytesPerNumber * 8); + } + + switch (event.m_dataType) { + case QQmlProfilerEvent::Inline8Bit: + readNumbers<qint8>(stream, event.m_data.internal8bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External8Bit: + readNumbers<qint8>(stream, static_cast<qint8 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline16Bit: + readNumbers<qint16>(stream, event.m_data.internal16bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External16Bit: + readNumbers<qint16>(stream, static_cast<qint16 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline32Bit: + readNumbers<qint32>(stream, event.m_data.internal32bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External32Bit: + readNumbers<qint32>(stream, static_cast<qint32 *>(event.m_data.external), + event.m_dataLength); + break; + case QQmlProfilerEvent::Inline64Bit: + readNumbers<qint64>(stream, event.m_data.internal64bit, event.m_dataLength); + break; + case QQmlProfilerEvent::External64Bit: + readNumbers<qint64>(stream, static_cast<qint64 *>(event.m_data.external), + event.m_dataLength); + break; + default: + Q_UNREACHABLE(); + break; + } + + return stream; +} + +static inline qint8 minimumType(const QQmlProfilerEvent &event, quint16 length, + quint16 origBitsPerNumber) +{ + qint8 type = OneByte; + bool ok = true; + for (quint16 i = 0; i < length;) { + if ((1 << type) == origBitsPerNumber / 8) + return type; + switch (type) { + case OneByte: + ok = (event.number<qint8>(i) == event.number<qint64>(i)); + break; + case TwoByte: + ok = (event.number<qint16>(i) == event.number<qint64>(i)); + break; + case FourByte: + ok = (event.number<qint32>(i) == event.number<qint64>(i)); + break; + default: + // EightByte isn't possible, as (1 << type) == origBitsPerNumber / 8 then. + Q_UNREACHABLE(); + break; + } + + if (ok) + ++i; + else + ++type; + } + return type; +} + +template<typename Number> +static inline qint8 minimumType(Number number) +{ + if (static_cast<qint8>(number) == number) + return OneByte; + if (static_cast<qint16>(number) == number) + return TwoByte; + if (static_cast<qint32>(number) == number) + return FourByte; + return EightByte; +} + +template<typename Number> +static inline void writeNumbers(QDataStream &stream, const QQmlProfilerEvent &event, quint16 length) +{ + for (quint16 i = 0; i != length; ++i) + stream << event.number<Number>(i); +} + +template<typename Number> +static inline void writeNumber(QDataStream &stream, Number number, qint8 type) +{ + switch (type) { + case OneByte: + stream << static_cast<qint8>(number); + break; + case TwoByte: + stream << static_cast<qint16>(number); + break; + case FourByte: + stream << static_cast<qint32>(number); + break; + case EightByte: + stream << static_cast<qint64>(number); + break; + default: + Q_UNREACHABLE(); + break; + } +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event) +{ + qint8 type = minimumType(event.m_timestamp); // << TimestampOffset; + type |= minimumType(event.m_typeIndex) << TypeIndexOffset; + type |= minimumType(event.m_dataLength) << DataLengthOffset; + type |= minimumType(event, event.m_dataLength, event.m_dataType) << DataOffset; + stream << type; + + writeNumber(stream, event.m_timestamp, (type >> TimestampOffset) & TypeMask); + writeNumber(stream, event.m_typeIndex, (type >> TypeIndexOffset) & TypeMask); + writeNumber(stream, event.m_dataLength, (type >> DataLengthOffset) & TypeMask); + + switch ((type >> DataOffset) & TypeMask) { + case OneByte: + writeNumbers<qint8>(stream, event, event.m_dataLength); + break; + case TwoByte: + writeNumbers<qint16>(stream, event, event.m_dataLength); + break; + case FourByte: + writeNumbers<qint32>(stream, event, event.m_dataLength); + break; + case EightByte: + writeNumbers<qint64>(stream, event, event.m_dataLength); + break; + default: + Q_UNREACHABLE(); + break; + } + + return stream; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilerevent_p.h b/src/qmldebug/qqmlprofilerevent_p.h new file mode 100644 index 0000000000..93562302e9 --- /dev/null +++ b/src/qmldebug/qqmlprofilerevent_p.h @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENT_P_H +#define QQMLPROFILEREVENT_P_H + +#include <private/qqmlprofilerdefinitions_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qmetatype.h> + +#include <initializer_list> +#include <type_traits> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QQmlProfilerEvent : public QQmlProfilerDefinitions { + QQmlProfilerEvent() : + m_timestamp(-1), m_typeIndex(-1), m_dataType(Inline8Bit), m_dataLength(0) + {} + + template<typename Number> + QQmlProfilerEvent(qint64 timestamp, int typeIndex, std::initializer_list<Number> list) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<std::initializer_list<Number>, Number>(list); + } + + QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QString &data) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<QByteArray, qint8>(data.toUtf8()); + } + + template<typename Number> + QQmlProfilerEvent(qint64 timestamp, int typeIndex, const QVector<Number> &data) + : m_timestamp(timestamp), m_typeIndex(typeIndex) + { + assignNumbers<QVector<Number>, Number>(data); + } + + QQmlProfilerEvent(const QQmlProfilerEvent &other) + : m_timestamp(other.m_timestamp), m_typeIndex(other.m_typeIndex), + m_dataType(other.m_dataType), m_dataLength(other.m_dataLength) + { + assignData(other); + } + + QQmlProfilerEvent(QQmlProfilerEvent &&other) + { + memcpy(this, &other, sizeof(QQmlProfilerEvent)); + other.m_dataType = Inline8Bit; // prevent dtor from deleting the pointer + } + + QQmlProfilerEvent &operator=(const QQmlProfilerEvent &other) + { + if (this != &other) { + clearPointer(); + m_timestamp = other.m_timestamp; + m_typeIndex = other.m_typeIndex; + m_dataType = other.m_dataType; + m_dataLength = other.m_dataLength; + assignData(other); + } + return *this; + } + + QQmlProfilerEvent &operator=(QQmlProfilerEvent &&other) + { + if (this != &other) { + memcpy(this, &other, sizeof(QQmlProfilerEvent)); + other.m_dataType = Inline8Bit; + } + return *this; + } + + ~QQmlProfilerEvent() + { + clearPointer(); + } + + qint64 timestamp() const { return m_timestamp; } + void setTimestamp(qint64 timestamp) { m_timestamp = timestamp; } + + int typeIndex() const { return m_typeIndex; } + void setTypeIndex(int typeIndex) { m_typeIndex = typeIndex; } + + template<typename Number> + Number number(int i) const + { + // Trailing zeroes can be omitted, for example for SceneGraph events + if (i >= m_dataLength) + return 0; + switch (m_dataType) { + case Inline8Bit: + return m_data.internal8bit[i]; +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Warray-bounds") // Mingw 5.3 gcc doesn't get the type/length logic. + case Inline16Bit: + return m_data.internal16bit[i]; + case Inline32Bit: + return m_data.internal32bit[i]; + case Inline64Bit: + return m_data.internal64bit[i]; +QT_WARNING_POP + case External8Bit: + return static_cast<const qint8 *>(m_data.external)[i]; + case External16Bit: + return static_cast<const qint16 *>(m_data.external)[i]; + case External32Bit: + return static_cast<const qint32 *>(m_data.external)[i]; + case External64Bit: + return static_cast<const qint64 *>(m_data.external)[i]; + default: + return 0; + } + } + + template<typename Number> + void setNumber(int i, Number number) + { + QVarLengthArray<Number> nums = numbers<QVarLengthArray<Number>, Number>(); + int prevSize = nums.size(); + if (i >= prevSize) { + nums.resize(i + 1); + // Fill with zeroes. We don't want to accidentally prevent squeezing. + while (prevSize < i) + nums[prevSize++] = 0; + } + nums[i] = number; + setNumbers<QVarLengthArray<Number>, Number>(nums); + } + + template<typename Container, typename Number> + void setNumbers(const Container &numbers) + { + clearPointer(); + assignNumbers<Container, Number>(numbers); + } + + template<typename Number> + void setNumbers(std::initializer_list<Number> numbers) + { + setNumbers<std::initializer_list<Number>, Number>(numbers); + } + + template<typename Container, typename Number = qint64> + Container numbers() const + { + Container container; + for (int i = 0; i < m_dataLength; ++i) + container.append(number<Number>(i)); + return container; + } + + QString string() const + { + switch (m_dataType) { + case External8Bit: + return QString::fromUtf8(static_cast<const char *>(m_data.external), m_dataLength); + case Inline8Bit: + return QString::fromUtf8(m_data.internalChar, m_dataLength); + default: + Q_UNREACHABLE(); + return QString(); + } + } + + void setString(const QString &data) + { + clearPointer(); + assignNumbers<QByteArray, char>(data.toUtf8()); + } + + Message rangeStage() const + { + Q_ASSERT(m_dataType == Inline8Bit); + return static_cast<Message>(m_data.internal8bit[0]); + } + + void setRangeStage(Message stage) + { + clearPointer(); + m_dataType = Inline8Bit; + m_dataLength = 1; + m_data.internal8bit[0] = stage; + } + + bool isValid() const + { + return m_timestamp != -1; + } + +private: + enum Type: quint16 { + External = 1, + Inline8Bit = 8, + External8Bit = Inline8Bit | External, + Inline16Bit = 16, + External16Bit = Inline16Bit | External, + Inline32Bit = 32, + External32Bit = Inline32Bit | External, + Inline64Bit = 64, + External64Bit = Inline64Bit | External + }; + + qint64 m_timestamp; + + static const int s_internalDataLength = 8; + union { + void *external; + char internalChar [s_internalDataLength]; + qint8 internal8bit [s_internalDataLength]; + qint16 internal16bit[s_internalDataLength / 2]; + qint32 internal32bit[s_internalDataLength / 4]; + qint64 internal64bit[s_internalDataLength / 8]; + } m_data; + + qint32 m_typeIndex; + Type m_dataType; + quint16 m_dataLength; + + void assignData(const QQmlProfilerEvent &other) + { + if (m_dataType & External) { + uint length = m_dataLength * (other.m_dataType / 8); + m_data.external = malloc(length); + memcpy(m_data.external, other.m_data.external, length); + } else { + memcpy(&m_data, &other.m_data, sizeof(m_data)); + } + } + + template<typename Big, typename Small> + bool squeezable(Big source) + { + return static_cast<Small>(source) == source; + } + + template<typename Container, typename Number> + typename std::enable_if<(sizeof(Number) > 1), bool>::type + squeeze(const Container &numbers) + { + typedef typename QIntegerForSize<sizeof(Number) / 2>::Signed Small; + foreach (Number item, numbers) { + if (!squeezable<Number, Small>(item)) + return false; + } + assignNumbers<Container, Small>(numbers); + return true; + } + + template<typename Container, typename Number> + typename std::enable_if<(sizeof(Number) <= 1), bool>::type + squeeze(const Container &) + { + return false; + } + + template<typename Container, typename Number> + void assignNumbers(const Container &numbers) + { + Number *data; + m_dataLength = squeezable<size_t, quint16>(static_cast<size_t>(numbers.size())) ? + static_cast<quint16>(numbers.size()) : std::numeric_limits<quint16>::max(); + if (m_dataLength > sizeof(m_data) / sizeof(Number)) { + if (squeeze<Container, Number>(numbers)) + return; + m_dataType = static_cast<Type>((sizeof(Number) * 8) | External); + m_data.external = malloc(m_dataLength * sizeof(Number)); + data = static_cast<Number *>(m_data.external); + } else { + m_dataType = static_cast<Type>(sizeof(Number) * 8); + data = static_cast<Number *>(m_dataType & External ? m_data.external : &m_data); + } + quint16 i = 0; + for (Number item : numbers) { + if (i >= m_dataLength) + break; + data[i++] = item; + } + } + + void clearPointer() + { + if (m_dataType & External) + free(m_data.external); + } + + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event); +}; + +bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2); +bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2); + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event); + +Q_DECLARE_TYPEINFO(QQmlProfilerEvent, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlProfilerEvent) + +#endif // QQMLPROFILEREVENT_P_H diff --git a/src/qmldebug/qqmlprofilereventlocation.cpp b/src/qmldebug/qqmlprofilereventlocation.cpp new file mode 100644 index 0000000000..8be44c17a3 --- /dev/null +++ b/src/qmldebug/qqmlprofilereventlocation.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilereventlocation_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location) +{ + return stream >> location.m_filename >> location.m_line >> location.m_column; +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location) +{ + return stream << location.m_filename << location.m_line << location.m_column; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilereventlocation_p.h b/src/qmldebug/qqmlprofilereventlocation_p.h new file mode 100644 index 0000000000..6f37eab14b --- /dev/null +++ b/src/qmldebug/qqmlprofilereventlocation_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTLOCATION_P_H +#define QQMLPROFILEREVENTLOCATION_P_H + +#include <QtCore/qstring.h> +#include <QtCore/qhash.h> +#include <QtCore/qdatastream.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlProfilerEventLocation +{ +public: + QQmlProfilerEventLocation() : m_line(-1),m_column(-1) {} + QQmlProfilerEventLocation(const QString &file, int lineNumber, int columnNumber) : + m_filename(file), m_line(lineNumber), m_column(columnNumber) + {} + + void clear() + { + m_filename.clear(); + m_line = m_column = -1; + } + + bool isValid() const + { + return !m_filename.isEmpty(); + } + + QString filename() const { return m_filename; } + int line() const { return m_line; } + int column() const { return m_column; } + +private: + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location); + + QString m_filename; + int m_line; + int m_column; +}; + +inline bool operator==(const QQmlProfilerEventLocation &location1, + const QQmlProfilerEventLocation &location2) +{ + // compare filename last as it's expensive. + return location1.line() == location2.line() && location1.column() == location2.column() + && location1.filename() == location2.filename(); +} + +inline bool operator!=(const QQmlProfilerEventLocation &location1, + const QQmlProfilerEventLocation &location2) +{ + return !(location1 == location2); +} + +inline uint qHash(const QQmlProfilerEventLocation &location) +{ + return qHash(location.filename()) + ^ ((location.line() & 0xfff) // 12 bits of line number + | ((location.column() << 16) & 0xff0000)); // 8 bits of column + +} + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventLocation &location); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventLocation &location); + +Q_DECLARE_TYPEINFO(QQmlProfilerEventLocation, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QQMLPROFILEREVENTLOCATION_P_H diff --git a/src/qmldebug/qqmlprofilereventreceiver_p.h b/src/qmldebug/qqmlprofilereventreceiver_p.h new file mode 100644 index 0000000000..ee8e592858 --- /dev/null +++ b/src/qmldebug/qqmlprofilereventreceiver_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTRECEIVER_P_H +#define QQMLPROFILEREVENTRECEIVER_P_H + +#include "qqmlprofilerevent_p.h" +#include "qqmlprofilereventtype_p.h" + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + +class QQmlProfilerEventReceiver : public QObject +{ + Q_OBJECT +public: + QQmlProfilerEventReceiver(QObject *parent = nullptr) : QObject(parent) {} + + virtual int numLoadedEventTypes() const = 0; + virtual void addEventType(const QQmlProfilerEventType &type) = 0; + virtual void addEvent(const QQmlProfilerEvent &event) = 0; +}; + +QT_END_NAMESPACE + +#endif // QQMLPROFILEREVENTRECEIVER_P_H diff --git a/src/qmldebug/qqmlprofilereventtype.cpp b/src/qmldebug/qqmlprofilereventtype.cpp new file mode 100644 index 0000000000..b0aad3fc5b --- /dev/null +++ b/src/qmldebug/qqmlprofilereventtype.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilereventtype_p.h" + +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type) +{ + quint8 message; + quint8 rangeType; + stream >> type.m_displayName >> type.m_data >> type.m_location >> message >> rangeType + >> type.m_detailType; + type.m_message = static_cast<QQmlProfilerDefinitions::Message>(message); + type.m_rangeType = static_cast<QQmlProfilerDefinitions::RangeType>(rangeType); + return stream; +} + +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type) +{ + return stream << type.m_displayName << type.m_data << type.m_location + << static_cast<quint8>(type.m_message) << static_cast<quint8>(type.m_rangeType) + << type.m_detailType; +} + +QQmlProfilerDefinitions::ProfileFeature QQmlProfilerEventType::feature() const +{ + switch (m_message) { + case Event: { + switch (m_detailType) { + case Mouse: + case Key: + return ProfileInputEvents; + case AnimationFrame: + return ProfileAnimations; + default: + return MaximumProfileFeature; + } + } + case PixmapCacheEvent: + return ProfilePixmapCache; + case SceneGraphFrame: + return ProfileSceneGraph; + case MemoryAllocation: + return ProfileMemory; + case DebugMessage: + return ProfileDebugMessages; + default: + return featureFromRangeType(m_rangeType); + } +} + + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmlprofilereventtype_p.h b/src/qmldebug/qqmlprofilereventtype_p.h new file mode 100644 index 0000000000..7bd67f9ca8 --- /dev/null +++ b/src/qmldebug/qqmlprofilereventtype_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPROFILEREVENTTYPE_P_H +#define QQMLPROFILEREVENTTYPE_P_H + +#include "qqmlprofilereventlocation_p.h" + +#include <private/qqmlprofilerdefinitions_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qhash.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QQmlProfilerEventType : public QQmlProfilerDefinitions { +public: + QQmlProfilerEventType(Message message = MaximumMessage, RangeType rangeType = MaximumRangeType, + int detailType = -1, + const QQmlProfilerEventLocation &location = QQmlProfilerEventLocation(), + const QString &data = QString(), const QString displayName = QString()) : + m_displayName(displayName), m_data(data), m_location(location), m_message(message), + m_rangeType(rangeType), m_detailType(detailType) + {} + + void setDisplayName(const QString &displayName) { m_displayName = displayName; } + void setData(const QString &data) { m_data = data; } + void setLocation(const QQmlProfilerEventLocation &location) { m_location = location; } + + ProfileFeature feature() const; + QString displayName() const { return m_displayName; } + QString data() const { return m_data; } + QQmlProfilerEventLocation location() const { return m_location; } + Message message() const { return m_message; } + RangeType rangeType() const { return m_rangeType; } + int detailType() const { return m_detailType; } + +private: + friend QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type); + friend QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type); + + QString m_displayName; + QString m_data; + QQmlProfilerEventLocation m_location; + Message m_message; + RangeType m_rangeType; + int m_detailType; // can be EventType, BindingType, PixmapEventType or SceneGraphFrameType +}; + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerEventType &type); +QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEventType &type); + +inline uint qHash(const QQmlProfilerEventType &type) +{ + return qHash(type.location()) + ^ (((type.message() << 12) & 0xf000) // 4 bits message + | ((type.rangeType() << 24) & 0xf000000) // 4 bits rangeType + | ((static_cast<uint>(type.detailType()) << 28) & 0xf0000000)); // 4 bits detailType +} + +inline bool operator==(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2) +{ + return type1.message() == type2.message() && type1.rangeType() == type2.rangeType() + && type1.detailType() == type2.detailType() && type1.location() == type2.location(); +} + +inline bool operator!=(const QQmlProfilerEventType &type1, const QQmlProfilerEventType &type2) +{ + return !(type1 == type2); +} + +Q_DECLARE_TYPEINFO(QQmlProfilerEventType, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlProfilerEventType) + +#endif // QQMLPROFILEREVENTTYPE_P_H diff --git a/src/qmldebug/qqmlprofilertypedevent.cpp b/src/qmldebug/qqmlprofilertypedevent.cpp new file mode 100644 index 0000000000..9b00481e17 --- /dev/null +++ b/src/qmldebug/qqmlprofilertypedevent.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlprofilertypedevent_p.h" +#include <QtCore/qvarlengtharray.h> + +QT_BEGIN_NAMESPACE + +QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event) +{ + qint64 time; + qint32 messageType; + qint32 subtype; + + stream >> time >> messageType; + + if (messageType < 0 || messageType > QQmlProfilerDefinitions::MaximumMessage) + messageType = QQmlProfilerDefinitions::MaximumMessage; + + QQmlProfilerDefinitions::RangeType rangeType = QQmlProfilerDefinitions::MaximumRangeType; + if (!stream.atEnd()) { + stream >> subtype; + rangeType = static_cast<QQmlProfilerDefinitions::RangeType>(subtype); + if (rangeType < 0 || rangeType > QQmlProfilerDefinitions::MaximumRangeType) + rangeType = QQmlProfilerDefinitions::MaximumRangeType; + } else { + subtype = -1; + } + + event.event.setTimestamp(time > 0 ? time : 0); + event.event.setTypeIndex(-1); + event.serverTypeId = 0; + + switch (messageType) { + case QQmlProfilerDefinitions::Event: { + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype); + switch (subtype) { + case QQmlProfilerDefinitions::StartTrace: + case QQmlProfilerDefinitions::EndTrace: { + QVarLengthArray<qint32> engineIds; + while (!stream.atEnd()) { + qint32 id; + stream >> id; + engineIds << id; + } + event.event.setNumbers<QVarLengthArray<qint32>, qint32>(engineIds); + break; + } + case QQmlProfilerDefinitions::AnimationFrame: { + qint32 frameRate, animationCount; + qint32 threadId; + stream >> frameRate >> animationCount; + if (!stream.atEnd()) + stream >> threadId; + else + threadId = 0; + + event.event.setNumbers<qint32>({frameRate, animationCount, threadId}); + break; + } + case QQmlProfilerDefinitions::Mouse: + case QQmlProfilerDefinitions::Key: + int inputType = (subtype == QQmlProfilerDefinitions::Key + ? QQmlProfilerDefinitions::InputKeyUnknown + : QQmlProfilerDefinitions::InputMouseUnknown); + if (!stream.atEnd()) + stream >> inputType; + qint32 a = -1; + if (!stream.atEnd()) + stream >> a; + qint32 b = -1; + if (!stream.atEnd()) + stream >> b; + + event.event.setNumbers<qint32>({inputType, a, b}); + break; + } + + break; + } + case QQmlProfilerDefinitions::Complete: { + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype); + break; + } + case QQmlProfilerDefinitions::SceneGraphFrame: { + QVarLengthArray<qint64> params; + qint64 param; + + while (!stream.atEnd()) { + stream >> param; + params.push_back(param); + } + + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype); + event.event.setNumbers<QVarLengthArray<qint64>, qint64>(params); + break; + } + case QQmlProfilerDefinitions::PixmapCacheEvent: { + qint32 width = 0, height = 0, refcount = 0; + QString filename; + stream >> filename; + if (subtype == QQmlProfilerDefinitions::PixmapReferenceCountChanged + || subtype == QQmlProfilerDefinitions::PixmapCacheCountChanged) { + stream >> refcount; + } else if (subtype == QQmlProfilerDefinitions::PixmapSizeKnown) { + stream >> width >> height; + refcount = 1; + } + + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype, + QQmlProfilerEventLocation(filename, 0, 0)); + event.event.setNumbers<qint32>({width, height, refcount}); + break; + } + case QQmlProfilerDefinitions::MemoryAllocation: { + qint64 delta; + stream >> delta; + + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype); + event.event.setNumbers<qint64>({delta}); + break; + } + case QQmlProfilerDefinitions::RangeStart: { + if (!stream.atEnd()) { + qint64 typeId; + stream >> typeId; + if (stream.status() == QDataStream::Ok) + event.serverTypeId = typeId; + // otherwise it's the old binding type of 4 bytes + } + + event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1); + event.event.setRangeStage(QQmlProfilerDefinitions::RangeStart); + break; + } + case QQmlProfilerDefinitions::RangeData: { + QString data; + stream >> data; + + event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1, + QQmlProfilerEventLocation(), data); + event.event.setRangeStage(QQmlProfilerDefinitions::RangeData); + if (!stream.atEnd()) + stream >> event.serverTypeId; + break; + } + case QQmlProfilerDefinitions::RangeLocation: { + QString filename; + qint32 line = 0; + qint32 column = 0; + stream >> filename >> line; + + if (!stream.atEnd()) { + stream >> column; + if (!stream.atEnd()) + stream >> event.serverTypeId; + } + + event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1, + QQmlProfilerEventLocation(filename, line, column)); + event.event.setRangeStage(QQmlProfilerDefinitions::RangeLocation); + break; + } + case QQmlProfilerDefinitions::RangeEnd: { + event.type = QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, rangeType, -1); + event.event.setRangeStage(QQmlProfilerDefinitions::RangeEnd); + break; + } + default: + event.event.setNumbers<char>({}); + event.type = QQmlProfilerEventType( + static_cast<QQmlProfilerDefinitions::Message>(messageType), + QQmlProfilerDefinitions::MaximumRangeType, subtype); + break; + } + + return stream; +} + +QT_END_NAMESPACE diff --git a/src/qmldebug/qqmleventlocation_p.h b/src/qmldebug/qqmlprofilertypedevent_p.h index c3a2b93f0f..e7e947f2ef 100644 --- a/src/qmldebug/qqmleventlocation_p.h +++ b/src/qmldebug/qqmlprofilertypedevent_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -37,10 +37,13 @@ ** ****************************************************************************/ -#ifndef QQMLEVENTLOCATION_P_H -#define QQMLEVENTLOCATION_P_H +#ifndef QQMLPROFILERTYPEDEVENT_P_H +#define QQMLPROFILERTYPEDEVENT_P_H -#include <QtCore/qstring.h> +#include "qqmlprofilerevent_p.h" +#include "qqmlprofilereventtype_p.h" + +#include <QtCore/qdatastream.h> // // W A R N I N G @@ -55,19 +58,19 @@ QT_BEGIN_NAMESPACE -struct QQmlEventLocation +struct QQmlProfilerTypedEvent { - QQmlEventLocation() : line(-1), column(-1) {} - QQmlEventLocation(const QString &file, int lineNumber, int columnNumber) : - filename(file), line(lineNumber), column(columnNumber) {} - - QString filename; - int line; - int column; + QQmlProfilerEvent event; + QQmlProfilerEventType type; + qint64 serverTypeId = 0; }; -Q_DECLARE_TYPEINFO(QQmlEventLocation, Q_MOVABLE_TYPE); +QDataStream &operator>>(QDataStream &stream, QQmlProfilerTypedEvent &event); + +Q_DECLARE_TYPEINFO(QQmlProfilerTypedEvent, Q_MOVABLE_TYPE); QT_END_NAMESPACE -#endif // QQMLEVENTLOCATION_P_H +Q_DECLARE_METATYPE(QQmlProfilerTypedEvent) + +#endif // QQMLPROFILERTYPEDEVENT_P_H diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index d2b3f0e1f4..6b5f8fdf4c 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -68,8 +68,8 @@ \endcode Where "example" is the identifier to use to uniquely identify - this set of tests. You should add \c{CONFIG += qmltestcase}. - For example: + this set of tests. Finally, add \c{CONFIG += qmltestcase} to the project + file: \badcode TEMPLATE = app @@ -130,4 +130,56 @@ \badcode tst_example -help \endcode + + \section1 Executing C++ Before QML Tests + + To execute C++ code before any of the QML tests are run, the + \c QUICK_TEST_MAIN_WITH_SETUP macro can be used. This can be useful for + setting context properties on the QML engine, amongst other things. + + The macro is identical to \l QUICK_TEST_MAIN, except that it takes an + additional \c QObject* argument. The test framework will call slots and + invokable functions with the following names: + + \table + \header + \li Name + \li Purpose + \row + \li void qmlEngineAvailable(QQmlEngine*) + \li Called when the QML engine is available. + Any \l {QQmlEngine::addImportPath}{import paths}, + \l {QQmlEngine::addPluginPath}{plugin paths}, + and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors} + will have been set on the engine by this point. + \endtable + + Each function will be called once for each \c tst_*.qml file, so any + arguments are unique to that test. For example, this means that each QML + test file will have its own QML engine. + + The following example demonstrates how the macro can be used to set context + properties on the QML engine: + + \code + #include <QtQuickTest> + #include <QQmlEngine> + #include <QQmlContext> + + class Setup : public QObject + { + public: + Setup() {} + + public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + engine->rootContext()->setContextProperty("myContextProperty", QVariant(true)); + } + }; + + QUICK_TEST_MAIN_WITH_SETUP(mytest, Setup) + + #include "tst_mytest.moc" + \endcode */ diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index 463833a100..817f9a5389 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -327,6 +327,11 @@ private: int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir) { + return quick_test_main_with_setup(argc, argv, name, sourceDir, nullptr); +} + +int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup) +{ // Peek at arguments to check for '-widgets' argument #ifdef QT_QMLTEST_WITH_WIDGETS bool withWidgets = false; @@ -516,6 +521,14 @@ int quick_test_main(int argc, char **argv, const char *name, const char *sourceD view.rootContext()->setContextProperty (QLatin1String("qtest"), QTestRootObject::instance()); // Deprecated. Use QTestRootObject from Qt.test.qtestroot instead + // Do this down here so that import paths, plugin paths, + // file selectors, etc. are available in case the user needs access to them. + if (setup) { + // Don't check the return value; it's OK if it doesn't exist. + // If we add more callbacks in the future, it makes sense if the user only implements one of them. + QMetaObject::invokeMethod(setup, "qmlEngineAvailable", Q_ARG(QQmlEngine*, view.engine())); + } + view.setObjectName(fi.baseName()); view.setTitle(view.objectName()); QTestRootObject::instance()->init(); diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h index 6486accb9e..62ed749722 100644 --- a/src/qmltest/quicktest.h +++ b/src/qmltest/quicktest.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir); +Q_QUICK_TEST_EXPORT int quick_test_main_with_setup(int argc, char **argv, const char *name, const char *sourceDir, QObject *setup); #ifdef QUICK_TEST_SOURCE_DIR @@ -67,6 +68,15 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, return quick_test_main(argc, argv, #name, QUICK_TEST_SOURCE_DIR); \ } +#define QUICK_TEST_MAIN_WITH_SETUP(name, QuickTestSetupClass) \ + int main(int argc, char **argv) \ + { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ + QuickTestSetupClass setup; \ + return quick_test_main_with_setup(argc, argv, #name, QUICK_TEST_SOURCE_DIR, &setup); \ + } + #else #define QUICK_TEST_MAIN(name) \ @@ -74,7 +84,7 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, { \ QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ - return quick_test_main(argc, argv, #name, 0); \ + return quick_test_main(argc, argv, #name, nullptr); \ } #define QUICK_TEST_OPENGL_MAIN(name) \ @@ -82,7 +92,16 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, { \ QTEST_ADD_GPU_BLACKLIST_SUPPORT \ QTEST_SET_MAIN_SOURCE_PATH \ - return quick_test_main(argc, argv, #name, 0); \ + return quick_test_main(argc, argv, #name, nullptr); \ + } + +#define QUICK_TEST_MAIN_WITH_SETUP(name, QuickTestSetupClass) \ + int main(int argc, char **argv) \ + { \ + QTEST_ADD_GPU_BLACKLIST_SUPPORT \ + QTEST_SET_MAIN_SOURCE_PATH \ + QuickTestSetupClass setup; \ + return quick_test_main_with_setup(argc, argv, #name, nullptr, &setup); \ } #endif diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 8a2282f5c6..d7d692a80d 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -143,7 +143,7 @@ public Q_SLOTS: QImageWriter writer(filePath); if (!writer.write(m_image)) { QQmlEngine *engine = qmlContext(this)->engine(); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle()); + QV4::ExecutionEngine *v4 = engine->handle(); v4->throwError(QStringLiteral("Can't save to %1: %2").arg(filePath, writer.errorString())); } } diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp index 33ea442b76..63364192c6 100644 --- a/src/quick/designer/qqmldesignermetaobject.cpp +++ b/src/quick/designer/qqmldesignermetaobject.cpp @@ -127,7 +127,7 @@ void QQmlDesignerMetaObject::init(QObject *object, QQmlEngine *engine) } QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engine) - : QQmlVMEMetaObject(QQmlEnginePrivate::getV4Engine(engine), object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1), + : QQmlVMEMetaObject(engine->handle(), object, cacheForObject(object, engine), /*qml compilation unit*/nullptr, /*qmlObjectId*/-1), m_context(engine->contextForObject(object)), m_data(new MetaPropertyData) { diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 57936d8cec..0739889e67 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -727,7 +727,7 @@ void QQuickCanvasItem::updatePolish() QMap<int, QV4::PersistentValue> animationCallbacks = d->animationCallbacks; d->animationCallbacks.clear(); - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(qmlEngine(this)); + QV4::ExecutionEngine *v4 = qmlEngine(this)->handle(); QV4::Scope scope(v4); QV4::ScopedFunctionObject function(scope); QV4::JSCallData jsCall(scope, 1); @@ -1210,7 +1210,7 @@ void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVa d->context = context; d->context->init(this, args); - d->context->setV4Engine(QQmlEnginePrivate::get(qmlEngine(this))->v4engine()); + d->context->setV4Engine(qmlEngine(this)->handle()); connect(d->context, SIGNAL(textureChanged()), SLOT(update())); connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted())); emit contextChanged(); diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index d5601292b7..1f2828585c 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -664,7 +664,7 @@ void QQuickLoaderPrivate::setInitialState(QObject *obj) QQmlComponentPrivate *d = QQmlComponentPrivate::get(component); Q_ASSERT(d && d->engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(d->engine); + QV4::ExecutionEngine *v4 = d->engine->handle(); Q_ASSERT(v4); QV4::Scope scope(v4); QV4::ScopedValue ipv(scope, initialPropertyValues.value()); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 8f3a8998f5..f8363b1e5a 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1968,12 +1968,11 @@ void QQuickTextEdit::triggerPreprocess() } typedef QQuickTextEditPrivate::Node TextNode; -typedef QList<TextNode*>::iterator TextNodeIterator; +using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator; - -static bool comesBefore(TextNode* n1, TextNode* n2) +static inline bool operator<(const TextNode &n1, const TextNode &n2) { - return n1->startPos() < n2->startPos(); + return n1.startPos() < n2.startPos(); } static inline void updateNodeTransform(QQuickTextNode* node, const QPointF &topLeft) @@ -2026,13 +2025,12 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * if (!oldNode) { // If we had any QQuickTextNode node references, they were deleted along with the root node // But here we must delete the Node structures in textNodeMap - qDeleteAll(d->textNodeMap); d->textNodeMap.clear(); } RootNode *rootNode = static_cast<RootNode *>(oldNode); TextNodeIterator nodeIterator = d->textNodeMap.begin(); - while (nodeIterator != d->textNodeMap.end() && !(*nodeIterator)->dirty()) + while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty()) ++nodeIterator; QQuickTextNodeEngine engine; @@ -2045,13 +2043,12 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * int firstDirtyPos = 0; if (nodeIterator != d->textNodeMap.end()) { - firstDirtyPos = (*nodeIterator)->startPos(); + firstDirtyPos = nodeIterator->startPos(); do { - rootNode->removeChildNode((*nodeIterator)->textNode()); - delete (*nodeIterator)->textNode(); - delete *nodeIterator; + rootNode->removeChildNode(nodeIterator->textNode()); + delete nodeIterator->textNode(); nodeIterator = d->textNodeMap.erase(nodeIterator); - } while (nodeIterator != d->textNodeMap.end() && (*nodeIterator)->dirty()); + } while (nodeIterator != d->textNodeMap.end() && nodeIterator->dirty()); } // FIXME: the text decorations could probably be handled separately (only updated for affected textFrames) @@ -2068,7 +2065,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * rootNode->setMatrix(basePositionMatrix); QPointF nodeOffset; - TextNode *firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator : 0; + const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator + : TextNode(); QList<QTextFrame *> frames; frames.append(d->document->rootFrame()); @@ -2078,7 +2076,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * frames.append(textFrame->childFrames()); frameDecorationsEngine.addFrameDecorations(d->document, textFrame); - if (textFrame->lastPosition() < firstDirtyPos || (firstCleanNode && textFrame->firstPosition() >= firstCleanNode->startPos())) + if (textFrame->lastPosition() < firstDirtyPos + || textFrame->firstPosition() >= firstCleanNode.startPos()) continue; node = d->createTextNode(); resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); @@ -2118,8 +2117,8 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1); currentNodeSize += block.length(); - if ((it.atEnd()) || (firstCleanNode && block.next().position() >= firstCleanNode->startPos())) // last node that needed replacing or last block of the frame - break; + if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos()) + break; // last node that needed replacing or last block of the frame QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position()); if (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart) { @@ -2137,16 +2136,19 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * // Now prepend the frame decorations since we want them rendered first, with the text nodes and cursor in front. rootNode->prependChildNode(rootNode->frameDecorationsNode); - Q_ASSERT(nodeIterator == d->textNodeMap.end() || (*nodeIterator) == firstCleanNode); + Q_ASSERT(nodeIterator == d->textNodeMap.end() + || (nodeIterator->textNode() == firstCleanNode.textNode() + && nodeIterator->startPos() == firstCleanNode.startPos())); // Update the position of the subsequent text blocks. - if (firstCleanNode) { - QPointF oldOffset = firstCleanNode->textNode()->matrix().map(QPointF(0,0)); - QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(d->document->findBlock(firstCleanNode->startPos())).topLeft(); + if (firstCleanNode.textNode() != nullptr) { + QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0)); + QPointF currentOffset = d->document->documentLayout()->blockBoundingRect( + d->document->findBlock(firstCleanNode.startPos())).topLeft(); QPointF delta = currentOffset - oldOffset; while (nodeIterator != d->textNodeMap.end()) { - QMatrix4x4 transformMatrix = (*nodeIterator)->textNode()->matrix(); + QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix(); transformMatrix.translate(delta.x(), delta.y()); - (*nodeIterator)->textNode()->setMatrix(transformMatrix); + nodeIterator->textNode()->setMatrix(transformMatrix); ++nodeIterator; } @@ -2154,7 +2156,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * // Since we iterate over blocks from different text frames that are potentially not sorted // we need to ensure that our list of nodes is sorted again: - std::sort(d->textNodeMap.begin(), d->textNodeMap.end(), &comesBefore); + std::sort(d->textNodeMap.begin(), d->textNodeMap.end()); } if (d->cursorComponent == 0) { @@ -2333,22 +2335,26 @@ void QQuickTextEdit::markDirtyNodesForRange(int start, int end, int charDelta) if (start == end) return; - TextNode dummyNode(start, 0); - TextNodeIterator it = std::lower_bound(d->textNodeMap.begin(), d->textNodeMap.end(), &dummyNode, &comesBefore); + TextNode dummyNode(start); + + const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin(); + const TextNodeIterator textNodeMapEnd = d->textNodeMap.end(); + + TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode); // qLowerBound gives us the first node past the start of the affected portion, rewind to the first node // that starts at the last position before the edit position. (there might be several because of images) - if (it != d->textNodeMap.begin()) { + if (it != textNodeMapBegin) { --it; - TextNode otherDummy((*it)->startPos(), 0); - it = std::lower_bound(d->textNodeMap.begin(), d->textNodeMap.end(), &otherDummy, &comesBefore); + TextNode otherDummy(it->startPos()); + it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy); } // mark the affected nodes as dirty - while (it != d->textNodeMap.end()) { - if ((*it)->startPos() <= end) - (*it)->setDirty(); + while (it != textNodeMapEnd) { + if (it->startPos() <= end) + it->setDirty(); else if (charDelta) - (*it)->moveStartPos(charDelta); + it->moveStartPos(charDelta); else return; ++it; @@ -2533,8 +2539,8 @@ void QQuickTextEdit::updateWholeDocument() { Q_D(QQuickTextEdit); if (!d->textNodeMap.isEmpty()) { - for (TextNode* node : qAsConst(d->textNodeMap)) - node->setDirty(); + for (TextNode &node : d->textNodeMap) + node.setDirty(); } polish(); @@ -2678,7 +2684,7 @@ void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event) void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QQuickTextNode *node, TextNodeIterator &it, int startPos) { engine->addToSceneGraph(node, QQuickText::Normal, QColor()); - it = textNodeMap.insert(it, new TextNode(startPos, node)); + it = textNodeMap.insert(it, TextNode(startPos, node)); ++it; root->appendChildNode(node); } diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 09718cb49a..ef2bdfd0ea 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -59,6 +59,8 @@ #include <QtCore/qlist.h> #include <private/qlazilyallocated_p.h> +#include <limits> + QT_BEGIN_NAMESPACE class QTextLayout; class QQuickTextDocumentWithImageResources; @@ -74,7 +76,8 @@ public: typedef QQuickTextEdit Public; struct Node { - explicit Node(int startPos, QQuickTextNode* node) + explicit Node(int startPos = std::numeric_limits<int>::max(), + QQuickTextNode *node = nullptr) : m_startPos(startPos), m_node(node), m_dirty(false) { } QQuickTextNode* textNode() const { return m_node; } void moveStartPos(int delta) { Q_ASSERT(m_startPos + delta > 0); m_startPos += delta; } @@ -87,7 +90,7 @@ public: QQuickTextNode* m_node; bool m_dirty; }; - typedef QList<Node*>::iterator TextNodeIterator; + typedef QList<Node>::iterator TextNodeIterator; struct ExtraData { ExtraData(); @@ -128,11 +131,6 @@ public: { } - ~QQuickTextEditPrivate() - { - qDeleteAll(textNodeMap); - } - static QQuickTextEditPrivate *get(QQuickTextEdit *item) { return static_cast<QQuickTextEditPrivate *>(QObjectPrivate::get(item)); } @@ -186,7 +184,7 @@ public: QQuickTextDocumentWithImageResources *document; QQuickTextControl *control; QQuickTextDocument *quickDocument; - QList<Node*> textNodeMap; + QList<Node> textNodeMap; int lastSelectionStart; int lastSelectionEnd; diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 9a29d6c2ca..ff9789ad57 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -70,7 +70,7 @@ void QQuickViewPrivate::init(QQmlEngine* e) { // The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS // wrapper so that the garbage collector can see the policy. - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine.data()); + QV4::ExecutionEngine *v4 = engine.data()->handle(); QV4::QObjectWrapper::wrap(v4, contentItem); } } diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 6165ce1ba1..f048f12bf9 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -114,7 +114,7 @@ void QQuickWindowQmlImpl::classBegin() { // The content item has CppOwnership policy (set in QQuickWindow). Ensure the presence of a JS // wrapper so that the garbage collector can see the policy. - QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(e); + QV4::ExecutionEngine *v4 = e->handle(); QV4::QObjectWrapper::wrap(v4, d->contentItem); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp index f6898b3879..bf3141bc32 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -227,8 +227,8 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) { //We can only check for a device pixel ratio change when we know what //paint device is being used. - if (painter->device()->devicePixelRatio() != m_devicePixelRatio) { - m_devicePixelRatio = painter->device()->devicePixelRatio(); + if (!qFuzzyCompare(painter->device()->devicePixelRatioF(), m_devicePixelRatio)) { + m_devicePixelRatio = painter->device()->devicePixelRatioF(); generateCornerPixmap(); } @@ -245,7 +245,7 @@ void QSGSoftwareInternalRectangleNode::paint(QPainter *painter) } else { //Rounded Rects and Rects with Borders //Avoids broken behaviors of QPainter::drawRect/roundedRect - QPixmap pixmap = QPixmap(m_rect.width() * m_devicePixelRatio, m_rect.height() * m_devicePixelRatio); + QPixmap pixmap = QPixmap(qRound(m_rect.width() * m_devicePixelRatio), qRound(m_rect.height() * m_devicePixelRatio)); pixmap.fill(Qt::transparent); pixmap.setDevicePixelRatio(m_devicePixelRatio); QPainter pixmapPainter(&pixmap); @@ -356,7 +356,7 @@ void QSGSoftwareInternalRectangleNode::paintRectangle(QPainter *painter, const Q } else { //blit 4 corners to border - int scaledRadius = radius * m_devicePixelRatio; + int scaledRadius = qRound(radius * m_devicePixelRatio); QRectF topLeftCorner(QPointF(rect.x(), rect.y()), QPointF(rect.x() + radius, rect.y() + radius)); painter->drawPixmap(topLeftCorner, m_cornerPixmap, QRectF(0, 0, scaledRadius, scaledRadius)); @@ -416,7 +416,7 @@ void QSGSoftwareInternalRectangleNode::generateCornerPixmap() //Generate new corner Pixmap int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); - m_cornerPixmap = QPixmap(radius * 2 * m_devicePixelRatio, radius * 2 * m_devicePixelRatio); + m_cornerPixmap = QPixmap(qRound(radius * 2 * m_devicePixelRatio), qRound(radius * 2 * m_devicePixelRatio)); m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); m_cornerPixmap.fill(Qt::transparent); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h index f363e279e1..1f87424d2a 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h @@ -95,7 +95,7 @@ private: bool m_cornerPixmapIsDirty; QPixmap m_cornerPixmap; - int m_devicePixelRatio; + qreal m_devicePixelRatio; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp new file mode 100644 index 0000000000..301f2826dc --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcompressedatlastexture_p.h" + +#include <QtCore/QVarLengthArray> +#include <QtCore/QElapsedTimer> +#include <QtCore/QtMath> + +#include <QtGui/QOpenGLContext> +#include <QtGui/QGuiApplication> +#include <QtGui/QScreen> +#include <QtGui/QSurface> +#include <QtGui/QWindow> +#include <QtGui/QOpenGLFunctions> +#include <QtGui/QOpenGLTexture> +#include <QDebug> + +#include <private/qqmlglobal_p.h> +#include <private/qquickprofiler_p.h> +#include <private/qsgtexture_p.h> +#include <private/qsgcompressedtexture_p.h> +#include <private/qsgpkmhandler_p.h> + +QT_BEGIN_NAMESPACE + +static QElapsedTimer qsg_renderer_timer; + +namespace QSGCompressedAtlasTexture +{ + +Atlas::Atlas(const QSize &size, uint format) + : QSGAtlasTexture::AtlasBase(size) + , m_format(format) +{ +} + +Atlas::~Atlas() +{ +} + +Texture *Atlas::create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize) +{ + // No need to lock, as manager already locked it. + QRect rect = m_allocator.allocate(paddedSize); + if (rect.width() > 0 && rect.height() > 0) { + Texture *t = new Texture(this, rect, data, dataLength, dataOffset, size); + m_pending_uploads << t; + return t; + } + return 0; +} + +void Atlas::generateTexture() +{ + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_format, + m_size.width(), m_size.height(), 0, + (m_size.width() * m_size.height()) / 2, + 0); +} + +void Atlas::uploadPendingTexture(int i) +{ + Texture *texture = static_cast<Texture*>(m_pending_uploads.at(i)); + + const QRect &r = texture->atlasSubRect(); + + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + funcs->glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, + r.x(), r.y(), r.width(), r.height(), m_format, + texture->sizeInBytes(), + texture->data().constData() + texture->dataOffset()); + + qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "compressed atlastexture uploaded in: " << qsg_renderer_timer.elapsed() + << "ms (" << texture->textureSize().width() << "x" + << texture->textureSize().height() << ")"; + + // TODO: consider releasing the data (as is done in the regular atlas)? + // The advantage of keeping this data around is that it makes it much easier + // to remove the texture from the atlas +} + +Texture::Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size) + : QSGAtlasTexture::TextureBase(atlas, textureRect) + , m_nonatlas_texture(0) + , m_data(data) + , m_size(size) + , m_dataLength(dataLength) + , m_dataOffset(dataOffset) +{ + float w = atlas->size().width(); + float h = atlas->size().height(); + QRect nopad = atlasSubRect(); + // offset by half-pixel to prevent bleeding when scaling + m_texture_coords_rect = QRectF((nopad.x() + .5) / w, + (nopad.y() + .5) / h, + (nopad.width() - 1.) / w, + (nopad.height() - 1.) / h); +} + +Texture::~Texture() +{ + delete m_nonatlas_texture; +} + +bool Texture::hasAlphaChannel() const +{ + return QSGCompressedTexture::formatIsOpaque(static_cast<Atlas*>(m_atlas)->format()); +} + +QSGTexture *Texture::removedFromAtlas() const +{ + if (m_nonatlas_texture) { + m_nonatlas_texture->setMipmapFiltering(mipmapFiltering()); + m_nonatlas_texture->setFiltering(filtering()); + return m_nonatlas_texture; + } + + if (!m_data.isEmpty()) { + QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create()); + texData->data = m_data; + texData->size = m_size; + texData->format = static_cast<Atlas*>(m_atlas)->format(); + texData->hasAlpha = hasAlphaChannel(); + texData->dataLength = m_dataLength; + texData->dataOffset = m_dataOffset; + m_nonatlas_texture = new QSGCompressedTexture(texData); + m_nonatlas_texture->setMipmapFiltering(mipmapFiltering()); + m_nonatlas_texture->setFiltering(filtering()); + } + + return m_nonatlas_texture; +} + +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h new file mode 100644 index 0000000000..59e935b623 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGCOMPRESSEDATLASTEXTURE_P_H +#define QSGCOMPRESSEDATLASTEXTURE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QSize> + +#include <QtGui/qopengl.h> + +#include <QtQuick/QSGTexture> +#include <QtQuick/private/qsgareaallocator_p.h> +#include <QtQuick/private/qsgatlastexture_p.h> + +QT_BEGIN_NAMESPACE + +class QSGCompressedTextureFactory; + +namespace QSGCompressedAtlasTexture { + +class Texture; + +class Atlas : public QSGAtlasTexture::AtlasBase +{ +public: + Atlas(const QSize &size, uint format); + ~Atlas(); + + void generateTexture() override; + void uploadPendingTexture(int i) override; + + Texture *create(const QByteArray &data, int dataLength, int dataOffset, const QSize &size, const QSize &paddedSize); + + uint format() const { return m_format; } + +private: + uint m_format; +}; + +class Texture : public QSGAtlasTexture::TextureBase +{ + Q_OBJECT +public: + Texture(Atlas *atlas, const QRect &textureRect, const QByteArray &data, int dataLength, int dataOffset, const QSize &size); + ~Texture(); + + QSize textureSize() const override { return m_size; } + bool hasAlphaChannel() const override; + bool hasMipmaps() const override { return false; } + + QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; } + + QSGTexture *removedFromAtlas() const override; + + const QByteArray &data() const { return m_data; } + int sizeInBytes() const { return m_dataLength; } + int dataOffset() const { return m_dataOffset; } + +private: + QRectF m_texture_coords_rect; + mutable QSGTexture *m_nonatlas_texture; + QByteArray m_data; + QSize m_size; + int m_dataLength; + int m_dataOffset; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp index 6d51ed9d61..839c562989 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp @@ -43,6 +43,7 @@ #include <QOpenGLTexture> #include <QOpenGLFunctions> #include <QDebug> +#include <QtQuick/private/qquickwindow_p.h> QT_BEGIN_NAMESPACE @@ -215,11 +216,17 @@ QSGCompressedTextureFactory::QSGCompressedTextureFactory(const QSGCompressedText { } -QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *) const +QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *window) const { if (!m_textureData || !m_textureData->isValid()) return nullptr; + // attempt to atlas the texture + QSGRenderContext *context = QQuickWindowPrivate::get(window)->context; + QSGTexture *t = context->compressedTextureForFactory(this); + if (t) + return t; + return new QSGCompressedTexture(m_textureData); } diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h index dfedac5558..aa87316809 100644 --- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h +++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h @@ -103,6 +103,9 @@ protected: bool m_uploaded = false; }; +namespace QSGAtlasTexture { + class Manager; +} class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory { @@ -114,6 +117,8 @@ public: protected: QSGCompressedTexture::DataPtr m_textureData; +private: + friend class QSGAtlasTexture::Manager; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp new file mode 100644 index 0000000000..e3e4ca6824 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgktxhandler_p.h" +#include "qsgcompressedtexture_p.h" +#include <QOpenGLTexture> +#include <QtEndian> + +//#define KTX_DEBUG + +QT_BEGIN_NAMESPACE + +#define KTX_IDENTIFIER_LENGTH 12 +static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' }; +static const quint32 platformEndianIdentifier = 0x04030201; +static const quint32 inversePlatformEndianIdentifier = 0x01020304; + +struct KTXHeader { + quint8 identifier[KTX_IDENTIFIER_LENGTH]; //Must match ktxIdentifier + quint32 endianness; //Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed. + quint32 glType; + quint32 glTypeSize; + quint32 glFormat; + quint32 glInternalFormat; + quint32 glBaseInternalFormat; + quint32 pixelWidth; + quint32 pixelHeight; + quint32 pixelDepth; + quint32 numberOfArrayElements; + quint32 numberOfFaces; + quint32 numberOfMipmapLevels; + quint32 bytesOfKeyValueData; +}; + +static const int headerSize = sizeof(KTXHeader); + +// Currently unused, declared for future reference +struct KTXKeyValuePairItem { + quint32 keyAndValueByteSize; + /* + quint8 keyAndValue[keyAndValueByteSize]; + quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)]; + */ +}; + +struct KTXMipmapLevel { + quint32 imageSize; + /* + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + quint8 mipPadding[3 - ((imageSize + 3) % 4)] + */ +}; + +bool QSGKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block) +{ + Q_UNUSED(suffix) + + return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0); +} + +QQuickTextureFactory *QSGKtxHandler::read() +{ + if (!device()) + return nullptr; + + QByteArray buf = device()->readAll(); + if (buf.size() < headerSize || !canRead(QByteArray(), buf)) { + qCDebug(QSG_LOG_TEXTUREIO, "Invalid KTX file %s", logName().constData()); + return nullptr; + } + + const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.constData()); + if (!checkHeader(*header)) { + qCDebug(QSG_LOG_TEXTUREIO, "Unsupported KTX file format in %s", logName().constData()); + return nullptr; + } + + QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create()); + + texData->size = QSize(decode(header->pixelWidth), decode(header->pixelHeight)); + texData->format = decode(header->glInternalFormat); + texData->hasAlpha = !QSGCompressedTexture::formatIsOpaque(texData->format); + + // For now, ignore any additional mipmap levels + int preambleSize = headerSize + decode(header->bytesOfKeyValueData); + if (buf.size() >= preambleSize + int(sizeof(KTXMipmapLevel))) { + texData->data = buf; + texData->dataOffset = preambleSize + sizeof(quint32); // for the imageSize + const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + preambleSize); + texData->dataLength = decode(level->imageSize); + } + + if (!texData->isValid()) { + qCDebug(QSG_LOG_TEXTUREIO, "Invalid values in header of KTX file %s", logName().constData()); + return nullptr; + } + + texData->logName = logName(); +#ifdef KTX_DEBUG + qDebug() << "KTX file handler read" << texData.data(); +#endif + + return new QSGCompressedTextureFactory(texData); +} + +bool QSGKtxHandler::checkHeader(const KTXHeader &header) +{ + if (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier) + return false; + inverseEndian = (header.endianness == inversePlatformEndianIdentifier); +#ifdef KTX_DEBUG + QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>(); + QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>(); + qDebug("Header of %s:", logName().constData()); + qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType))); + qDebug(" glTypeSize: %u", decode(header.glTypeSize)); + qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), tfme.valueToKey(decode(header.glFormat))); + qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), tfme.valueToKey(decode(header.glInternalFormat))); + qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), tfme.valueToKey(decode(header.glBaseInternalFormat))); + qDebug(" pixelWidth: %u", decode(header.pixelWidth)); + qDebug(" pixelHeight: %u", decode(header.pixelHeight)); + qDebug(" pixelDepth: %u", decode(header.pixelDepth)); + qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements)); + qDebug(" numberOfFaces: %u", decode(header.numberOfFaces)); + qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels)); + qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData)); +#endif + return ((decode(header.glType) == 0) && + (decode(header.glFormat) == 0) && + (decode(header.pixelDepth) == 0) && + (decode(header.numberOfFaces) == 1)); +} + +quint32 QSGKtxHandler::decode(quint32 val) +{ + return inverseEndian ? qbswap<quint32>(val) : val; +} + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h b/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h new file mode 100644 index 0000000000..22f4db65b2 --- /dev/null +++ b/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGKTXHANDLER_H +#define QSGKTXHANDLER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qsgtexturefilehandler_p.h" + +QT_BEGIN_NAMESPACE + +struct KTXHeader; + +class QSGKtxHandler : public QSGTextureFileHandler +{ +public: + using QSGTextureFileHandler::QSGTextureFileHandler; + + static bool canRead(const QByteArray &suffix, const QByteArray &block); + + QQuickTextureFactory *read() override; + +private: + bool checkHeader(const KTXHeader &header); + quint32 decode(quint32 val); + + bool inverseEndian = false; +}; + +QT_END_NAMESPACE + +#endif // QSGKTXHANDLER_H diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index fb66a6ebb1..e5334d59e1 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -404,6 +404,20 @@ void QSGRenderContext::textureFactoryDestroyed(QObject *o) m_mutex.unlock(); } +/*! + Return the texture corresponding to a texture factory. + + This may optionally manipulate the texture in some way; for example by returning + an atlased texture. + + This function is not a replacement for textureForFactory; both should be used + for a single texture (this might atlas, while the other might cache). +*/ +QSGTexture *QSGRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *) const +{ + return nullptr; +} + #include "qsgcontext.moc" #include "moc_qsgcontext_p.cpp" diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 84a2523f26..da0adcd5d7 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -78,6 +78,7 @@ class QSGMaterial; class QSGRenderLoop; class QSGLayer; class QQuickTextureFactory; +class QSGCompressedTextureFactory; class QSGContext; class QQuickPaintedItem; class QSGRendererInterface; @@ -173,6 +174,7 @@ public: virtual QSGTexture *createTexture(const QImage &image, uint flags = CreateTexture_Alpha) const = 0; virtual QSGRenderer *createRenderer() = 0; + virtual QSGTexture *compressedTextureForFactory(const QSGCompressedTextureFactory *) const; virtual void setAttachToGraphicsContext(bool attach) { Q_UNUSED(attach); } diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 95f3555994..12357f12c7 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -44,6 +44,7 @@ #include <QtQuick/private/qsgbatchrenderer_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgatlastexture_p.h> +#include <QtQuick/private/qsgcompressedtexture_p.h> #include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> QT_BEGIN_NAMESPACE @@ -243,6 +244,14 @@ QSGRenderer *QSGDefaultRenderContext::createRenderer() return new QSGBatchRenderer::Renderer(this); } +QSGTexture *QSGDefaultRenderContext::compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const +{ + // The atlas implementation is only supported from the render thread + if (openglContext() && QThread::currentThread() == openglContext()->thread()) + return m_atlasManager->create(factory); + return nullptr; +} + /*! Compile \a shader, optionally using \a vertexCode and \a fragmentCode as replacement for the source code supplied by \a shader. diff --git a/src/quick/scenegraph/qsgdefaultrendercontext_p.h b/src/quick/scenegraph/qsgdefaultrendercontext_p.h index 2537a06988..68329256f1 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext_p.h +++ b/src/quick/scenegraph/qsgdefaultrendercontext_p.h @@ -84,6 +84,7 @@ public: QSGTexture *createTexture(const QImage &image, uint flags) const override; QSGRenderer *createRenderer() override; + QSGTexture *compressedTextureForFactory(const QSGCompressedTextureFactory *factory) const override; virtual void compileShader(QSGMaterialShader *shader, QSGMaterial *material, const char *vertexCode = 0, const char *fragmentCode = 0); virtual void initializeShader(QSGMaterialShader *shader); diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 377a4647da..34396dcf43 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -230,11 +230,15 @@ SOURCES += \ qtConfig(opengl(es1|es2)?) { HEADERS += \ + $$PWD/compressedtexture/qsgcompressedatlastexture_p.h \ $$PWD/compressedtexture/qsgcompressedtexture_p.h \ $$PWD/compressedtexture/qsgtexturefilehandler_p.h \ - $$PWD/compressedtexture/qsgpkmhandler_p.h + $$PWD/compressedtexture/qsgpkmhandler_p.h \ + $$PWD/compressedtexture/qsgktxhandler_p.h SOURCES += \ + $$PWD/compressedtexture/qsgcompressedatlastexture.cpp \ $$PWD/compressedtexture/qsgcompressedtexture.cpp \ - $$PWD/compressedtexture/qsgpkmhandler.cpp + $$PWD/compressedtexture/qsgpkmhandler.cpp \ + $$PWD/compressedtexture/qsgktxhandler.cpp } diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 22f0b13f46..9d37746fff 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -44,6 +44,7 @@ #include <QtCore/QtMath> #include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> #include <QtGui/QOpenGLFunctions> #include <QtGui/QGuiApplication> #include <QtGui/QScreen> @@ -52,6 +53,8 @@ #include <QtGui/qpa/qplatformnativeinterface.h> #include <private/qsgtexture_p.h> +#include <private/qsgcompressedtexture_p.h> +#include <private/qsgcompressedatlastexture_p.h> #include <private/qquickprofiler_p.h> @@ -65,6 +68,8 @@ int qt_sg_envInt(const char *name, int defaultValue); static QElapsedTimer qsg_renderer_timer; +DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS) + namespace QSGAtlasTexture { @@ -100,6 +105,7 @@ Manager::Manager() Manager::~Manager() { Q_ASSERT(m_atlas == 0); + Q_ASSERT(m_atlases.isEmpty()); } void Manager::invalidate() @@ -109,6 +115,14 @@ void Manager::invalidate() m_atlas->deleteLater(); m_atlas = 0; } + + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin(); + while (i != m_atlases.end()) { + i.value()->invalidate(); + i.value()->deleteLater(); + ++i; + } + m_atlases.clear(); } QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) @@ -125,13 +139,147 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel) return t; } -Atlas::Atlas(const QSize &size) +QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory) +{ + QSGTexture *t = 0; + if (!qsgEnableCompressedAtlas() || !factory->m_textureData || !factory->m_textureData->isValid()) + return t; + + // TODO: further abstract the atlas and remove this restriction + unsigned int format = factory->m_textureData->format; + switch (format) { + case QOpenGLTexture::RGB8_ETC1: + case QOpenGLTexture::RGB8_ETC2: + case QOpenGLTexture::RGBA8_ETC2_EAC: + case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2: + break; + default: + return t; + } + + QSize size = factory->m_textureData->size; + if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) { + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format); + if (i == m_atlases.end()) + i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format)); + // must be multiple of 4 + QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4); + QByteArray data = factory->m_textureData->data; + t = i.value()->create(data, factory->m_textureData->sizeInBytes(), factory->m_textureData->dataOffset, size, paddedSize); + } + return t; +} + +AtlasBase::AtlasBase(const QSize &size) : m_allocator(size) , m_texture_id(0) , m_size(size) - , m_atlas_transient_image_threshold(0) , m_allocated(false) { +} + +AtlasBase::~AtlasBase() +{ + Q_ASSERT(!m_texture_id); +} + +void AtlasBase::invalidate() +{ + if (m_texture_id && QOpenGLContext::currentContext()) + QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; +} + +int AtlasBase::textureId() const +{ + if (!m_texture_id) { + Q_ASSERT(QOpenGLContext::currentContext()); + QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<AtlasBase *>(this)->m_texture_id); + } + + return m_texture_id; +} + +void AtlasBase::bind(QSGTexture::Filtering filtering) +{ + QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); + if (!m_allocated) { + m_allocated = true; + + while (funcs->glGetError() != GL_NO_ERROR) ; + + funcs->glGenTextures(1, &m_texture_id); + funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#if !defined(QT_OPENGL_ES_2) + if (!QOpenGLContext::currentContext()->isOpenGLES()) + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); +#endif + generateTexture(); + + GLenum errorCode = funcs->glGetError(); + if (errorCode == GL_OUT_OF_MEMORY) { + qDebug("QSGTextureAtlas: texture atlas allocation failed, out of memory"); + funcs->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + } else if (errorCode != GL_NO_ERROR) { + qDebug("QSGTextureAtlas: texture atlas allocation failed, code=%x", errorCode); + funcs->glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + } + } else { + funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); + } + + if (m_texture_id == 0) + return; + + // Upload all pending images.. + for (int i=0; i<m_pending_uploads.size(); ++i) { + + bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled(); + if (profileFrames) + qsg_renderer_timer.start(); + + Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); + + // Skip bind, convert, swizzle; they're irrelevant + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareStart, 3); + + uploadPendingTexture(i); + + Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareUpload); + + // Skip mipmap; unused + Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareUpload, 1); + Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare, + QQuickProfiler::SceneGraphTexturePrepareMipmap); + } + + GLenum f = filtering == QSGTexture::Nearest ? GL_NEAREST : GL_LINEAR; + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, f); + funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, f); + + m_pending_uploads.clear(); +} + +void AtlasBase::remove(TextureBase *t) +{ + QRect atlasRect = t->atlasSubRect(); + m_allocator.deallocate(atlasRect); + m_pending_uploads.removeOne(t); +} + +Atlas::Atlas(const QSize &size) + : AtlasBase(size) + , m_atlas_transient_image_threshold(0) +{ m_internalFormat = GL_RGBA; m_externalFormat = GL_BGRA; @@ -188,14 +336,6 @@ Atlas::Atlas(const QSize &size) Atlas::~Atlas() { - Q_ASSERT(!m_texture_id); -} - -void Atlas::invalidate() -{ - if (m_texture_id && QOpenGLContext::currentContext()) - QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; } Texture *Atlas::create(const QImage &image) @@ -210,17 +350,6 @@ Texture *Atlas::create(const QImage &image) return 0; } - -int Atlas::textureId() const -{ - if (!m_texture_id) { - Q_ASSERT(QOpenGLContext::currentContext()); - QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<Atlas *>(this)->m_texture_id); - } - - return m_texture_id; -} - static void swizzleBGRAToRGBA(QImage *image) { const int width = image->width(); @@ -334,121 +463,68 @@ void Atlas::uploadBgra(Texture *texture) } } -void Atlas::bind(QSGTexture::Filtering filtering) +void Atlas::generateTexture() { QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); - if (!m_allocated) { - m_allocated = true; - - while (funcs->glGetError() != GL_NO_ERROR) ; - - funcs->glGenTextures(1, &m_texture_id); - funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -#if !defined(QT_OPENGL_ES_2) - if (!QOpenGLContext::currentContext()->isOpenGLES()) - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); -#endif - funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, 0); + funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, 0); #if 0 - QImage pink(m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); - pink.fill(0); - QPainter p(&pink); - QLinearGradient redGrad(0, 0, m_size.width(), 0); - redGrad.setColorAt(0, Qt::black); - redGrad.setColorAt(1, Qt::red); - p.fillRect(0, 0, m_size.width(), m_size.height(), redGrad); - p.setCompositionMode(QPainter::CompositionMode_Plus); - QLinearGradient blueGrad(0, 0, 0, m_size.height()); - blueGrad.setColorAt(0, Qt::black); - blueGrad.setColorAt(1, Qt::blue); - p.fillRect(0, 0, m_size.width(), m_size.height(), blueGrad); - p.end(); - - funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, pink.constBits()); + QImage pink(m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied); + pink.fill(0); + QPainter p(&pink); + QLinearGradient redGrad(0, 0, m_size.width(), 0); + redGrad.setColorAt(0, Qt::black); + redGrad.setColorAt(1, Qt::red); + p.fillRect(0, 0, m_size.width(), m_size.height(), redGrad); + p.setCompositionMode(QPainter::CompositionMode_Plus); + QLinearGradient blueGrad(0, 0, 0, m_size.height()); + blueGrad.setColorAt(0, Qt::black); + blueGrad.setColorAt(1, Qt::blue); + p.fillRect(0, 0, m_size.width(), m_size.height(), blueGrad); + p.end(); + + funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, pink.constBits()); #endif +} - GLenum errorCode = funcs->glGetError(); - if (errorCode == GL_OUT_OF_MEMORY) { - qDebug("QSGTextureAtlas: texture atlas allocation failed, out of memory"); - funcs->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; - } else if (errorCode != GL_NO_ERROR) { - qDebug("QSGTextureAtlas: texture atlas allocation failed, code=%x", errorCode); - funcs->glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; - } +void Atlas::uploadPendingTexture(int i) +{ + Texture *t = static_cast<Texture*>(m_pending_uploads.at(i)); + if (m_externalFormat == GL_BGRA && + !m_use_bgra_fallback) { + uploadBgra(t); } else { - funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); + upload(t); } - - if (m_texture_id == 0) - return; - - // Upload all pending images.. - for (int i=0; i<m_pending_uploads.size(); ++i) { - - bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled(); - if (profileFrames) - qsg_renderer_timer.start(); - - Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); - - // Skip bind, convert, swizzle; they're irrelevant - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareStart, 3); - - Texture *t = m_pending_uploads.at(i); - if (m_externalFormat == GL_BGRA && - !m_use_bgra_fallback) { - uploadBgra(t); - } else { - upload(t); - } - const QSize textureSize = t->textureSize(); - if (textureSize.width() > m_atlas_transient_image_threshold || - textureSize.height() > m_atlas_transient_image_threshold) - t->releaseImage(); - - qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "atlastexture uploaded in: " << qsg_renderer_timer.elapsed() - << "ms (" << t->textureSize().width() << "x" - << t->textureSize().height() << ")"; - - Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareUpload); - - // Skip mipmap; unused - Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareUpload, 1); - Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare, - QQuickProfiler::SceneGraphTexturePrepareMipmap); - } - - GLenum f = filtering == QSGTexture::Nearest ? GL_NEAREST : GL_LINEAR; - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, f); - funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, f); - - m_pending_uploads.clear(); + const QSize textureSize = t->textureSize(); + if (textureSize.width() > m_atlas_transient_image_threshold || + textureSize.height() > m_atlas_transient_image_threshold) + t->releaseImage(); + + qCDebug(QSG_LOG_TIME_TEXTURE).nospace() << "atlastexture uploaded in: " << qsg_renderer_timer.elapsed() + << "ms (" << t->textureSize().width() << "x" + << t->textureSize().height() << ")"; } -void Atlas::remove(Texture *t) +TextureBase::TextureBase(AtlasBase *atlas, const QRect &textureRect) + : m_allocated_rect(textureRect) + , m_atlas(atlas) { - QRect atlasRect = t->atlasSubRect(); - m_allocator.deallocate(atlasRect); - m_pending_uploads.removeOne(t); } +TextureBase::~TextureBase() +{ + m_atlas->remove(this); +} +void TextureBase::bind() +{ + m_atlas->bind(filtering()); +} Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image) - : QSGTexture() - , m_allocated_rect(textureRect) + : TextureBase(atlas, textureRect) , m_image(image) - , m_atlas(atlas) , m_nonatlas_texture(0) , m_has_alpha(image.hasAlphaChannel()) { @@ -463,16 +539,10 @@ Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image) Texture::~Texture() { - m_atlas->remove(this); if (m_nonatlas_texture) delete m_nonatlas_texture; } -void Texture::bind() -{ - m_atlas->bind(filtering()); -} - QSGTexture *Texture::removedFromAtlas() const { if (m_nonatlas_texture) { @@ -508,7 +578,7 @@ QSGTexture *Texture::removedFromAtlas() const QRect r = atlasSubRectWithoutPadding(); // and copy atlas into our texture. while (f->glGetError() != GL_NO_ERROR) ; - f->glCopyTexImage2D(GL_TEXTURE_2D, 0, m_atlas->internalFormat(), r.x(), r.y(), r.width(), r.height(), 0); + f->glCopyTexImage2D(GL_TEXTURE_2D, 0, static_cast<Atlas*>(m_atlas)->internalFormat(), r.x(), r.y(), r.width(), r.height(), 0); // BGRA may have been rejected by some GLES implementations if (f->glGetError() != GL_NO_ERROR) f->glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r.x(), r.y(), r.width(), r.height(), 0); diff --git a/src/quick/scenegraph/util/qsgatlastexture_p.h b/src/quick/scenegraph/util/qsgatlastexture_p.h index 3dee539547..14dc8f7958 100644 --- a/src/quick/scenegraph/util/qsgatlastexture_p.h +++ b/src/quick/scenegraph/util/qsgatlastexture_p.h @@ -61,10 +61,16 @@ QT_BEGIN_NAMESPACE +namespace QSGCompressedAtlasTexture { + class Atlas; +} +class QSGCompressedTextureFactory; + namespace QSGAtlasTexture { class Texture; +class TextureBase; class Atlas; class Manager : public QObject @@ -76,93 +82,121 @@ public: ~Manager(); QSGTexture *create(const QImage &image, bool hasAlphaChannel); + QSGTexture *create(const QSGCompressedTextureFactory *factory); void invalidate(); private: Atlas *m_atlas; + // set of atlases for different compressed formats + QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*> m_atlases; QSize m_atlas_size; int m_atlas_size_limit; }; -class Atlas : public QObject +class AtlasBase : public QObject { + Q_OBJECT public: - Atlas(const QSize &size); - ~Atlas(); + AtlasBase(const QSize &size); + ~AtlasBase(); void invalidate(); int textureId() const; void bind(QSGTexture::Filtering filtering); + void remove(TextureBase *t); + + QSize size() const { return m_size; } + +protected: + virtual void generateTexture() = 0; + virtual void uploadPendingTexture(int i) = 0; + +protected: + QSGAreaAllocator m_allocator; + unsigned int m_texture_id; + QSize m_size; + QList<TextureBase *> m_pending_uploads; + +private: + bool m_allocated; +}; + +class Atlas : public AtlasBase +{ +public: + Atlas(const QSize &size); + ~Atlas(); + + void generateTexture() override; + void uploadPendingTexture(int i) override; + void upload(Texture *texture); void uploadBgra(Texture *texture); Texture *create(const QImage &image); - void remove(Texture *t); - - QSize size() const { return m_size; } uint internalFormat() const { return m_internalFormat; } uint externalFormat() const { return m_externalFormat; } private: - QSGAreaAllocator m_allocator; - unsigned int m_texture_id; - QSize m_size; - QList<Texture *> m_pending_uploads; - uint m_internalFormat; uint m_externalFormat; int m_atlas_transient_image_threshold; - uint m_allocated : 1; uint m_use_bgra_fallback: 1; - uint m_debug_overlay : 1; }; -class Texture : public QSGTexture +class TextureBase : public QSGTexture +{ + Q_OBJECT +public: + TextureBase(AtlasBase *atlas, const QRect &textureRect); + ~TextureBase(); + + int textureId() const override { return m_atlas->textureId(); } + bool isAtlasTexture() const override { return true; } + + QRect atlasSubRect() const { return m_allocated_rect; } + + void bind() override; + +protected: + QRect m_allocated_rect; + AtlasBase *m_atlas; +}; + +class Texture : public TextureBase { Q_OBJECT public: Texture(Atlas *atlas, const QRect &textureRect, const QImage &image); ~Texture(); - int textureId() const override { return m_atlas->textureId(); } QSize textureSize() const override { return atlasSubRectWithoutPadding().size(); } void setHasAlphaChannel(bool alpha) { m_has_alpha = alpha; } bool hasAlphaChannel() const override { return m_has_alpha; } bool hasMipmaps() const override { return false; } - bool isAtlasTexture() const override { return true; } QRectF normalizedTextureSubRect() const override { return m_texture_coords_rect; } QRect atlasSubRect() const { return m_allocated_rect; } QRect atlasSubRectWithoutPadding() const { return m_allocated_rect.adjusted(1, 1, -1, -1); } - bool isTexture() const { return true; } - QSGTexture *removedFromAtlas() const override; void releaseImage() { m_image = QImage(); } const QImage &image() const { return m_image; } - void bind() override; - private: - QRect m_allocated_rect; QRectF m_texture_coords_rect; - QImage m_image; - - Atlas *m_atlas; - mutable QSGPlainTexture *m_nonatlas_texture; - - uint m_has_alpha : 1; + bool m_has_alpha; }; } diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp index cf4edf29b8..168afb9e56 100644 --- a/src/quick/scenegraph/util/qsgtexturereader.cpp +++ b/src/quick/scenegraph/util/qsgtexturereader.cpp @@ -43,6 +43,7 @@ #if QT_CONFIG(opengl) #include <private/qsgpkmhandler_p.h> +#include <private/qsgktxhandler_p.h> #endif #include <QFileInfo> @@ -80,6 +81,8 @@ bool QSGTextureReader::isTexture() // Currently the handlers are hardcoded; later maybe a list of plugins if (QSGPkmHandler::canRead(suffix, headerBlock)) { m_handler = new QSGPkmHandler(m_device, logName); + } else if (QSGKtxHandler::canRead(suffix, headerBlock)) { + m_handler = new QSGKtxHandler(m_device, logName); } // else if OtherHandler::canRead() ...etc. } diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp index 0323dc9286..1ae9b78669 100644 --- a/src/quick/util/qquickpath.cpp +++ b/src/quick/util/qquickpath.cpp @@ -397,7 +397,12 @@ void QQuickPath::processPath() d->_pointCache.clear(); d->prevBez.isValid = false; - d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed); + if (d->isShapePath) { + // This path is a ShapePath, so avoid extra overhead + d->_path = createShapePath(QPointF(), QPointF(), d->pathLength, &d->closed); + } else { + d->_path = createPath(QPointF(), QPointF(), d->_attributes, d->pathLength, d->_attributePoints, &d->closed); + } emit changed(); } @@ -493,6 +498,42 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en return path; } +QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPointF &endPoint, qreal &pathLength, bool *closed) +{ + Q_D(QQuickPath); + + if (!d->componentComplete) + return QPainterPath(); + + QPainterPath path; + + qreal startX = d->startX.isValid() ? d->startX.value : startPoint.x(); + qreal startY = d->startY.isValid() ? d->startY.value : startPoint.y(); + path.moveTo(startX, startY); + + int index = 0; + for (QQuickCurve *curve : qAsConst(d->_pathCurves)) { + QQuickPathData data; + data.index = index; + data.endPoint = endPoint; + data.curves = d->_pathCurves; + curve->addToPath(path, data); + ++index; + } + + if (closed) { + QPointF end = path.currentPosition(); + *closed = startX == end.x() && startY == end.y(); + } + + // Note: Length of paths inside ShapePath is not used, so currently + // length is always 0. This avoids potentially heavy path.length() + //pathLength = path.length(); + pathLength = 0; + + return path; +} + void QQuickPath::classBegin() { Q_D(QQuickPath); diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index d5474e2d30..a49403fd0e 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -522,6 +522,7 @@ private: public: QPainterPath createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed = 0); + QPainterPath createShapePath(const QPointF &startPoint, const QPointF &endPoint, qreal &pathLength, bool *closed = 0); static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = 0); }; diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h index 8ce85dbf0f..f5c9664223 100644 --- a/src/quick/util/qquickpath_p_p.h +++ b/src/quick/util/qquickpath_p_p.h @@ -72,7 +72,7 @@ public: static QQuickPathPrivate* get(QQuickPath *path) { return path->d_func(); } static const QQuickPathPrivate* get(const QQuickPath *path) { return path->d_func(); } - QQuickPathPrivate() : pathLength(0), closed(false), componentComplete(true) { } + QQuickPathPrivate() : pathLength(0), closed(false), componentComplete(true), isShapePath(false) { } QPainterPath _path; QList<QQuickPathElement*> _pathElements; @@ -86,6 +86,7 @@ public: qreal pathLength; bool closed; bool componentComplete; + bool isShapePath; }; QT_END_NAMESPACE diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp index b3f4faea3f..c585d4c16d 100644 --- a/src/quick/util/qquickpropertychanges.cpp +++ b/src/quick/util/qquickpropertychanges.cpp @@ -461,7 +461,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions() QQmlBinding *newBinding = 0; if (e.id != QQmlBinding::Invalid) { - QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); + QV4::Scope scope(qmlEngine(this)->handle()); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(scope.engine->rootContext(), context, object())); newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(prop)->core, d->compilationUnit->runtimeFunctions.at(e.id), object(), context, qmlContext); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml index dd7cb2055d..4235a2d55f 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/controlFromJS.qml @@ -43,13 +43,6 @@ QtObject { interval: 1000 onTriggered: { console.profileEnd(); - endTimer.start(); } } - - property var endTimer: Timer { - id: endTimer - interval: 1000 - onTriggered: Qt.quit(); - } } diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml index 4236d70ea3..3b28e65174 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/exit.qml @@ -1,7 +1,7 @@ import QtQml 2.0 QtObject { - Timer { + property Timer timer: Timer { running: true interval: 1 onTriggered: Qt.quit(); diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index 43ab3e1171..c32fa7ea7d 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -40,223 +40,134 @@ #include <QtGui/private/qguiapplication_p.h> #include <QtGui/qpa/qplatformintegration.h> -struct QQmlProfilerData -{ - QQmlProfilerData(qint64 time = -2, int messageType = -1, int detailType = -1, - const QString &detailData = QString()) : - time(time), messageType(messageType), detailType(detailType), detailData(detailData), - line(-1), column(-1), framerate(-1), animationcount(-1), amount(-1) - {} - - qint64 time; - int messageType; - int detailType; - - //### - QString detailData; //used by RangeData and RangeLocation - int line; //used by RangeLocation - int column; //used by RangeLocation - int framerate; //used by animation events - int animationcount; //used by animation events - qint64 amount; //used by heap events -}; - -class QQmlProfilerTestClient : public QQmlProfilerClient +class QQmlProfilerTestClient : public QQmlProfilerEventReceiver, public QQmlProfilerDefinitions { Q_OBJECT public: - QQmlProfilerTestClient(QQmlDebugConnection *connection) : QQmlProfilerClient(connection), - lastTimestamp(-1) {} + QQmlProfilerTestClient(QQmlDebugConnection *connection) : + client(new QQmlProfilerClient(connection, this)) + { + connect(client.data(), &QQmlProfilerClient::traceStarted, + this, &QQmlProfilerTestClient::startTrace); + connect(client.data(), &QQmlProfilerClient::traceFinished, + this, &QQmlProfilerTestClient::endTrace); + } - QVector<QQmlProfilerData> qmlMessages; - QVector<QQmlProfilerData> javascriptMessages; - QVector<QQmlProfilerData> jsHeapMessages; - QVector<QQmlProfilerData> asynchronousMessages; - QVector<QQmlProfilerData> pixmapMessages; + void startTrace(qint64 timestamp, const QList<int> &engineIds); + void endTrace(qint64 timestamp, const QList<int> &engineIds); - qint64 lastTimestamp; + QPointer<QQmlProfilerClient> client; // Owned by QQmlDebugTest + QVector<QQmlProfilerEventType> types; -signals: - void recordingFinished(); + QVector<QQmlProfilerEvent> qmlMessages; + QVector<QQmlProfilerEvent> javascriptMessages; + QVector<QQmlProfilerEvent> jsHeapMessages; + QVector<QQmlProfilerEvent> asynchronousMessages; + QVector<QQmlProfilerEvent> pixmapMessages; -private: - void traceStarted(qint64 time, int engineId); - void traceFinished(qint64 time, int engineId); - void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime); - void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data); - void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location); - void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime); - void animationFrame(qint64 time, int frameRate, int animationCount, int threadId); - void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2); - void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount); - void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b); - void complete(); - - void unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, int detailType); - void unknownData(QPacket &stream); + int numLoadedEventTypes() const override; + void addEventType(const QQmlProfilerEventType &type) override; + void addEvent(const QQmlProfilerEvent &event) override; }; -void QQmlProfilerTestClient::traceStarted(qint64 time, int engineId) -{ - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace, - QString::number(engineId))); -} - -void QQmlProfilerTestClient::traceFinished(qint64 time, int engineId) +void QQmlProfilerTestClient::startTrace(qint64 timestamp, const QList<int> &engineIds) { - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::EndTrace, - QString::number(engineId))); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, StartTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) +void QQmlProfilerTestClient::endTrace(qint64 timestamp, const QList<int> &engineIds) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= startTime); - lastTimestamp = startTime; - QQmlProfilerData data(startTime, QQmlProfilerDefinitions::RangeStart, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + types.append(QQmlProfilerEventType(Event, MaximumRangeType, EndTrace)); + asynchronousMessages.append(QQmlProfilerEvent(timestamp, types.length() - 1, + engineIds.toVector())); } -void QQmlProfilerTestClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &string) +int QQmlProfilerTestClient::numLoadedEventTypes() const { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeData, type, string); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + return types.length(); } -void QQmlProfilerTestClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) +void QQmlProfilerTestClient::addEventType(const QQmlProfilerEventType &type) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(location.line >= -2); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::RangeLocation, type, location.filename); - data.line = location.line; - data.column = location.column; - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); + types.append(type); } -void QQmlProfilerTestClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) +void QQmlProfilerTestClient::addEvent(const QQmlProfilerEvent &event) { - QVERIFY(type >= 0 && type < QQmlProfilerDefinitions::MaximumRangeType); - QVERIFY(lastTimestamp <= endTime); - lastTimestamp = endTime; - QQmlProfilerData data(endTime, QQmlProfilerDefinitions::RangeEnd, type); - if (type == QQmlProfilerDefinitions::Javascript) - javascriptMessages.append(data); - else - qmlMessages.append(data); -} + const int typeIndex = event.typeIndex(); + QVERIFY(typeIndex < types.length()); -void QQmlProfilerTestClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId) -{ - QVERIFY(threadId >= 0); - QVERIFY(frameRate != -1); - QVERIFY(animationCount != -1); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::AnimationFrame); - data.framerate = frameRate; - data.animationcount = animationCount; - asynchronousMessages.append(data); -} + const QQmlProfilerEventType &type = types[typeIndex]; -void QQmlProfilerTestClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - Q_UNUSED(numericData1); - Q_UNUSED(numericData2); - Q_UNUSED(numericData3); - Q_UNUSED(numericData4); - Q_UNUSED(numericData5); - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - asynchronousMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::SceneGraphFrame, - type)); -} - -void QQmlProfilerTestClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &url, int numericData1, - int numericData2) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::PixmapCacheEvent, type, url); - switch (type) { - case QQmlProfilerDefinitions::PixmapSizeKnown: - data.line = numericData1; - data.column = numericData2; + switch (type.message()) { + case Event: { + switch (type.detailType()) { + case StartTrace: + QFAIL("StartTrace should not be passed on as event"); + break; + case EndTrace: + QFAIL("EndTrace should not be passed on as event"); + break; + case AnimationFrame: + asynchronousMessages.append(event); + break; + case Mouse: + case Key: + qmlMessages.append(event); + break; + default: + QFAIL(qPrintable(QString::fromLatin1("Event with unknown detailType %1 received at %2.") + .arg(type.detailType()).arg(event.timestamp()))); + break; + } break; - case QQmlProfilerDefinitions::PixmapReferenceCountChanged: - case QQmlProfilerDefinitions::PixmapCacheCountChanged: - data.animationcount = numericData1; + } + case RangeStart: + case RangeData: + case RangeLocation: + case RangeEnd: + QFAIL("Range stages are transmitted as part of events"); break; - default: + case Complete: + QFAIL("Complete should not be passed on as event"); + break; + case PixmapCacheEvent: + pixmapMessages.append(event); + break; + case SceneGraphFrame: + asynchronousMessages.append(event); + break; + case MemoryAllocation: + jsHeapMessages.append(event); + break; + case DebugMessage: + // Unhandled + break; + case MaximumMessage: + switch (type.rangeType()) { + case Painting: + QFAIL("QtQuick1 paint message received."); + break; + case Compiling: + case Creating: + case Binding: + case HandlingSignal: + qmlMessages.append(event); + break; + case Javascript: + javascriptMessages.append(event); + break; + default: + QFAIL(qPrintable( + QString::fromLatin1("Unknown range event %1 received at %2.") + .arg(type.rangeType()).arg(event.timestamp()))); + break; + } break; } - pixmapMessages.append(data); -} - -void QQmlProfilerTestClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - QQmlProfilerData data(time, QQmlProfilerDefinitions::MemoryAllocation, type); - data.amount = amount; - jsHeapMessages.append(data); -} - -void QQmlProfilerTestClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - QVERIFY(lastTimestamp <= time); - lastTimestamp = time; - qmlMessages.append(QQmlProfilerData(time, QQmlProfilerDefinitions::Event, type, - QString::number(a) + QLatin1Char('x') + - QString::number(b))); -} - -void QQmlProfilerTestClient::unknownEvent(QQmlProfilerDefinitions::Message messageType, qint64 time, - int detailType) -{ - QFAIL(qPrintable(QString::fromLatin1("Unknown event %1 with detail type %2 received at %3.") - .arg(messageType).arg(detailType).arg(time))); -} - -void QQmlProfilerTestClient::unknownData(QPacket &stream) -{ - QFAIL(qPrintable(QString::fromLatin1("%1 bytes of extra data after receiving message.") - .arg(stream.device()->bytesAvailable()))); -} - -void QQmlProfilerTestClient::complete() -{ - emit recordingFinished(); } class tst_QQmlProfilerService : public QQmlDebugTest @@ -278,18 +189,23 @@ private: CheckLine = 1 << 2, CheckColumn = 1 << 3, CheckDataEndsWith = 1 << 4, + CheckFileEndsWith = 1 << 5, + CheckNumbers = 1 << 6, - CheckAll = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckDataEndsWith + CheckType = CheckMessageType | CheckDetailType | CheckLine | CheckColumn | CheckFileEndsWith }; - ConnectResult connect(bool block, const QString &testFile, bool restrictServices = true); + ConnectResult connect(bool block, const QString &testFile, bool recordFromStart = true, + uint flushInterval = 0, bool restrictServices = true); + void checkProcessTerminated(); void checkTraceReceived(); void checkJsHeap(); - bool verify(MessageListType type, int expectedPosition, const QQmlProfilerData &expected, - quint32 checks); + bool verify(MessageListType type, int expectedPosition, + const QQmlProfilerEventType &expectedType, quint32 checks, + const QVector<qint64> &numbers); QList<QQmlDebugClient *> createClients() override; - QPointer<QQmlProfilerTestClient> m_client; + QScopedPointer<QQmlProfilerTestClient> m_client; private slots: void cleanup() override; @@ -305,33 +221,63 @@ private slots: void flushInterval(); void translationBinding(); void memory(); + +private: + bool m_recordFromStart = true; + bool m_flushInterval = 0; + bool m_isComplete = false; + + // Don't use ({...}) here as MSVC will interpret that as the "QVector(int size)" ctor. + const QVector<qint64> m_rangeStart = (QVector<qint64>() << QQmlProfilerDefinitions::RangeStart); + const QVector<qint64> m_rangeEnd = (QVector<qint64>() << QQmlProfilerDefinitions::RangeEnd); }; -#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks)) +#define VERIFY(type, position, expected, checks, numbers) \ + QVERIFY(verify(type, position, expected, checks, numbers)) -QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect(bool block, const QString &file, - bool restrictServices) +QQmlDebugTest::ConnectResult tst_QQmlProfilerService::connect( + bool block, const QString &file, bool recordFromStart, uint flushInterval, + bool restrictServices) { + m_recordFromStart = recordFromStart; + m_flushInterval = flushInterval; + m_isComplete = false; + // ### Still using qmlscene due to QTBUG-33377 return QQmlDebugTest::connect(QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmlscene", restrictServices ? QStringLiteral("CanvasFrameRate") : QString(), testFile(file), block); } +void tst_QQmlProfilerService::checkProcessTerminated() +{ + // If the process ends before connect(), we get a non-success value from connect() + // That's not a problem as we will still receive the trace. We check that process has terminated + // cleanly here. + + // Wait for the process to finish by itself, if that hasn't happened already + QTRY_COMPARE(m_client->client->state(), QQmlDebugClient::NotConnected); + QTRY_COMPARE(m_process->exitStatus(), QProcess::NormalExit); +} + void tst_QQmlProfilerService::checkTraceReceived() { - QVERIFY2(QQmlDebugTest::waitForSignal(m_client, SIGNAL(recordingFinished())), - "No trace received in time."); + QTRY_VERIFY2(m_isComplete, "No trace received in time."); + + QVector<qint64> numbers; // must start with "StartTrace" - QQmlProfilerData expected(0, QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::StartTrace); - VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType); + QQmlProfilerEventType expected(QQmlProfilerDefinitions::Event, + QQmlProfilerDefinitions::MaximumRangeType, + QQmlProfilerDefinitions::StartTrace); + VERIFY(MessageListAsynchronous, 0, expected, CheckMessageType | CheckDetailType, numbers); // must end with "EndTrace" - expected.detailType = QQmlProfilerDefinitions::EndTrace; + expected = QQmlProfilerEventType(QQmlProfilerDefinitions::Event, + QQmlProfilerDefinitions::MaximumRangeType, + QQmlProfilerDefinitions::EndTrace); VERIFY(MessageListAsynchronous, m_client->asynchronousMessages.length() - 1, expected, - CheckMessageType | CheckDetailType); + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::checkJsHeap() @@ -344,33 +290,35 @@ void tst_QQmlProfilerService::checkJsHeap() qint64 allocated = 0; qint64 used = 0; qint64 lastTimestamp = -1; - foreach (const QQmlProfilerData &message, m_client->jsHeapMessages) { - switch (message.detailType) { + foreach (const QQmlProfilerEvent &message, m_client->jsHeapMessages) { + const qint64 amount = message.number<qint64>(0); + const QQmlProfilerEventType &type = m_client->types.at(message.typeIndex()); + switch (type.detailType()) { case QV4::Profiling::HeapPage: - allocated += message.amount; + allocated += amount; seen_alloc = true; break; case QV4::Profiling::SmallItem: - used += message.amount; + used += amount; seen_small = true; break; case QV4::Profiling::LargeItem: - allocated += message.amount; - used += message.amount; + allocated += amount; + used += amount; seen_large = true; break; } - QVERIFY(message.time >= lastTimestamp); + QVERIFY(message.timestamp() >= lastTimestamp); // The heap will only be consistent after all events of the same timestamp are processed. if (lastTimestamp == -1) { - lastTimestamp = message.time; + lastTimestamp = message.timestamp(); continue; - } else if (message.time == lastTimestamp) { + } else if (message.timestamp() == lastTimestamp) { continue; } - lastTimestamp = message.time; + lastTimestamp = message.timestamp(); QVERIFY2(used >= 0, QString::fromLatin1("Negative memory usage seen: %1") .arg(used).toUtf8().constData()); @@ -389,10 +337,10 @@ void tst_QQmlProfilerService::checkJsHeap() } bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType type, - int expectedPosition, const QQmlProfilerData &expected, - quint32 checks) + int expectedPosition, const QQmlProfilerEventType &expected, + quint32 checks, const QVector<qint64> &expectedNumbers) { - QVector<QQmlProfilerData> *target = 0; + const QVector<QQmlProfilerEvent> *target = 0; switch (type) { case MessageListQML: target = &(m_client->qmlMessages); break; case MessageListJavaScript: target = &(m_client->javascriptMessages); break; @@ -408,41 +356,81 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty } uint position = expectedPosition; - qint64 timestamp = target->at(expectedPosition).time; - while (position > 0 && target->at(position - 1).time == timestamp) + qint64 timestamp = target->at(expectedPosition).timestamp(); + while (position > 0 && target->at(position - 1).timestamp() == timestamp) --position; QStringList warnings; do { - const QQmlProfilerData &actual = target->at(position); - if ((checks & CheckMessageType) && actual.messageType != expected.messageType) { - warnings << QString::fromLatin1("%1: unexpected messageType. actual: %2 - expected: %3") - .arg(position).arg(actual.messageType).arg(expected.messageType); + const QQmlProfilerEvent &event = target->at(position); + const QQmlProfilerEventType &actual = m_client->types.at(event.typeIndex()); + if ((checks & CheckMessageType) && + (actual.message() != expected.message() + || actual.rangeType() != expected.rangeType())) { + warnings << QString::fromLatin1("%1: unexpected messageType or rangeType. " + "actual: %2, %3 - expected: %4, %5") + .arg(position).arg(actual.message()).arg(actual.rangeType()) + .arg(expected.message()).arg(expected.rangeType()); continue; } - if ((checks & CheckDetailType) && actual.detailType != expected.detailType) { + if ((checks & CheckDetailType) && actual.detailType() != expected.detailType()) { warnings << QString::fromLatin1("%1: unexpected detailType. actual: %2 - expected: %3") - .arg(position).arg(actual.detailType).arg(expected.detailType); + .arg(position).arg(actual.detailType()).arg(expected.detailType()); continue; } - if ((checks & CheckLine) && actual.line != expected.line) { + + const QQmlProfilerEventLocation expectedLocation = expected.location(); + const QQmlProfilerEventLocation actualLocation = actual.location(); + + if ((checks & CheckLine) && actualLocation.line() != expectedLocation.line()) { warnings << QString::fromLatin1("%1: unexpected line. actual: %2 - expected: %3") - .arg(position).arg(actual.line).arg(expected.line); + .arg(position).arg(actualLocation.line()) + .arg(expectedLocation.line()); continue; } - if ((checks & CheckColumn) && actual.column != expected.column) { + if ((checks & CheckColumn) && actualLocation.column() != expectedLocation.column()) { warnings << QString::fromLatin1("%1: unexpected column. actual: %2 - expected: %3") - .arg(position).arg(actual.column).arg(expected.column); + .arg(position).arg(actualLocation.column()) + .arg(expectedLocation.column()); continue; } - if ((checks & CheckDataEndsWith) && !actual.detailData.endsWith(expected.detailData)) { + if ((checks & CheckFileEndsWith) && + !actualLocation.filename().endsWith(expectedLocation.filename())) { + warnings << QString::fromLatin1("%1: unexpected fileName. actual: %2 - expected: %3") + .arg(position).arg(actualLocation.filename()) + .arg(expectedLocation.filename()); + continue; + } + + if ((checks & CheckDataEndsWith) && !actual.data().endsWith(expected.data())) { warnings << QString::fromLatin1("%1: unexpected detailData. actual: %2 - expected: %3") - .arg(position).arg(actual.detailData).arg(expected.detailData); + .arg(position).arg(actual.data()).arg(expected.data()); continue; } + + if (checks & CheckNumbers) { + const QVector<qint64> actualNumbers = event.numbers<QVector<qint64>>(); + if (actualNumbers != expectedNumbers) { + + QStringList expectedList; + for (qint64 number : expectedNumbers) + expectedList.append(QString::number(number)); + QStringList actualList; + for (qint64 number : actualNumbers) + actualList.append(QString::number(number)); + + warnings << QString::fromLatin1( + "%1: unexpected numbers. actual [%2] - expected: [%3]") + .arg(position) + .arg(actualList.join(QLatin1String(", "))) + .arg(expectedList.join(QLatin1String(", "))); + continue; + } + } + return true; - } while (target->at(++position).time == timestamp); + } while (target->at(++position).timestamp() == timestamp); foreach (const QString &message, warnings) qWarning() << message.toLocal8Bit().constData(); @@ -452,49 +440,59 @@ bool tst_QQmlProfilerService::verify(tst_QQmlProfilerService::MessageListType ty QList<QQmlDebugClient *> tst_QQmlProfilerService::createClients() { - m_client = new QQmlProfilerTestClient(m_connection); - return QList<QQmlDebugClient *>({m_client}); + m_client.reset(new QQmlProfilerTestClient(m_connection)); + m_client->client->setRecording(m_recordFromStart); + m_client->client->setFlushInterval(m_flushInterval); + QObject::connect(m_client->client, &QQmlProfilerClient::complete, + this, [this](){ m_isComplete = true; }); + return QList<QQmlDebugClient *>({m_client->client}); } void tst_QQmlProfilerService::cleanup() { + auto log = [this](const QQmlProfilerEvent &data, int i) { + const QQmlProfilerEventType &type = m_client->types.at(data.typeIndex()); + const QQmlProfilerEventLocation location = type.location(); + qDebug() << i << data.timestamp() << type.message() << type.rangeType() << type.detailType() + << location.filename() << location.line() << location.column() + << data.numbers<QVector<qint64>>(); + }; + if (m_client && QTest::currentTestFailed()) { qDebug() << "QML Messages:" << m_client->qmlMessages.count(); int i = 0; - foreach (const QQmlProfilerData &data, m_client->qmlMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->qmlMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "JavaScript Messages:" << m_client->javascriptMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->javascriptMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + + for (const QQmlProfilerEvent &data : qAsConst(m_client->javascriptMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Asynchronous Messages:" << m_client->asynchronousMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->asynchronousMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.framerate << data.animationcount << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->asynchronousMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Pixmap Cache Messages:" << m_client->pixmapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->pixmapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType << data.detailData - << data.line << data.column; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->pixmapMessages)) + log(data, i++); + qDebug() << " "; qDebug() << "Javascript Heap Messages:" << m_client->jsHeapMessages.count(); i = 0; - foreach (const QQmlProfilerData &data, m_client->jsHeapMessages) { - qDebug() << i++ << data.time << data.messageType << data.detailType; - } + for (const QQmlProfilerEvent &data : qAsConst(m_client->jsHeapMessages)) + log(data, i++); + qDebug() << " "; } + m_client.reset(); QQmlDebugTest::cleanup(); } @@ -519,63 +517,62 @@ void tst_QQmlProfilerService::connect() QFETCH(bool, restrictMode); QFETCH(bool, traceEnabled); - QCOMPARE(connect(blockMode, "test.qml", restrictMode), ConnectSuccess); + QCOMPARE(connect(blockMode, "test.qml", traceEnabled, 0, restrictMode), ConnectSuccess); - // if the engine is waiting, then the first message determines if it starts with trace enabled if (!traceEnabled) - m_client->sendRecordingStatus(false); - m_client->sendRecordingStatus(true); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(true); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::pixmapCacheData() { - QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); - QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); + QCOMPARE(connect(true, "pixmapCacheTest.qml"), ConnectSuccess); + // Don't wait for readyReadStandardOutput before the loop. It may have already arrived. while (m_process->output().indexOf(QLatin1String("image loaded")) == -1 && m_process->output().indexOf(QLatin1String("image error")) == -1) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::PixmapCacheEvent); + auto createType = [](QQmlProfilerDefinitions::PixmapEventType type) { + return QQmlProfilerEventType(QQmlProfilerDefinitions::PixmapCacheEvent, + QQmlProfilerDefinitions::MaximumRangeType, type); + }; + + QVector<qint64> numbers; // image starting to load - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingStarted; - VERIFY(MessageListPixmap, 0, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 0, createType(QQmlProfilerDefinitions::PixmapLoadingStarted), + CheckMessageType | CheckDetailType, numbers); // image size - expected.detailType = QQmlProfilerDefinitions::PixmapSizeKnown; - expected.line = expected.column = 2; // width and height, in fact - VERIFY(MessageListPixmap, 1, expected, - CheckMessageType | CheckDetailType | CheckLine | CheckColumn); + numbers = QVector<qint64>({2, 2, 1}); + VERIFY(MessageListPixmap, 1, createType(QQmlProfilerDefinitions::PixmapSizeKnown), + CheckMessageType | CheckDetailType | CheckNumbers, numbers); // image loaded - expected.detailType = QQmlProfilerDefinitions::PixmapLoadingFinished; - VERIFY(MessageListPixmap, 2, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 2, createType(QQmlProfilerDefinitions::PixmapLoadingFinished), + CheckMessageType | CheckDetailType, numbers); // cache size - expected.detailType = QQmlProfilerDefinitions::PixmapCacheCountChanged; - VERIFY(MessageListPixmap, 3, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListPixmap, 3, createType(QQmlProfilerDefinitions::PixmapCacheCountChanged), + CheckMessageType | CheckDetailType, numbers); } void tst_QQmlProfilerService::scenegraphData() { QCOMPARE(connect(true, "scenegraphTest.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); - while (!m_process->output().contains(QLatin1String("tick"))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); @@ -591,10 +588,11 @@ void tst_QQmlProfilerService::scenegraphData() qint64 renderFrameTime = -1; #if QT_CONFIG(opengl) //Software renderer doesn't have context frames if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) { - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.messageType == QQmlProfilerDefinitions::SceneGraphFrame) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphContextFrame) { - contextFrameTime = msg.time; + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.message() == QQmlProfilerDefinitions::SceneGraphFrame) { + if (type.detailType() == QQmlProfilerDefinitions::SceneGraphContextFrame) { + contextFrameTime = msg.timestamp(); break; } } @@ -603,23 +601,25 @@ void tst_QQmlProfilerService::scenegraphData() QVERIFY(contextFrameTime != -1); } #endif - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRendererFrame) { - QVERIFY(msg.time >= contextFrameTime); - renderFrameTime = msg.time; + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == QQmlProfilerDefinitions::SceneGraphRendererFrame) { + QVERIFY(msg.timestamp() >= contextFrameTime); + renderFrameTime = msg.timestamp(); break; } } QVERIFY(renderFrameTime != -1); - foreach (const QQmlProfilerData &msg, m_client->asynchronousMessages) { - if (msg.detailType == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) { - if (msg.time >= contextFrameTime) { + foreach (const QQmlProfilerEvent &msg, m_client->asynchronousMessages) { + const QQmlProfilerEventType &type = m_client->types.at(msg.typeIndex()); + if (type.detailType() == QQmlProfilerDefinitions::SceneGraphRenderLoopFrame) { + if (msg.timestamp() >= contextFrameTime) { // Make sure SceneGraphRenderLoopFrame is not between SceneGraphContextFrame and // SceneGraphRendererFrame. A SceneGraphRenderLoopFrame before everything else is // OK as the scene graph might decide to do an initial rendering. - QVERIFY(msg.time >= renderFrameTime); + QVERIFY(msg.timestamp() >= renderFrameTime); break; } } @@ -628,9 +628,8 @@ void tst_QQmlProfilerService::scenegraphData() void tst_QQmlProfilerService::profileOnExit() { - QCOMPARE(connect(true, "exit.qml"), ConnectSuccess); - - m_client->sendRecordingStatus(true); + connect(true, "exit.qml"); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); @@ -638,9 +637,9 @@ void tst_QQmlProfilerService::profileOnExit() void tst_QQmlProfilerService::controlFromJS() { - QCOMPARE(connect(true, "controlFromJS.qml"), ConnectSuccess); + QCOMPARE(connect(true, "controlFromJS.qml", false), ConnectSuccess); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } @@ -649,104 +648,97 @@ void tst_QQmlProfilerService::signalSourceLocation() { QCOMPARE(connect(true, "signalSourceLocation.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("500")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeLocation, - QQmlProfilerDefinitions::HandlingSignal, - QLatin1String("signalSourceLocation.qml")); - expected.line = 8; - expected.column = 28; - VERIFY(MessageListQML, 9, expected, CheckAll); + auto createType = [](int line, int column) { + return QQmlProfilerEventType( + QQmlProfilerDefinitions::MaximumMessage, + QQmlProfilerDefinitions::HandlingSignal, -1, + QQmlProfilerEventLocation(QLatin1String("signalSourceLocation.qml"), line, + column)); + }; - expected.line = 7; - expected.column = 21; - VERIFY(MessageListQML, 11, expected, CheckAll); + VERIFY(MessageListQML, 4, createType(8, 28), CheckType | CheckNumbers, m_rangeStart); + VERIFY(MessageListQML, 6, createType(7, 21), CheckType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::javascript() { QCOMPARE(connect(true, "javascript.qml"), ConnectSuccess); - m_client->sendRecordingStatus(true); while (!(m_process->output().contains(QLatin1String("done")))) QVERIFY(QQmlDebugTest::waitForSignal(m_process, SIGNAL(readyReadStandardOutput()))); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart, - QQmlProfilerDefinitions::Javascript); - VERIFY(MessageListJavaScript, 6, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 2, QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, + QQmlProfilerDefinitions::Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeLocation; - expected.detailData = QLatin1String("javascript.qml"); - expected.line = 4; - expected.column = 5; - VERIFY(MessageListJavaScript, 7, expected, CheckAll); + VERIFY(MessageListJavaScript, 3, + QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, + QQmlProfilerDefinitions::Javascript, -1, + QQmlProfilerEventLocation(QLatin1String("javascript.qml"), 4, 5)), + CheckType | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeData; - expected.detailData = QLatin1String("something"); - VERIFY(MessageListJavaScript, 8, expected, - CheckMessageType | CheckDetailType | CheckDataEndsWith); + VERIFY(MessageListJavaScript, 4, QQmlProfilerEventType( + QQmlProfilerDefinitions::MaximumMessage, QQmlProfilerDefinitions::Javascript, -1, + QQmlProfilerEventLocation(), QLatin1String("something")), + CheckMessageType | CheckDetailType | CheckDataEndsWith | CheckNumbers, m_rangeStart); - expected.messageType = QQmlProfilerDefinitions::RangeEnd; - VERIFY(MessageListJavaScript, 21, expected, CheckMessageType | CheckDetailType); + VERIFY(MessageListJavaScript, 10, QQmlProfilerEventType(QQmlProfilerDefinitions::MaximumMessage, + QQmlProfilerDefinitions::Javascript), + CheckMessageType | CheckDetailType | CheckNumbers, m_rangeEnd); } void tst_QQmlProfilerService::flushInterval() { - QCOMPARE(connect(true, "timer.qml"), ConnectSuccess); - - m_client->sendRecordingStatus(true, -1, 1); + QCOMPARE(connect(true, "timer.qml", true, 1), ConnectSuccess); // Make sure we get multiple messages QTRY_VERIFY(m_client->qmlMessages.length() > 0); QVERIFY(m_client->qmlMessages.length() < 100); QTRY_VERIFY(m_client->qmlMessages.length() > 100); - m_client->sendRecordingStatus(false); + m_client->client->setRecording(false); checkTraceReceived(); checkJsHeap(); } void tst_QQmlProfilerService::translationBinding() { - QCOMPARE(connect(true, "qstr.qml"), ConnectSuccess); - - m_client->sendRecordingStatus(true); + connect(true, "qstr.qml"); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); - QQmlProfilerData expected(0, QQmlProfilerDefinitions::RangeStart, - QQmlProfilerDefinitions::Binding); - VERIFY(MessageListQML, 8, expected, - CheckDetailType | CheckMessageType); + const QQmlProfilerEventType type(QQmlProfilerDefinitions::MaximumMessage, + QQmlProfilerDefinitions::Binding); - expected.messageType = QQmlProfilerDefinitions::RangeEnd; - VERIFY(MessageListQML, 10, expected, - CheckDetailType | CheckMessageType); + VERIFY(MessageListQML, 4, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeStart); + VERIFY(MessageListQML, 5, type, CheckDetailType | CheckMessageType | CheckNumbers, + m_rangeEnd); } void tst_QQmlProfilerService::memory() { connect(true, "memory.qml"); - if (QTest::currentTestFailed() || QTestResult::skipCurrentTest()) - return; - - m_client->sendRecordingStatus(true); + checkProcessTerminated(); checkTraceReceived(); checkJsHeap(); int smallItems = 0; for (auto message : m_client->jsHeapMessages) { - if (message.detailType == QV4::Profiling::SmallItem) + const QQmlProfilerEventType &type = m_client->types[message.typeIndex()]; + if (type.detailType() == QV4::Profiling::SmallItem) ++smallItems; } diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 562ed7c53d..b569ad6b3c 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -78,7 +78,7 @@ public: emit evaluateFinished(); } - QV4::ExecutionEngine *v4Engine() { return QV8Engine::getV4(this); } + QV4::ExecutionEngine *v4Engine() { return handle(); } Q_INVOKABLE void injectFunction(const QString &functionName, InjectedFunction injectedFunction) { @@ -487,7 +487,7 @@ void tst_qv4debugger::conditionalBreakPoint() void tst_qv4debugger::conditionalBreakPointInQml() { QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + QV4::ExecutionEngine *v4 = engine.handle(); QV4Debugger *v4Debugger = new QV4Debugger(v4); v4->setDebugger(v4Debugger); diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index 3c78f6601e..105469fbcf 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -544,7 +544,7 @@ public: QJSEngine *jsEngine = qjsEngine(this); if (!jsEngine) return; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(jsEngine); + QV4::ExecutionEngine *v4 = jsEngine->handle(); if (!v4) return; QV4::Scope scope(v4); diff --git a/tests/auto/qml/qqmlecmascript/testtypes.h b/tests/auto/qml/qqmlecmascript/testtypes.h index e15a05a00c..2db35d0219 100644 --- a/tests/auto/qml/qqmlecmascript/testtypes.h +++ b/tests/auto/qml/qqmlecmascript/testtypes.h @@ -1262,7 +1262,6 @@ public: { CircularReferenceObject *retn = new CircularReferenceObject(parent); retn->m_dtorCount = m_dtorCount; - retn->m_engine = m_engine; return retn; } @@ -1283,14 +1282,8 @@ public: thisObject->defineDefaultProperty(QStringLiteral("autoTestStrongRef"), v); } - void setEngine(QQmlEngine* declarativeEngine) - { - m_engine = QQmlEnginePrivate::get(declarativeEngine)->v8engine(); - } - private: int *m_dtorCount; - QV8Engine* m_engine; }; Q_DECLARE_METATYPE(CircularReferenceObject*) diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 8913fa2f2e..f48cb2743c 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -2346,12 +2346,12 @@ void tst_qqmlecmascript::regExpBug() } } -static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const char *source) +static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::Scope scope(v4); QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource); program.inheritContext = true; @@ -2362,7 +2362,7 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const } QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = o; - *jsCallData->thisObject = engine->global(); + *jsCallData->thisObject = v4->global(); function->call(jsCallData); if (scope.engine->hasException) { scope.engine->catchException(); @@ -2371,13 +2371,13 @@ static inline bool evaluate_error(QV8Engine *engine, const QV4::Value &o, const return false; } -static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, +static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source, const QV4::Value &result) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::Scope scope(v4); QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource); program.inheritContext = true; @@ -2392,7 +2392,7 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, QV4::ScopedValue value(scope); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = o; - *jsCallData->thisObject = engine->global(); + *jsCallData->thisObject = v4->global(); value = function->call(jsCallData); if (scope.engine->hasException) { scope.engine->catchException(); @@ -2401,13 +2401,13 @@ static inline bool evaluate_value(QV8Engine *engine, const QV4::Value &o, return QV4::Runtime::method_strictEqual(value, result); } -static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o, - const char *source) +static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o, + const char *source) { QString functionSource = QLatin1String("(function(object) { return ") + QLatin1String(source) + QLatin1String(" })"); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::Scope scope(v4); QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::EvalCode, functionSource); program.inheritContext = true; @@ -2421,7 +2421,7 @@ static inline QV4::ReturnedValue evaluate(QV8Engine *engine, const QV4::Value &o return QV4::Encode::undefined(); QV4::JSCallData jsCallData(scope, 1); jsCallData->args[0] = o; - *jsCallData->thisObject = engine->global(); + *jsCallData->thisObject = v4->global(); QV4::ScopedValue result(scope, function->call(jsCallData)); if (scope.engine->hasException) { scope.engine->catchException(); @@ -2441,12 +2441,11 @@ void tst_qqmlecmascript::callQtInvokables() MyInvokableObject *o = new MyInvokableObject(); QQmlEngine qmlengine; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine); - QV8Engine *engine = ep->v8engine(); - QV4::Scope scope(QV8Engine::getV4(engine)); + QV4::ExecutionEngine *engine = qmlengine.handle(); + QV4::Scope scope(engine); - QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(QV8Engine::getV4(engine), o)); + QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o)); // Non-existent methods o->reset(); @@ -3014,9 +3013,8 @@ void tst_qqmlecmascript::resolveClashingProperties() { ClashingNames *o = new ClashingNames(); QQmlEngine qmlengine; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&qmlengine); - QV4::ExecutionEngine *engine = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *engine = qmlengine.handle(); QV4::Scope scope(engine); QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, o)); @@ -4046,8 +4044,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { QQmlContextData *childCtxt = ctxt->childContexts; if (!ctxt->importedScripts.isNullOrUndefined()) { - QV8Engine *engine = QV8Engine::get(ctxt->engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + QV4::ExecutionEngine *v4 = ctxt->engine->handle(); QV4::Scope scope(v4); QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts.value()); QV4::Scoped<QV4::QQmlContextWrapper> qml(scope); @@ -4059,7 +4056,7 @@ void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) { qml = QV4::Encode::undefined(); { - QV4::Scope scope(QV8Engine::getV4((engine))); + QV4::Scope scope(v4); QV4::ScopedContext temporaryScope(scope, QV4::QmlContext::create(scope.engine->rootContext(), scriptContext, 0)); Q_UNUSED(temporaryScope) } @@ -4376,8 +4373,7 @@ void tst_qqmlecmascript::scarceResources_other() QPixmap origPixmap(100, 100); origPixmap.fill(Qt::blue); QString srp_name, expectedWarning; - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4 = engine.handle(); ScarceResourceObject *eo = 0; QObject *srsc = 0; QObject *object = 0; @@ -4748,8 +4744,7 @@ void tst_qqmlecmascript::scarceResources() QFETCH(QVariantList, expectedValues); QFETCH(QStringList, expectedErrors); - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(&engine); - QV4::ExecutionEngine *v4 = QV8Engine::getV4(ep->v8engine()); + QV4::ExecutionEngine *v4 = engine.handle(); ScarceResourceObject *eo = 0; QObject *object = 0; @@ -5189,9 +5184,9 @@ void tst_qqmlecmascript::propertyVarInheritance() // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only // public function which can return us a handle to something in the varProperties array. QV4::ReturnedValue tmp = icovmemo->vmeProperty(ico5->metaObject()->indexOfProperty("circ")); - icoCanaryHandle.set(QQmlEnginePrivate::getV4Engine(&engine), tmp); + icoCanaryHandle.set(engine.handle(), tmp); tmp = ccovmemo->vmeProperty(cco5->metaObject()->indexOfProperty("circ")); - ccoCanaryHandle.set(QQmlEnginePrivate::getV4Engine(&engine), tmp); + ccoCanaryHandle.set(engine.handle(), tmp); tmp = QV4::Encode::null(); QVERIFY(!icoCanaryHandle.isUndefined()); QVERIFY(!ccoCanaryHandle.isUndefined()); @@ -5229,7 +5224,7 @@ void tst_qqmlecmascript::propertyVarInheritance2() QCOMPARE(childObject->property("textCanary").toInt(), 10); QV4::WeakValue childObjectVarArrayValueHandle; { - childObjectVarArrayValueHandle.set(QQmlEnginePrivate::getV4Engine(&engine), + childObjectVarArrayValueHandle.set(engine.handle(), QQmlVMEMetaObject::get(childObject)->vmeProperty(childObject->metaObject()->indexOfProperty("vp"))); QVERIFY(!childObjectVarArrayValueHandle.isUndefined()); gc(engine); @@ -5317,7 +5312,6 @@ void tst_qqmlecmascript::handleReferenceManagement() QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); - cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "createReference"); gc(hrmEngine); @@ -5337,7 +5331,6 @@ void tst_qqmlecmascript::handleReferenceManagement() QObject *object = component.create(); QVERIFY(object != 0); CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>("cro"); - cro->setEngine(&hrmEngine); cro->setDtorCount(&dtorCount); QMetaObject::invokeMethod(object, "circularReference"); gc(hrmEngine); @@ -7312,7 +7305,7 @@ private slots: *resultPtr = weakRef->valueRef() && weakRef->isNullOrUndefined(); if (!*resultPtr) return; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(qmlEngine(this)); + QV4::ExecutionEngine *v4 = qmlEngine(this)->handle(); weakRef->set(v4, v4->newObject()); *resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined(); } @@ -7362,7 +7355,7 @@ void tst_qqmlecmascript::onDestructionViaGC() qmlRegisterType<WeakReferenceMutator>("Test", 1, 0, "WeakReferenceMutator"); QQmlEngine engine; - QV4::ExecutionEngine *v4 = QV8Engine::getV4(&engine); + QV4::ExecutionEngine *v4 =engine.handle(); QQmlComponent component(&engine, testFileUrl("DestructionHelper.qml")); diff --git a/tests/auto/qml/qqmllanguage/testtypes.cpp b/tests/auto/qml/qqmllanguage/testtypes.cpp index d2240d25a9..1a81528bc4 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.cpp +++ b/tests/auto/qml/qqmllanguage/testtypes.cpp @@ -136,7 +136,7 @@ void CustomBinding::componentComplete() QQmlContextData *context = QQmlContextData::get(qmlContext(this)); QQmlProperty property(m_target, name, qmlContext(this)); - QV4::Scope scope(QQmlEnginePrivate::getV4Engine(qmlEngine(this))); + QV4::Scope scope(qmlEngine(this)->handle()); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(scope.engine->rootContext(), context, m_target)); QQmlBinding *qmlBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, compilationUnit->runtimeFunctions[bindingId], m_target, context, qmlContext); diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro deleted file mode 100644 index 150f2f08d3..0000000000 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/invalidStrictModule.pro +++ /dev/null @@ -1,12 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -SOURCES = plugin.cpp -QT = core qml -DESTDIR = ../imports/org/qtproject/InvalidStrictModule - -QT += core-private gui-private qml-private - -IMPORT_FILES = \ - qmldir - -include (../../../shared/imports.pri) diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir b/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir deleted file mode 100644 index 20716dc9f9..0000000000 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/qmldir +++ /dev/null @@ -1,2 +0,0 @@ -module org.qtproject.InvalidStrictModule -plugin invalidStrictModule diff --git a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro index 0f548aa6f8..ae13a041cc 100644 --- a/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro +++ b/tests/auto/qml/qqmlmoduleplugin/qqmlmoduleplugin.pro @@ -11,7 +11,6 @@ SUBDIRS =\ nestedPlugin\ strictModule\ strictModule.2\ - invalidStrictModule\ nonstrictModule\ preemptiveModule\ preemptedStrictModule\ diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 8600e1e8ab..edfbf57e76 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -506,12 +506,6 @@ void tst_qqmlmoduleplugin::importStrictModule_data() << QString() << QString(); - QTest::newRow("wrong target") - << "import org.qtproject.InvalidStrictModule 1.0\n" - "MyPluginType {}" - << QString() - << ":1:1: plugin cannot be loaded for module \"org.qtproject.InvalidStrictModule\": Cannot install element 'MyPluginType' into unregistered namespace 'org.qtproject.SomeOtherModule'"; - QTest::newRow("non-strict clash") << "import org.qtproject.NonstrictModule 1.0\n" "MyPluginType {}" diff --git a/tests/auto/qmltest/image/car.ktx b/tests/auto/qmltest/image/car.ktx Binary files differnew file mode 100644 index 0000000000..2aefdd306b --- /dev/null +++ b/tests/auto/qmltest/image/car.ktx diff --git a/tests/auto/qmltest/image/tst_image.qml b/tests/auto/qmltest/image/tst_image.qml index 6385db7fbb..346bee6969 100644 --- a/tests/auto/qmltest/image/tst_image.qml +++ b/tests/auto/qmltest/image/tst_image.qml @@ -127,6 +127,11 @@ Item { source: "logo.pkm" } + Image { + id: ktxImage + source: "car.ktx" + } + TestCase { name: "Image" @@ -231,5 +236,10 @@ Item { compare(pkmImage.width, 256) compare(pkmImage.height, 256) } + + function test_ktxImage() { + compare(ktxImage.width, 146) + compare(ktxImage.height, 80) + } } } diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml index 565f906fb1..8238d87313 100644 --- a/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml +++ b/tests/auto/quick/qquickcanvasitem/data/tst_canvas.qml @@ -151,9 +151,11 @@ CanvasTestCase { {mimeType:"image/bmp"}, {mimeType:"image/jpeg"}, {mimeType:"image/x-portable-pixmap"}, - //{mimeType:"image/tiff"}, QTBUG-23980 {mimeType:"image/xpm"}, ]; + if (hasImageFormats) + imageTypes.push({ mimeType: "image/tiff" }); + for (var i = 0; i < imageTypes.length; i++) { ctx.fillStyle = "red"; ctx.fillRect(0, 0, c.width, c.height); diff --git a/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro b/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro index c6d2a69f8c..845128f9de 100644 --- a/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro +++ b/tests/auto/quick/qquickcanvasitem/qquickcanvasitem.pro @@ -5,6 +5,8 @@ TARGET=tst_qquickcanvasitem CONFIG += qmltestcase SOURCES += tst_qquickcanvasitem.cpp +exists($$[QT_INSTALL_PLUGINS]/imageformats): DEFINES += HAS_IMAGE_FORMATS + TESTDATA = data/* OTHER_FILES += \ diff --git a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp index bde2b4809b..dad8df0682 100644 --- a/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp +++ b/tests/auto/quick/qquickcanvasitem/tst_qquickcanvasitem.cpp @@ -26,4 +26,29 @@ ** ****************************************************************************/ #include <QtQuickTest/quicktest.h> -QUICK_TEST_MAIN(qquickcanvasitem) +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> + +class Setup : public QObject +{ + Q_OBJECT + +public: + Setup() {} + +public slots: + void qmlEngineAvailable(QQmlEngine *engine) + { + engine->rootContext()->setContextProperty("hasImageFormats", QVariant( +#ifdef HAS_IMAGE_FORMATS + true +#else + false +#endif + )); + } +}; + +QUICK_TEST_MAIN_WITH_SETUP(qquickcanvasitem, Setup) + +#include "tst_qquickcanvasitem.moc" diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 2280f75518..bf43e976ab 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -2038,9 +2038,6 @@ void tst_qquickvisualdatamodel::get() QQmlDelegateModelGroup *selectedItems = visualModel->findChild<QQmlDelegateModelGroup *>("selectedItems"); QVERIFY(selectedItems); - QV8Engine *v8Engine = QQmlEnginePrivate::getV8Engine(ctxt->engine()); - QVERIFY(v8Engine); - const bool f = false; const bool t = true; diff --git a/tests/auto/quicktest/quicktest.pro b/tests/auto/quicktest/quicktest.pro index 3b4ec23a64..9d909f1d16 100644 --- a/tests/auto/quicktest/quicktest.pro +++ b/tests/auto/quicktest/quicktest.pro @@ -1,3 +1,4 @@ TEMPLATE = subdirs SUBDIRS = \ - signalspy + signalspy \ + quicktestmainwithsetup diff --git a/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml new file mode 100644 index 0000000000..0f5466998a --- /dev/null +++ b/tests/auto/quicktest/quicktestmainwithsetup/data/tst_setup.qml @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.2 + +TestCase { + name: "setup" + + function initTestCase() + { + verify(qmlEngineAvailableCalled) + } +} diff --git a/tests/auto/quicktest/quicktestmainwithsetup/quicktestmainwithsetup.pro b/tests/auto/quicktest/quicktestmainwithsetup/quicktestmainwithsetup.pro new file mode 100644 index 0000000000..a8c1c6d36a --- /dev/null +++ b/tests/auto/quicktest/quicktestmainwithsetup/quicktestmainwithsetup.pro @@ -0,0 +1,12 @@ +CONFIG += qmltestcase +macos:CONFIG -= app_bundle +TARGET = tst_quicktestmainwithsetup + +QT += testlib quick + +include (../../shared/util.pri) + +SOURCES += tst_quicktestmainwithsetup.cpp + +TESTDATA += \ + $$PWD/data/*.qml diff --git a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp index fe01507412..b0545d1a95 100644 --- a/tests/auto/qml/qqmlmoduleplugin/invalidStrictModule/plugin.cpp +++ b/tests/auto/quicktest/quicktestmainwithsetup/tst_quicktestmainwithsetup.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -25,32 +25,30 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include <QStringList> -#include <QtQml/qqmlextensionplugin.h> -#include <QtQml/qqml.h> -#include <QDebug> -class MyPluginType : public QObject -{ - Q_OBJECT -public: - MyPluginType(QObject *parent=0) : QObject(parent) {} -}; +#include <QtTest/qtest.h> +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQuick/qquickitem.h> +#include <QtQuick/qquickview.h> +#include <QtQuickTest/quicktest.h> +#include "../../shared/util.h" -class MyPlugin : public QQmlExtensionPlugin +class CustomTestSetup : public QObject { Q_OBJECT - Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) public: - MyPlugin() {} + CustomTestSetup() {} - void registerTypes(const char *uri) +public slots: + void qmlEngineAvailable(QQmlEngine *qmlEngine) { - Q_ASSERT(QLatin1String(uri) == "org.qtproject.InvalidStrictModule"); - qmlRegisterType<MyPluginType>("org.qtproject.SomeOtherModule", 1, 0, "MyPluginType"); + qmlEngine->rootContext()->setContextProperty("qmlEngineAvailableCalled", true); } }; -#include "plugin.moc" +QUICK_TEST_MAIN_WITH_SETUP(qquicktestsetup, CustomTestSetup) + +#include "tst_quicktestmainwithsetup.moc" diff --git a/tools/qmlprofiler/qmlprofilerapplication.cpp b/tools/qmlprofiler/qmlprofilerapplication.cpp index 033492b516..0b0417bd7d 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.cpp +++ b/tools/qmlprofiler/qmlprofilerapplication.cpp @@ -83,24 +83,27 @@ QmlProfilerApplication::QmlProfilerApplication(int &argc, char **argv) : m_verbose(false), m_recording(true), m_interactive(false), - m_qmlProfilerClient(&m_connection, &m_profilerData), m_connectionAttempts(0) { + m_connection.reset(new QQmlDebugConnection); + m_profilerData.reset(new QmlProfilerData); + m_qmlProfilerClient.reset(new QmlProfilerClient(m_connection.data(), m_profilerData.data())); m_connectTimer.setInterval(1000); connect(&m_connectTimer, &QTimer::timeout, this, &QmlProfilerApplication::tryToConnect); - connect(&m_connection, &QQmlDebugConnection::connected, + connect(m_connection.data(), &QQmlDebugConnection::connected, this, &QmlProfilerApplication::connected); - connect(&m_qmlProfilerClient, &QmlProfilerClient::enabledChanged, + connect(m_qmlProfilerClient.data(), &QmlProfilerClient::enabledChanged, this, &QmlProfilerApplication::traceClientEnabledChanged); - connect(&m_qmlProfilerClient, &QmlProfilerClient::recordingStarted, + connect(m_qmlProfilerClient.data(), &QmlProfilerClient::traceStarted, this, &QmlProfilerApplication::notifyTraceStarted); - connect(&m_qmlProfilerClient, &QmlProfilerClient::error, + connect(m_qmlProfilerClient.data(), &QmlProfilerClient::error, this, &QmlProfilerApplication::logError); - connect(&m_profilerData, &QmlProfilerData::error, this, &QmlProfilerApplication::logError); - connect(&m_profilerData, &QmlProfilerData::dataReady, + connect(m_profilerData.data(), &QmlProfilerData::error, + this, &QmlProfilerApplication::logError); + connect(m_profilerData.data(), &QmlProfilerData::dataReady, this, &QmlProfilerApplication::traceFinished); } @@ -239,7 +242,7 @@ void QmlProfilerApplication::parseArguments() if (features == 0) parser.showHelp(4); - m_qmlProfilerClient.setFeatures(features); + m_qmlProfilerClient->setRequestedFeatures(features); if (parser.isSet(verbose)) m_verbose = true; @@ -295,10 +298,10 @@ void QmlProfilerApplication::flush() { if (m_recording) { m_pendingRequest = REQUEST_FLUSH; - m_qmlProfilerClient.sendRecordingStatus(false); + m_qmlProfilerClient->sendRecordingStatus(false); } else { - if (m_profilerData.save(m_interactiveOutputFile)) { - m_profilerData.clear(); + if (m_profilerData->save(m_interactiveOutputFile)) { + m_profilerData->clear(); if (!m_interactiveOutputFile.isEmpty()) prompt(tr("Data written to %1.").arg(m_interactiveOutputFile)); else @@ -313,7 +316,7 @@ void QmlProfilerApplication::flush() void QmlProfilerApplication::output() { - if (m_profilerData.save(m_interactiveOutputFile)) { + if (m_profilerData->save(m_interactiveOutputFile)) { if (!m_interactiveOutputFile.isEmpty()) prompt(tr("Data written to %1.").arg(m_interactiveOutputFile)); else @@ -385,12 +388,12 @@ void QmlProfilerApplication::userCommand(const QString &command) if (cmd == Constants::CMD_RECORD || cmd == Constants::CMD_RECORD2) { m_pendingRequest = REQUEST_TOGGLE_RECORDING; - m_qmlProfilerClient.sendRecordingStatus(!m_recording); + m_qmlProfilerClient->sendRecordingStatus(!m_recording); } else if (cmd == Constants::CMD_QUIT || cmd == Constants::CMD_QUIT2) { m_pendingRequest = REQUEST_QUIT; if (m_recording) { prompt(tr("The application is still generating data. Really quit (y/n)?")); - } else if (!m_profilerData.isEmpty()) { + } else if (!m_profilerData->isEmpty()) { prompt(tr("There is still trace data in memory. Really quit (y/n)?")); } else { quit(); @@ -398,7 +401,7 @@ void QmlProfilerApplication::userCommand(const QString &command) } else if (cmd == Constants::CMD_OUTPUT || cmd == Constants::CMD_OUTPUT2) { if (m_recording) { prompt(tr("Cannot output while recording data.")); - } else if (m_profilerData.isEmpty()) { + } else if (m_profilerData->isEmpty()) { prompt(tr("No data was recorded so far.")); } else { m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; @@ -408,14 +411,14 @@ void QmlProfilerApplication::userCommand(const QString &command) } else if (cmd == Constants::CMD_CLEAR || cmd == Constants::CMD_CLEAR2) { if (m_recording) { prompt(tr("Cannot clear data while recording.")); - } else if (m_profilerData.isEmpty()) { + } else if (m_profilerData->isEmpty()) { prompt(tr("No data was recorded so far.")); } else { - m_profilerData.clear(); + m_profilerData->clear(); prompt(tr("Trace data cleared.")); } } else if (cmd == Constants::CMD_FLUSH || cmd == Constants::CMD_FLUSH2) { - if (!m_recording && m_profilerData.isEmpty()) { + if (!m_recording && m_profilerData->isEmpty()) { prompt(tr("No data was recorded so far.")); } else { m_interactiveOutputFile = args.length() > 0 ? args.at(0).toString() : m_outputFile; @@ -443,9 +446,9 @@ void QmlProfilerApplication::notifyTraceStarted() void QmlProfilerApplication::outputData() { - if (!m_profilerData.isEmpty()) { - m_profilerData.save(m_outputFile); - m_profilerData.clear(); + if (!m_profilerData->isEmpty()) { + m_profilerData->save(m_outputFile); + m_profilerData->clear(); } } @@ -454,7 +457,7 @@ void QmlProfilerApplication::run() if (m_runMode == LaunchMode) { if (!m_socketFile.isEmpty()) { logStatus(QString::fromLatin1("Listening on %1 ...").arg(m_socketFile)); - m_connection.startLocalServer(m_socketFile); + m_connection->startLocalServer(m_socketFile); } m_process = new QProcess(this); QStringList arguments; @@ -481,7 +484,7 @@ void QmlProfilerApplication::run() void QmlProfilerApplication::tryToConnect() { - Q_ASSERT(!m_connection.isConnected()); + Q_ASSERT(!m_connection->isConnected()); ++ m_connectionAttempts; if (!m_verbose && !(m_connectionAttempts % 5)) {// print every 5 seconds @@ -497,7 +500,7 @@ void QmlProfilerApplication::tryToConnect() if (m_socketFile.isEmpty()) { logStatus(QString::fromLatin1("Connecting to %1:%2 ...").arg(m_hostName).arg(m_port)); - m_connection.connectToHost(m_hostName, m_port); + m_connection->connectToHost(m_hostName, m_port); } } @@ -538,7 +541,7 @@ void QmlProfilerApplication::processFinished() if (!m_interactive) exit(exitCode); else - m_qmlProfilerClient.clearPendingData(); + m_qmlProfilerClient->clearAll(); } void QmlProfilerApplication::traceClientEnabledChanged(bool enabled) @@ -547,7 +550,7 @@ void QmlProfilerApplication::traceClientEnabledChanged(bool enabled) logStatus("Trace client is attached."); // blocked server is waiting for recording message from both clients // once the last one is connected, both messages should be sent - m_qmlProfilerClient.sendRecordingStatus(m_recording); + m_qmlProfilerClient->setRecording(m_recording); } } @@ -564,7 +567,7 @@ void QmlProfilerApplication::traceFinished() prompt(tr("Application stopped recording."), false); } - m_qmlProfilerClient.clearPendingData(); + m_qmlProfilerClient->clearEvents(); } void QmlProfilerApplication::prompt(const QString &line, bool ready) diff --git a/tools/qmlprofiler/qmlprofilerapplication.h b/tools/qmlprofiler/qmlprofilerapplication.h index 13f0f041f0..f7a8efd61b 100644 --- a/tools/qmlprofiler/qmlprofilerapplication.h +++ b/tools/qmlprofiler/qmlprofilerapplication.h @@ -105,9 +105,9 @@ private: bool m_recording; bool m_interactive; - QQmlDebugConnection m_connection; - QmlProfilerClient m_qmlProfilerClient; - QmlProfilerData m_profilerData; + QScopedPointer<QQmlDebugConnection> m_connection; + QScopedPointer<QmlProfilerClient> m_qmlProfilerClient; + QScopedPointer<QmlProfilerData> m_profilerData; QTimer m_connectTimer; uint m_connectionAttempts; }; diff --git a/tools/qmlprofiler/qmlprofilerclient.cpp b/tools/qmlprofiler/qmlprofilerclient.cpp index 0c031db914..b69c7e73e1 100644 --- a/tools/qmlprofiler/qmlprofilerclient.cpp +++ b/tools/qmlprofiler/qmlprofilerclient.cpp @@ -43,36 +43,26 @@ public: QmlProfilerClientPrivate(QQmlDebugConnection *connection, QmlProfilerData *data); QmlProfilerData *data; - - qint64 inProgressRanges; - QStack<qint64> rangeStartTimes[QQmlProfilerDefinitions::MaximumRangeType]; - QStack<QStringList> rangeDatas[QQmlProfilerDefinitions::MaximumRangeType]; - QStack<QQmlEventLocation> rangeLocations[QQmlProfilerDefinitions::MaximumRangeType]; - int rangeCount[QQmlProfilerDefinitions::MaximumRangeType]; - bool enabled; }; QmlProfilerClientPrivate::QmlProfilerClientPrivate(QQmlDebugConnection *connection, QmlProfilerData *data) : - QQmlProfilerClientPrivate(connection), data(data), inProgressRanges(0), enabled(false) + QQmlProfilerClientPrivate(connection, data), data(data), enabled(false) { - ::memset(rangeCount, 0, QQmlProfilerDefinitions::MaximumRangeType * sizeof(int)); } QmlProfilerClient::QmlProfilerClient(QQmlDebugConnection *connection, QmlProfilerData *data) : QQmlProfilerClient(*(new QmlProfilerClientPrivate(connection, data))) { -} - -void QmlProfilerClient::clearPendingData() -{ Q_D(QmlProfilerClient); - for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) { - d->rangeCount[i] = 0; - d->rangeDatas[i].clear(); - d->rangeLocations[i].clear(); - } + setRequestedFeatures(std::numeric_limits<quint64>::max()); + connect(this, &QQmlProfilerClient::traceStarted, + d->data, &QmlProfilerData::setTraceStartTime); + connect(this, &QQmlProfilerClient::traceFinished, + d->data, &QmlProfilerData::setTraceEndTime); + connect(this, &QQmlProfilerClient::complete, + d->data, &QmlProfilerData::complete); } void QmlProfilerClient::stateChanged(State state) @@ -84,119 +74,4 @@ void QmlProfilerClient::stateChanged(State state) } } -void QmlProfilerClient::traceStarted(qint64 time, int engineId) -{ - Q_UNUSED(engineId); - Q_D(QmlProfilerClient); - d->data->setTraceStartTime(time); - emit recordingStarted(); -} - -void QmlProfilerClient::traceFinished(qint64 time, int engineId) -{ - Q_UNUSED(engineId); - Q_D(QmlProfilerClient); - d->data->setTraceEndTime(time); -} - -void QmlProfilerClient::rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) -{ - Q_D(QmlProfilerClient); - d->rangeStartTimes[type].push(startTime); - d->inProgressRanges |= (static_cast<qint64>(1) << type); - ++d->rangeCount[type]; -} - -void QmlProfilerClient::rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QString &data) -{ - Q_UNUSED(time); - Q_D(QmlProfilerClient); - int count = d->rangeCount[type]; - if (count > 0) { - while (d->rangeDatas[type].count() < count) - d->rangeDatas[type].push(QStringList()); - d->rangeDatas[type][count - 1] << data; - } -} - -void QmlProfilerClient::rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) -{ - Q_UNUSED(time); - Q_D(QmlProfilerClient); - if (d->rangeCount[type] > 0) - d->rangeLocations[type].push(location); -} - -void QmlProfilerClient::rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) -{ - Q_D(QmlProfilerClient); - - if (d->rangeCount[type] == 0) { - emit error(tr("Spurious range end detected.")); - return; - } - - --d->rangeCount[type]; - if (d->inProgressRanges & (static_cast<qint64>(1) << type)) - d->inProgressRanges &= ~(static_cast<qint64>(1) << type); - QStringList data = d->rangeDatas[type].count() ? d->rangeDatas[type].pop() : QStringList(); - QQmlEventLocation location = d->rangeLocations[type].count() ? d->rangeLocations[type].pop() : - QQmlEventLocation(); - qint64 startTime = d->rangeStartTimes[type].pop(); - - if (d->rangeCount[type] == 0 && d->rangeDatas[type].count() + d->rangeStartTimes[type].count() - + d->rangeLocations[type].count() != 0) { - emit error(tr("Incorrectly nested range data")); - return; - } - - d->data->addQmlEvent(type, QQmlProfilerDefinitions::QmlBinding, startTime, endTime - startTime, - data, location); -} - -void QmlProfilerClient::animationFrame(qint64 time, int frameRate, int animationCount, int threadId) -{ - Q_D(QmlProfilerClient); - d->data->addFrameEvent(time, frameRate, animationCount, threadId); -} - -void QmlProfilerClient::sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - Q_D(QmlProfilerClient); - d->data->addSceneGraphFrameEvent(type, time, numericData1, numericData2, numericData3, - numericData4, numericData5); -} - -void QmlProfilerClient::pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2) -{ - Q_D(QmlProfilerClient); - d->data->addPixmapCacheEvent(type, time, url, numericData1, numericData2); -} - -void QmlProfilerClient::memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 amount) -{ - Q_D(QmlProfilerClient); - d->data->addMemoryEvent(type, time, amount); -} - -void QmlProfilerClient::inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - Q_D(QmlProfilerClient); - d->data->addInputEvent(type, time, a, b); -} - -void QmlProfilerClient::complete() -{ - Q_D(QmlProfilerClient); - d->data->complete(); -} - #include "moc_qmlprofilerclient.cpp" diff --git a/tools/qmlprofiler/qmlprofilerclient.h b/tools/qmlprofiler/qmlprofilerclient.h index a04a412bb0..b9d8ce241f 100644 --- a/tools/qmlprofiler/qmlprofilerclient.h +++ b/tools/qmlprofiler/qmlprofilerclient.h @@ -29,9 +29,9 @@ #ifndef QMLPROFILERCLIENT_H #define QMLPROFILERCLIENT_H -#include <private/qqmleventlocation_p.h> #include <private/qqmlprofilerclient_p.h> #include <private/qqmlprofilerdefinitions_p.h> +#include <private/qqmlprofilereventlocation_p.h> class QmlProfilerData; class QmlProfilerClientPrivate; @@ -42,32 +42,13 @@ class QmlProfilerClient : public QQmlProfilerClient public: QmlProfilerClient(QQmlDebugConnection *connection, QmlProfilerData *data); - void clearPendingData(); signals: void enabledChanged(bool enabled); - void recordingStarted(); void error(const QString &error); private: void stateChanged(State state) override; - - void traceStarted(qint64 time, int engineId) override; - void traceFinished(qint64 time, int engineId) override; - void rangeStart(QQmlProfilerDefinitions::RangeType type, qint64 startTime) override; - void rangeData(QQmlProfilerDefinitions::RangeType type, qint64 time, const QString &data) override; - void rangeLocation(QQmlProfilerDefinitions::RangeType type, qint64 time, - const QQmlEventLocation &location) override; - void rangeEnd(QQmlProfilerDefinitions::RangeType type, qint64 endTime) override; - void animationFrame(qint64 time, int frameRate, int animationCount, int threadId) override; - void sceneGraphEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5) override; - void pixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &url, int numericData1, int numericData2) override; - void memoryAllocation(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 amount) override; - void inputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b) override; - void complete() override; }; #endif // QMLPROFILERCLIENT_H diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 7dcfa4cdaa..32e03298da 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -34,6 +34,8 @@ #include <QFile> #include <QXmlStreamReader> #include <QRegularExpression> +#include <QQueue> +#include <QStack> #include <limits> @@ -60,72 +62,13 @@ static const char *MESSAGE_STRINGS[] = { "Complete", "PixmapCache", "SceneGraph", - "MemoryAllocation" + "MemoryAllocation", + "DebugMessage" }; Q_STATIC_ASSERT(sizeof(MESSAGE_STRINGS) == QQmlProfilerDefinitions::MaximumMessage * sizeof(const char *)); -struct QmlRangeEventData { - QmlRangeEventData() {} // never called - QmlRangeEventData(const QString &_displayName, int _detailType, const QString &_eventHashStr, - const QQmlEventLocation &_location, const QString &_details, - QQmlProfilerDefinitions::Message _message, - QQmlProfilerDefinitions::RangeType _rangeType) - : displayName(_displayName), eventHashStr(_eventHashStr), location(_location), - details(_details), message(_message), rangeType(_rangeType), detailType(_detailType) {} - QString displayName; - QString eventHashStr; - QQmlEventLocation location; - QString details; - QQmlProfilerDefinitions::Message message; - QQmlProfilerDefinitions::RangeType rangeType; - int detailType; // can be BindingType, PixmapCacheEventType or SceneGraphFrameType -}; - -struct QmlRangeEventStartInstance { - QmlRangeEventStartInstance() {} // never called - QmlRangeEventStartInstance(qint64 _startTime, qint64 _duration, int _frameRate, - int _animationCount, int _threadId, QmlRangeEventData *_data) - : startTime(_startTime), duration(_duration), frameRate(_frameRate), - animationCount(_animationCount), threadId(_threadId), numericData4(-1), numericData5(-1), - data(_data) - { } - - QmlRangeEventStartInstance(qint64 _startTime, qint64 _numericData1, qint64 _numericData2, - qint64 _numericData3, qint64 _numericData4, qint64 _numericData5, - QmlRangeEventData *_data) - : startTime(_startTime), duration(-1), numericData1(_numericData1), - numericData2(_numericData2), numericData3(_numericData3), numericData4(_numericData4), - numericData5(_numericData5), data(_data) - { } - qint64 startTime; - qint64 duration; - union { - int frameRate; - int inputType; - qint64 numericData1; - }; - union { - int animationCount; - int inputA; - qint64 numericData2; - }; - union { - int threadId; - int inputB; - qint64 numericData3; - }; - qint64 numericData4; - qint64 numericData5; - QmlRangeEventData *data; -}; - -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(QmlRangeEventData, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QmlRangeEventStartInstance, Q_MOVABLE_TYPE); -QT_END_NAMESPACE - ///////////////////////////////////////////////////////////////// class QmlProfilerDataPrivate { @@ -133,8 +76,8 @@ public: QmlProfilerDataPrivate(QmlProfilerData *qq){ Q_UNUSED(qq); } // data storage - QHash<QString, QmlRangeEventData *> eventDescriptions; - QVector<QmlRangeEventStartInstance> startInstanceList; + QVector<QQmlProfilerEventType> eventTypes; + QVector<QQmlProfilerEvent> events; qint64 traceStartTime; qint64 traceEndTime; @@ -146,7 +89,7 @@ public: ///////////////////////////////////////////////////////////////// QmlProfilerData::QmlProfilerData(QObject *parent) : - QObject(parent),d(new QmlProfilerDataPrivate(this)) + QQmlProfilerEventReceiver(parent), d(new QmlProfilerDataPrivate(this)) { d->state = Empty; clear(); @@ -160,9 +103,8 @@ QmlProfilerData::~QmlProfilerData() void QmlProfilerData::clear() { - qDeleteAll(d->eventDescriptions); - d->eventDescriptions.clear(); - d->startInstanceList.clear(); + d->eventTypes.clear(); + d->events.clear(); d->traceEndTime = std::numeric_limits<qint64>::min(); d->traceStartTime = std::numeric_limits<qint64>::max(); @@ -171,15 +113,6 @@ void QmlProfilerData::clear() setState(Empty); } -QString QmlProfilerData::getHashStringForQmlEvent(const QQmlEventLocation &location, int eventType) -{ - return QString(QStringLiteral("%1:%2:%3:%4")).arg( - location.filename, - QString::number(location.line), - QString::number(location.column), - QString::number(eventType)); -} - QString QmlProfilerData::qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type) { if (type * sizeof(char *) < sizeof(RANGE_TYPE_STRINGS)) @@ -218,20 +151,20 @@ qint64 QmlProfilerData::traceEndTime() const return d->traceEndTime; } -void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, - QQmlProfilerDefinitions::BindingType bindingType, - qint64 startTime, - qint64 duration, - const QStringList &data, - const QQmlEventLocation &location) +void QmlProfilerData::addEvent(const QQmlProfilerEvent &event) { setState(AcquiringData); + d->events.append(event); +} + +void QmlProfilerData::addEventType(const QQmlProfilerEventType &type) +{ + QQmlProfilerEventType newType = type; QString details; // generate details string - if (!data.isEmpty()) { - details = data.join(QLatin1Char(' ')).replace( - QLatin1Char('\n'), QLatin1Char(' ')).simplified(); + if (!type.data().isEmpty()) { + details = type.data().simplified(); QRegularExpression rewrite(QStringLiteral("^\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)$")); QRegularExpressionMatch match = rewrite.match(details); if (match.hasMatch()) { @@ -241,223 +174,132 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1); } - QQmlEventLocation eventLocation = location; - QString displayName, eventHashStr; - // generate hash - if (eventLocation.filename.isEmpty()) { - displayName = tr("<bytecode>"); - eventHashStr = getHashStringForQmlEvent(eventLocation, type); - } else { - const QString filePath = QUrl(eventLocation.filename).path(); - displayName = filePath.midRef( - filePath.lastIndexOf(QLatin1Char('/')) + 1) + - QLatin1Char(':') + QString::number(eventLocation.line); - eventHashStr = getHashStringForQmlEvent(eventLocation, type); - } - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(displayName, bindingType, eventHashStr, location, details, - QQmlProfilerDefinitions::MaximumMessage, type); - d->eventDescriptions.insert(eventHashStr, newEvent); - } - - QmlRangeEventStartInstance rangeEventStartInstance(startTime, duration, -1, -1, -1, newEvent); - - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addFrameEvent(qint64 time, int framerate, int animationcount, int threadId) -{ - setState(AcquiringData); - - QString details = tr("Animation Timer Update"); - QString displayName = tr("<Animation Update>"); - QString eventHashStr = displayName; - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(displayName, QQmlProfilerDefinitions::AnimationFrame, - eventHashStr, - QQmlEventLocation(), details, - QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); - } - - QmlRangeEventStartInstance rangeEventStartInstance(time, -1, framerate, animationcount, - threadId, newEvent); - - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, - qint64 time, qint64 numericData1, qint64 numericData2, - qint64 numericData3, qint64 numericData4, - qint64 numericData5) -{ - setState(AcquiringData); - - QString eventHashStr = QString::fromLatin1("SceneGraph:%1").arg(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(QStringLiteral("<SceneGraph>"), type, eventHashStr, - QQmlEventLocation(), QString(), - QQmlProfilerDefinitions::SceneGraphFrame, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); - } + newType.setData(details); - QmlRangeEventStartInstance rangeEventStartInstance(time, numericData1, numericData2, - numericData3, numericData4, numericData5, - newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, - qint64 time, const QString &location, - int numericData1, int numericData2) -{ - setState(AcquiringData); - - QString filePath = QUrl(location).path(); - - const QString eventHashStr = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) - + QLatin1Char(':') + QString::number(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, - QQmlEventLocation(location, -1, -1), QString(), - QQmlProfilerDefinitions::PixmapCacheEvent, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); + QString displayName; + switch (type.message()) { + case QQmlProfilerDefinitions::Event: { + switch (type.detailType()) { + case QQmlProfilerDefinitions::Mouse: + case QQmlProfilerDefinitions::Key: + displayName = QString::fromLatin1("Input:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::AnimationFrame: + displayName = QString::fromLatin1("AnimationFrame"); + break; + default: + displayName = QString::fromLatin1("Unknown"); + } + break; } - - QmlRangeEventStartInstance rangeEventStartInstance(time, numericData1, numericData2, 0, 0, 0, - newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, - qint64 size) -{ - setState(AcquiringData); - QString eventHashStr = QString::fromLatin1("MemoryAllocation:%1").arg(type); - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(eventHashStr, type, eventHashStr, QQmlEventLocation(), - QString(), QQmlProfilerDefinitions::MemoryAllocation, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); + case QQmlProfilerDefinitions::RangeStart: + case QQmlProfilerDefinitions::RangeData: + case QQmlProfilerDefinitions::RangeLocation: + case QQmlProfilerDefinitions::RangeEnd: + case QQmlProfilerDefinitions::Complete: + Q_UNREACHABLE(); + break; + case QQmlProfilerDefinitions::PixmapCacheEvent: { + const QString filePath = QUrl(type.location().filename()).path(); + displayName = filePath.midRef(filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(type.detailType()); + break; } - QmlRangeEventStartInstance rangeEventStartInstance(time, size, 0, 0, 0, 0, newEvent); - d->startInstanceList.append(rangeEventStartInstance); -} - -void QmlProfilerData::addInputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, - int a, int b) -{ - setState(AcquiringData); - - QQmlProfilerDefinitions::EventType eventType; - switch (type) { - case QQmlProfilerDefinitions::InputKeyPress: - case QQmlProfilerDefinitions::InputKeyRelease: - case QQmlProfilerDefinitions::InputKeyUnknown: - eventType = QQmlProfilerDefinitions::Key; + case QQmlProfilerDefinitions::SceneGraphFrame: + displayName = QString::fromLatin1("SceneGraph:%1").arg(type.detailType()); break; - default: - eventType = QQmlProfilerDefinitions::Mouse; + case QQmlProfilerDefinitions::MemoryAllocation: + displayName = QString::fromLatin1("MemoryAllocation:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::DebugMessage: + displayName = QString::fromLatin1("DebugMessage:%1").arg(type.detailType()); + break; + case QQmlProfilerDefinitions::MaximumMessage: { + const QQmlProfilerEventLocation eventLocation = type.location(); + // generate hash + if (eventLocation.filename().isEmpty()) { + displayName = QString::fromLatin1("Unknown"); + } else { + const QString filePath = QUrl(eventLocation.filename()).path(); + displayName = filePath.midRef( + filePath.lastIndexOf(QLatin1Char('/')) + 1) + + QLatin1Char(':') + QString::number(eventLocation.line()); + } break; } - - QString eventHashStr = QString::fromLatin1("Input:%1").arg(eventType); - - QmlRangeEventData *newEvent; - if (d->eventDescriptions.contains(eventHashStr)) { - newEvent = d->eventDescriptions[eventHashStr]; - } else { - newEvent = new QmlRangeEventData(QString(), eventType, eventHashStr, QQmlEventLocation(), - QString(), QQmlProfilerDefinitions::Event, - QQmlProfilerDefinitions::MaximumRangeType); - d->eventDescriptions.insert(eventHashStr, newEvent); } - d->startInstanceList.append(QmlRangeEventStartInstance(time, -1, type, a, b, newEvent)); + newType.setDisplayName(displayName); + d->eventTypes.append(newType); } void QmlProfilerData::computeQmlTime() { // compute levels - QHash<int, qint64> endtimesPerLevel; - int minimumLevel = 1; - int level = minimumLevel; - - for (int i = 0; i < d->startInstanceList.count(); i++) { - qint64 st = d->startInstanceList.at(i).startTime; + qint64 level0Start = -1; + int level = 0; - if (d->startInstanceList.at(i).data->rangeType == QQmlProfilerDefinitions::Painting) { + for (const QQmlProfilerEvent &event : qAsConst(d->events)) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + if (type.message() != QQmlProfilerDefinitions::MaximumMessage) continue; - } - - // general level - if (endtimesPerLevel.value(level) > st) { - level++; - } else { - while (level > minimumLevel && endtimesPerLevel[level-1] <= st) - level--; - } - endtimesPerLevel[level] = st + d->startInstanceList.at(i).duration; - if (level == minimumLevel) { - d->qmlMeasuredTime += d->startInstanceList.at(i).duration; + switch (type.rangeType()) { + case QQmlProfilerDefinitions::Compiling: + case QQmlProfilerDefinitions::Creating: + case QQmlProfilerDefinitions::Binding: + case QQmlProfilerDefinitions::HandlingSignal: + case QQmlProfilerDefinitions::Javascript: + switch (event.rangeStage()) { + case QQmlProfilerDefinitions::RangeStart: + if (level++ == 0) + level0Start = event.timestamp(); + break; + case QQmlProfilerDefinitions::RangeEnd: + if (--level == 0) + d->qmlMeasuredTime += event.timestamp() - level0Start; + break; + default: + break; + } + break; + default: + break; } } } -bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2) +bool compareStartTimes(const QQmlProfilerEvent &t1, const QQmlProfilerEvent &t2) { - return t1.startTime < t2.startTime; + return t1.timestamp() < t2.timestamp(); } void QmlProfilerData::sortStartTimes() { - if (d->startInstanceList.count() < 2) + if (d->events.count() < 2) return; // assuming startTimes is partially sorted // identify blocks of events and sort them with quicksort - QVector<QmlRangeEventStartInstance>::iterator itFrom = d->startInstanceList.end() - 2; - QVector<QmlRangeEventStartInstance>::iterator itTo = d->startInstanceList.end() - 1; + QVector<QQmlProfilerEvent>::iterator itFrom = d->events.end() - 2; + QVector<QQmlProfilerEvent>::iterator itTo = d->events.end() - 1; - while (itFrom != d->startInstanceList.begin() && itTo != d->startInstanceList.begin()) { + while (itFrom != d->events.begin() && itTo != d->events.begin()) { // find block to sort - while ( itFrom != d->startInstanceList.begin() - && itTo->startTime > itFrom->startTime ) { + while (itFrom != d->events.begin() && itTo->timestamp() > itFrom->timestamp()) { --itTo; itFrom = itTo - 1; } // if we're at the end of the list - if (itFrom == d->startInstanceList.begin()) + if (itFrom == d->events.begin()) break; // find block length - while ( itFrom != d->startInstanceList.begin() - && itTo->startTime <= itFrom->startTime ) + while (itFrom != d->events.begin() && itTo->timestamp() <= itFrom->timestamp()) --itFrom; - if (itTo->startTime <= itFrom->startTime) + if (itTo->timestamp() <= itFrom->timestamp()) std::sort(itFrom, itTo + 1, compareStartTimes); else std::sort(itFrom + 1, itTo + 1, compareStartTimes); @@ -479,9 +321,88 @@ void QmlProfilerData::complete() bool QmlProfilerData::isEmpty() const { - return d->startInstanceList.isEmpty(); + return d->events.isEmpty(); } +struct StreamWriter { + QString error; + + StreamWriter(const QString &filename) + { + if (!filename.isEmpty()) { + file.setFileName(filename); + if (!file.open(QIODevice::WriteOnly)) { + error = QmlProfilerData::tr("Could not open %1 for writing").arg(filename); + return; + } + } else { + if (!file.open(stdout, QIODevice::WriteOnly)) { + error = QmlProfilerData::tr("Could not open stdout for writing"); + return; + } + } + + stream.setDevice(&file); + stream.setAutoFormatting(true); + stream.writeStartDocument(); + writeStartElement("trace"); + } + + ~StreamWriter() { + writeEndElement(); + stream.writeEndDocument(); + file.close(); + } + + template<typename Number> + void writeAttribute(const char *name, Number number) + { + stream.writeAttribute(QLatin1String(name), QString::number(number)); + } + + void writeAttribute(const char *name, const char *value) + { + stream.writeAttribute(QLatin1String(name), QLatin1String(value)); + } + + void writeAttribute(const char *name, const QQmlProfilerEvent &event, int i, bool printZero = true) + { + const qint64 number = event.number<qint64>(i); + if (printZero || number != 0) + writeAttribute(name, number); + } + + template<typename Number> + void writeTextElement(const char *name, Number number) + { + writeTextElement(name, QString::number(number)); + } + + void writeTextElement(const char *name, const char *value) + { + stream.writeTextElement(QLatin1String(name), QLatin1String(value)); + } + + void writeTextElement(const char *name, const QString &value) + { + stream.writeTextElement(QLatin1String(name), value); + } + + void writeStartElement(const char *name) + { + stream.writeStartElement(QLatin1String(name)); + } + + void writeEndElement() + { + stream.writeEndElement(); + } + +private: + QFile file; + QXmlStreamWriter stream; +}; + bool QmlProfilerData::save(const QString &filename) { if (isEmpty()) { @@ -489,157 +410,178 @@ bool QmlProfilerData::save(const QString &filename) return false; } - QFile file; - if (!filename.isEmpty()) { - file.setFileName(filename); - if (!file.open(QIODevice::WriteOnly)) { - emit error(tr("Could not open %1 for writing").arg(filename)); - return false; - } - } else { - if (!file.open(stdout, QIODevice::WriteOnly)) { - emit error(tr("Could not open stdout for writing")); - return false; - } + StreamWriter stream(filename); + if (!stream.error.isEmpty()) { + emit error(stream.error); + return false; } - QXmlStreamWriter stream(&file); - stream.setAutoFormatting(true); - stream.writeStartDocument(); - - stream.writeStartElement(QStringLiteral("trace")); - stream.writeAttribute(QStringLiteral("version"), PROFILER_FILE_VERSION); - - stream.writeAttribute(QStringLiteral("traceStart"), QString::number(traceStartTime())); - stream.writeAttribute(QStringLiteral("traceEnd"), QString::number(traceEndTime())); - - stream.writeStartElement(QStringLiteral("eventData")); - stream.writeAttribute(QStringLiteral("totalTime"), QString::number(d->qmlMeasuredTime)); - - const auto eventDescriptionsKeys = d->eventDescriptions.keys(); - for (auto it = d->eventDescriptions.cbegin(), end = d->eventDescriptions.cend(); - it != end; ++it) { - const QmlRangeEventData *eventData = it.value(); - stream.writeStartElement(QStringLiteral("event")); - stream.writeAttribute(QStringLiteral("index"), QString::number( - eventDescriptionsKeys.indexOf(eventData->eventHashStr))); - if (!eventData->displayName.isEmpty()) - stream.writeTextElement(QStringLiteral("displayname"), eventData->displayName); - if (eventData->rangeType != QQmlProfilerDefinitions::MaximumRangeType) - stream.writeTextElement(QStringLiteral("type"), - qmlRangeTypeAsString(eventData->rangeType)); - else - stream.writeTextElement(QStringLiteral("type"), - qmlMessageAsString(eventData->message)); - if (!eventData->location.filename.isEmpty()) - stream.writeTextElement(QStringLiteral("filename"), eventData->location.filename); - if (eventData->location.line >= 0) - stream.writeTextElement(QStringLiteral("line"), - QString::number(eventData->location.line)); - if (eventData->location.column >= 0) - stream.writeTextElement(QStringLiteral("column"), - QString::number(eventData->location.column)); - if (!eventData->details.isEmpty()) - stream.writeTextElement(QStringLiteral("details"), eventData->details); - if (eventData->rangeType == QQmlProfilerDefinitions::Binding) - stream.writeTextElement(QStringLiteral("bindingType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::Event) { - switch (eventData->detailType) { + stream.writeAttribute("version", PROFILER_FILE_VERSION); + stream.writeAttribute("traceStart", traceStartTime()); + stream.writeAttribute("traceEnd", traceEndTime()); + + stream.writeStartElement("eventData"); + stream.writeAttribute("totalTime", d->qmlMeasuredTime); + + for (int typeIndex = 0, end = d->eventTypes.size(); typeIndex < end; ++typeIndex) { + const QQmlProfilerEventType &eventData = d->eventTypes.at(typeIndex); + stream.writeStartElement("event"); + stream.writeAttribute("index", typeIndex); + if (!eventData.displayName().isEmpty()) + stream.writeTextElement("displayname", eventData.displayName()); + + stream.writeTextElement("type", + eventData.rangeType() == QQmlProfilerDefinitions::MaximumRangeType + ? qmlMessageAsString(eventData.message()) + : qmlRangeTypeAsString(eventData.rangeType())); + + const QQmlProfilerEventLocation location = eventData.location(); + if (!location.filename().isEmpty()) + stream.writeTextElement("filename", location.filename()); + if (location.line() >= 0) + stream.writeTextElement("line", location.line()); + if (location.column() >= 0) + stream.writeTextElement("column", location.column()); + if (!eventData.data().isEmpty()) + stream.writeTextElement("details", eventData.data()); + if (eventData.rangeType() == QQmlProfilerDefinitions::Binding) + stream.writeTextElement("bindingType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::Event) { + switch (eventData.detailType()) { case QQmlProfilerDefinitions::AnimationFrame: - stream.writeTextElement(QStringLiteral("animationFrame"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("animationFrame", eventData.detailType()); break; case QQmlProfilerDefinitions::Key: - stream.writeTextElement(QStringLiteral("keyEvent"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("keyEvent", eventData.detailType()); break; case QQmlProfilerDefinitions::Mouse: - stream.writeTextElement(QStringLiteral("mouseEvent"), - QString::number((int)eventData->detailType)); + stream.writeTextElement("mouseEvent", eventData.detailType()); break; } - } else if (eventData->message == QQmlProfilerDefinitions::PixmapCacheEvent) - stream.writeTextElement(QStringLiteral("cacheEventType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::SceneGraphFrame) - stream.writeTextElement(QStringLiteral("sgEventType"), - QString::number((int)eventData->detailType)); - else if (eventData->message == QQmlProfilerDefinitions::MemoryAllocation) - stream.writeTextElement(QStringLiteral("memoryEventType"), - QString::number((int)eventData->detailType)); + } else if (eventData.message() == QQmlProfilerDefinitions::PixmapCacheEvent) + stream.writeTextElement("cacheEventType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::SceneGraphFrame) + stream.writeTextElement("sgEventType", eventData.detailType()); + else if (eventData.message() == QQmlProfilerDefinitions::MemoryAllocation) + stream.writeTextElement("memoryEventType", eventData.detailType()); stream.writeEndElement(); } stream.writeEndElement(); // eventData - stream.writeStartElement(QStringLiteral("profilerDataModel")); - for (const QmlRangeEventStartInstance &event : qAsConst(d->startInstanceList)) { - stream.writeStartElement(QStringLiteral("range")); - stream.writeAttribute(QStringLiteral("startTime"), QString::number(event.startTime)); - if (event.duration >= 0) - stream.writeAttribute(QStringLiteral("duration"), - QString::number(event.duration)); - stream.writeAttribute(QStringLiteral("eventIndex"), QString::number( - eventDescriptionsKeys.indexOf(event.data->eventHashStr))); - if (event.data->message == QQmlProfilerDefinitions::Event) { - if (event.data->detailType == QQmlProfilerDefinitions::AnimationFrame) { + stream.writeStartElement("profilerDataModel"); + + auto sendEvent = [&](const QQmlProfilerEvent &event, qint64 duration = 0) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + stream.writeStartElement("range"); + stream.writeAttribute("startTime", event.timestamp()); + if (duration != 0) + stream.writeAttribute("duration", duration); + stream.writeAttribute("eventIndex", event.typeIndex()); + if (type.message() == QQmlProfilerDefinitions::Event) { + if (type.detailType() == QQmlProfilerDefinitions::AnimationFrame) { // special: animation frame - stream.writeAttribute(QStringLiteral("framerate"), QString::number(event.frameRate)); - stream.writeAttribute(QStringLiteral("animationcount"), - QString::number(event.animationCount)); - stream.writeAttribute(QStringLiteral("thread"), QString::number(event.threadId)); - } else if (event.data->detailType == QQmlProfilerDefinitions::Key || - event.data->detailType == QQmlProfilerDefinitions::Mouse) { + stream.writeAttribute("framerate", event, 0); + stream.writeAttribute("animationcount", event, 1); + stream.writeAttribute("thread", event, 2); + } else if (type.detailType() == QQmlProfilerDefinitions::Key || + type.detailType() == QQmlProfilerDefinitions::Mouse) { // numerical value here, to keep the format a bit more compact - stream.writeAttribute(QStringLiteral("type"), - QString::number(event.inputType)); - stream.writeAttribute(QStringLiteral("data1"), - QString::number(event.inputA)); - stream.writeAttribute(QStringLiteral("data2"), - QString::number(event.inputB)); + stream.writeAttribute("type", event, 0); + stream.writeAttribute("data1", event, 1); + stream.writeAttribute("data2", event, 2); } - } else if (event.data->message == QQmlProfilerDefinitions::PixmapCacheEvent) { + } else if (type.message() == QQmlProfilerDefinitions::PixmapCacheEvent) { // special: pixmap cache event - if (event.data->detailType == QQmlProfilerDefinitions::PixmapSizeKnown) { - stream.writeAttribute(QStringLiteral("width"), - QString::number(event.numericData1)); - stream.writeAttribute(QStringLiteral("height"), - QString::number(event.numericData2)); - } else if (event.data->detailType == - QQmlProfilerDefinitions::PixmapReferenceCountChanged || - event.data->detailType == - QQmlProfilerDefinitions::PixmapCacheCountChanged) { - stream.writeAttribute(QStringLiteral("refCount"), - QString::number(event.numericData1)); + if (type.detailType() == QQmlProfilerDefinitions::PixmapSizeKnown) { + stream.writeAttribute("width", event, 0); + stream.writeAttribute("height", event, 1); + } else if (type.detailType() == QQmlProfilerDefinitions::PixmapReferenceCountChanged + || type.detailType() == QQmlProfilerDefinitions::PixmapCacheCountChanged) { + stream.writeAttribute("refCount", event, 1); } - } else if (event.data->message == QQmlProfilerDefinitions::SceneGraphFrame) { - // special: scenegraph frame events - if (event.numericData1 > 0) - stream.writeAttribute(QStringLiteral("timing1"), - QString::number(event.numericData1)); - if (event.numericData2 > 0) - stream.writeAttribute(QStringLiteral("timing2"), - QString::number(event.numericData2)); - if (event.numericData3 > 0) - stream.writeAttribute(QStringLiteral("timing3"), - QString::number(event.numericData3)); - if (event.numericData4 > 0) - stream.writeAttribute(QStringLiteral("timing4"), - QString::number(event.numericData4)); - if (event.numericData5 > 0) - stream.writeAttribute(QStringLiteral("timing5"), - QString::number(event.numericData5)); - } else if (event.data->message == QQmlProfilerDefinitions::MemoryAllocation) { - stream.writeAttribute(QStringLiteral("amount"), QString::number(event.numericData1)); + } else if (type.message() == QQmlProfilerDefinitions::SceneGraphFrame) { + stream.writeAttribute("timing1", event, 0, false); + stream.writeAttribute("timing2", event, 1, false); + stream.writeAttribute("timing3", event, 2, false); + stream.writeAttribute("timing4", event, 3, false); + stream.writeAttribute("timing5", event, 4, false); + } else if (type.message() == QQmlProfilerDefinitions::MemoryAllocation) { + stream.writeAttribute("amount", event, 0); } stream.writeEndElement(); + }; + + QQueue<QQmlProfilerEvent> pointEvents; + QQueue<QQmlProfilerEvent> rangeStarts[QQmlProfilerDefinitions::MaximumRangeType]; + QStack<qint64> rangeEnds[QQmlProfilerDefinitions::MaximumRangeType]; + int level = 0; + + auto sendPending = [&]() { + forever { + int minimum = QQmlProfilerDefinitions::MaximumRangeType; + qint64 minimumTime = std::numeric_limits<qint64>::max(); + for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) { + const QQueue<QQmlProfilerEvent> &starts = rangeStarts[i]; + if (starts.isEmpty()) + continue; + if (starts.head().timestamp() < minimumTime) { + minimumTime = starts.head().timestamp(); + minimum = i; + } + } + if (minimum == QQmlProfilerDefinitions::MaximumRangeType) + break; + + while (!pointEvents.isEmpty() && pointEvents.front().timestamp() < minimumTime) + sendEvent(pointEvents.dequeue()); + + sendEvent(rangeStarts[minimum].dequeue(), + rangeEnds[minimum].pop() - minimumTime); + } + }; + + for (const QQmlProfilerEvent &event : qAsConst(d->events)) { + const QQmlProfilerEventType &type = d->eventTypes.at(event.typeIndex()); + + if (type.rangeType() != QQmlProfilerDefinitions::MaximumRangeType) { + QQueue<QQmlProfilerEvent> &starts = rangeStarts[type.rangeType()]; + switch (event.rangeStage()) { + case QQmlProfilerDefinitions::RangeStart: { + ++level; + starts.enqueue(event); + break; + } + case QQmlProfilerDefinitions::RangeEnd: { + QStack<qint64> &ends = rangeEnds[type.rangeType()]; + if (starts.length() > ends.length()) { + ends.push(event.timestamp()); + if (--level == 0) + sendPending(); + } + break; + } + default: + break; + } + } else { + if (level == 0) + sendEvent(event); + else + pointEvents.enqueue(event); + } } - stream.writeEndElement(); // profilerDataModel - stream.writeEndElement(); // trace - stream.writeEndDocument(); + for (int i = 0; i < QQmlProfilerDefinitions::MaximumRangeType; ++i) { + while (rangeEnds[i].length() < rangeStarts[i].length()) { + rangeEnds[i].push(d->traceEndTime); + --level; + } + } + + sendPending(); + + stream.writeEndElement(); // profilerDataModel - file.close(); return true; } @@ -683,4 +625,9 @@ void QmlProfilerData::setState(QmlProfilerData::State state) return; } +int QmlProfilerData::numLoadedEventTypes() const +{ + return d->eventTypes.length(); +} + #include "moc_qmlprofilerdata.cpp" diff --git a/tools/qmlprofiler/qmlprofilerdata.h b/tools/qmlprofiler/qmlprofilerdata.h index 00ef037071..2be0b73aee 100644 --- a/tools/qmlprofiler/qmlprofilerdata.h +++ b/tools/qmlprofiler/qmlprofilerdata.h @@ -29,13 +29,14 @@ #ifndef QMLPROFILERDATA_H #define QMLPROFILERDATA_H -#include <private/qqmleventlocation_p.h> #include <private/qqmlprofilerdefinitions_p.h> +#include <private/qqmlprofilereventlocation_p.h> +#include <private/qqmlprofilereventreceiver_p.h> #include <QObject> class QmlProfilerDataPrivate; -class QmlProfilerData : public QObject +class QmlProfilerData : public QQmlProfilerEventReceiver { Q_OBJECT public: @@ -49,7 +50,11 @@ public: explicit QmlProfilerData(QObject *parent = 0); ~QmlProfilerData(); - static QString getHashStringForQmlEvent(const QQmlEventLocation &location, int eventType); + int numLoadedEventTypes() const override; + void addEventType(const QQmlProfilerEventType &type) override; + void addEvent(const QQmlProfilerEvent &event) override; + + static QString getHashStringForQmlEvent(const QQmlProfilerEventLocation &location, int eventType); static QString qmlRangeTypeAsString(QQmlProfilerDefinitions::RangeType type); static QString qmlMessageAsString(QQmlProfilerDefinitions::Message type); @@ -61,18 +66,6 @@ public: void clear(); void setTraceEndTime(qint64 time); void setTraceStartTime(qint64 time); - void addQmlEvent(QQmlProfilerDefinitions::RangeType type, - QQmlProfilerDefinitions::BindingType bindingType, - qint64 startTime, qint64 duration, const QStringList &data, - const QQmlEventLocation &location); - void addFrameEvent(qint64 time, int framerate, int animationcount, int threadId); - void addSceneGraphFrameEvent(QQmlProfilerDefinitions::SceneGraphFrameType type, qint64 time, - qint64 numericData1, qint64 numericData2, qint64 numericData3, - qint64 numericData4, qint64 numericData5); - void addPixmapCacheEvent(QQmlProfilerDefinitions::PixmapEventType type, qint64 time, - const QString &location, int numericData1, int numericData2); - void addMemoryEvent(QQmlProfilerDefinitions::MemoryType type, qint64 time, qint64 size); - void addInputEvent(QQmlProfilerDefinitions::InputEventType type, qint64 time, int a, int b); void complete(); bool save(const QString &filename); |