Commit 31467e86 authored by Martin Jones's avatar Martin Jones Committed by Qt by Nokia

Allow threaded compilation in an async Loader

Enables threaded compilation for a Loader "source".

Change-Id: I2d60a3ace07aab58f3b8f069e45a2864178c959f
Reviewed-by: default avatarChris Adams <christopher.adams@nokia.com>
parent b634c196
......@@ -280,6 +280,16 @@ static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
\value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}.
*/
/*!
\enum QQmlComponent::CompilationMode
Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
\value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
This is not always possible, e.g. remote URLs will always load asynchronously.
\value Asynchronous Load/compile the component in a background thread.
*/
void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
{
Q_Q(QQmlComponent);
......@@ -288,8 +298,10 @@ void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
fromTypeData(typeData);
typeData = 0;
progress = 1.0;
emit q->statusChanged(q->status());
emit q->progressChanged(progress);
}
void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
......@@ -476,7 +488,24 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *paren
{
Q_D(QQmlComponent);
d->engine = engine;
loadUrl(url);
d->loadUrl(url);
}
/*!
Create a QQmlComponent from the given \a url and give it the
specified \a parent and \a engine. If \a mode is \l Asynchronous,
the component will be loaded and compiled asynchronously.
Ensure that the URL provided is full and correct, in particular, use
\l QUrl::fromLocalFile() when loading a file from the local filesystem.
\sa loadUrl()
*/QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode, QObject *parent)
: QObject(*(new QQmlComponentPrivate), parent)
{
Q_D(QQmlComponent);
d->engine = engine;
d->loadUrl(url, mode);
}
/*!
......@@ -491,7 +520,23 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
{
Q_D(QQmlComponent);
d->engine = engine;
loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
}
/*!
Create a QQmlComponent from the given \a fileName and give it the specified
\a parent and \a engine. If \a mode is \l Asynchronous,
the component will be loaded and compiled asynchronously.
\sa loadUrl()
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
CompilationMode mode, QObject *parent)
: QObject(*(new QQmlComponentPrivate), parent)
{
Q_D(QQmlComponent);
d->engine = engine;
d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode);
}
/*!
......@@ -558,35 +603,63 @@ QQmlContext *QQmlComponent::creationContext() const
void QQmlComponent::loadUrl(const QUrl &url)
{
Q_D(QQmlComponent);
d->loadUrl(url);
}
d->clear();
/*!
Load the QQmlComponent from the provided \a url.
If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
Ensure that the URL provided is full and correct, in particular, use
\l QUrl::fromLocalFile() when loading a file from the local filesystem.
*/
void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
{
Q_D(QQmlComponent);
d->loadUrl(url, mode);
}
void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
{
Q_Q(QQmlComponent);
clear();
if ((url.isRelative() && !url.isEmpty())
|| url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
d->url = d->engine->baseUrl().resolved(url);
if ((newUrl.isRelative() && !newUrl.isEmpty())
|| newUrl.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
url = engine->baseUrl().resolved(newUrl);
else
d->url = url;
url = newUrl;
if (url.isEmpty()) {
if (newUrl.isEmpty()) {
QQmlError error;
error.setDescription(tr("Invalid empty URL"));
d->state.errors << error;
error.setDescription(q->tr("Invalid empty URL"));
state.errors << error;
return;
}
QQmlTypeData *data = QQmlEnginePrivate::get(d->engine)->typeLoader.get(d->url);
if (progress != 0.0) {
progress = 0.0;
emit q->progressChanged(progress);
}
QQmlDataLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
? QQmlDataLoader::Asynchronous
: QQmlDataLoader::PreferSynchronous;
QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.get(url, loaderMode);
if (data->isCompleteOrError()) {
d->fromTypeData(data);
d->progress = 1.0;
fromTypeData(data);
progress = 1.0;
} else {
d->typeData = data;
d->typeData->registerCallback(d);
d->progress = data->progress();
typeData = data;
typeData->registerCallback(this);
progress = data->progress();
}
emit statusChanged(status());
emit progressChanged(d->progress);
emit q->statusChanged(q->status());
if (progress != 0.0)
emit q->progressChanged(progress);
}
/*!
......
......@@ -73,10 +73,15 @@ class Q_QML_EXPORT QQmlComponent : public QObject
Q_PROPERTY(QUrl url READ url CONSTANT)
public:
Q_ENUMS(CompilationMode)
enum CompilationMode { PreferSynchronous, Asynchronous };
QQmlComponent(QObject *parent = 0);
QQmlComponent(QQmlEngine *, QObject *parent=0);
QQmlComponent(QQmlEngine *, const QString &fileName, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QString &fileName, CompilationMode mode, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QUrl &url, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QUrl &url, CompilationMode mode, QObject *parent = 0);
virtual ~QQmlComponent();
Q_ENUMS(Status)
......@@ -108,6 +113,7 @@ public:
public Q_SLOTS:
void loadUrl(const QUrl &url);
void loadUrl(const QUrl &url, CompilationMode mode);
void setData(const QByteArray &, const QUrl &baseUrl);
Q_SIGNALS:
......
......@@ -86,6 +86,8 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public
public:
QQmlComponentPrivate() : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), profiler(0) {}
void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
QObject *beginCreate(QQmlContextData *);
void completeCreate();
void initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate);
......
......@@ -1171,7 +1171,7 @@ This enum defines the options that control the way type data is handled.
/*!
Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
*/
QQmlTypeData *QQmlTypeLoader::get(const QUrl &url)
QQmlTypeData *QQmlTypeLoader::get(const QUrl &url, Mode mode)
{
Q_ASSERT(!url.isRelative() &&
(QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
......@@ -1184,7 +1184,7 @@ QQmlTypeData *QQmlTypeLoader::get(const QUrl &url)
if (!typeData) {
typeData = new QQmlTypeData(url, None, this);
m_typeCache.insert(url, typeData);
QQmlDataLoader::load(typeData);
QQmlDataLoader::load(typeData, mode);
}
typeData->addref();
......
......@@ -236,7 +236,7 @@ public:
};
Q_DECLARE_FLAGS(Options, Option)
QQmlTypeData *get(const QUrl &url);
QQmlTypeData *get(const QUrl &url, Mode mode = PreferSynchronous);
QQmlTypeData *get(const QByteArray &, const QUrl &url, Options = None);
void clearCache();
......
......@@ -1120,7 +1120,7 @@ v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args)
}
/*!
\qmlmethod object Qt::createComponent(url)
\qmlmethod object Qt::createComponent(url, mode)
Returns a \l Component object created using the QML file at the specified \a url,
or \c null if an empty string was given.
......@@ -1129,6 +1129,12 @@ The returned component's \l Component::status property indicates whether the
component was successfully created. If the status is \c Component.Error,
see \l Component::errorString() for an error description.
If the optional \a mode parameter is set to \c Component.Asynchronous, the
component will be loaded in a background thread. The Component::status property
will be \c Component.Loading while it is loading. The status will change to
\c Component.Ready if the component loads successfully, or \c Component.Error
if loading fails.
Call \l {Component::createObject()}{Component.createObject()} on the returned
component to create an object instance of the component.
......@@ -1143,8 +1149,9 @@ use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}.
*/
v8::Handle<v8::Value> createComponent(const v8::Arguments &args)
{
if (args.Length() != 1)
V8THROW_ERROR("Qt.createComponent(): Invalid arguments");
const char *invalidArgs = "Qt.createComponent(): Invalid arguments";
if (args.Length() < 1 || args.Length() > 2)
V8THROW_ERROR(invalidArgs);
QV8Engine *v8engine = V8ENGINE();
QQmlEngine *engine = v8engine->engine();
......@@ -1159,8 +1166,20 @@ v8::Handle<v8::Value> createComponent(const v8::Arguments &args)
if (arg.isEmpty())
return v8::Null();
QQmlComponent::CompilationMode compileMode = QQmlComponent::PreferSynchronous;
if (args.Length() == 2) {
if (args[1]->IsInt32()) {
int mode = args[1]->Int32Value();
if (mode != int(QQmlComponent::PreferSynchronous) && mode != int(QQmlComponent::Asynchronous))
V8THROW_ERROR(invalidArgs);
compileMode = QQmlComponent::CompilationMode(mode);
} else {
V8THROW_ERROR(invalidArgs);
}
}
QUrl url = context->resolvedUrl(QUrl(arg));
QQmlComponent *c = new QQmlComponent(engine, url, engine);
QQmlComponent *c = new QQmlComponent(engine, url, compileMode, engine);
QQmlComponentPrivate::get(c)->creationContext = effectiveContext;
QQmlData::get(c, true)->setImplicitDestructible();
return v8engine->newQObject(c);
......
......@@ -344,7 +344,8 @@ void QQuickLoader::loadFromSource()
}
if (isComponentComplete()) {
d->component = new QQmlComponent(qmlEngine(this), d->source, this);
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
d->load();
}
}
......@@ -711,7 +712,8 @@ void QQuickLoader::componentComplete()
QQuickItem::componentComplete();
if (active()) {
if (d->loadingFromSource) {
d->component = new QQmlComponent(qmlEngine(this), d->source, this);
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
}
d->load();
}
......@@ -752,6 +754,9 @@ qreal QQuickLoader::progress() const
This property holds whether the component will be instantiated asynchronously.
When used in conjunction with the \l source property, loading and compilation
will also be performed in a background thread.
Loading asynchronously creates the objects declared by the component
across multiple frames, and reduces the
likelihood of glitches in animation. When loading asynchronously the status
......
This diff is collapsed.
import QtQuick 2.0
Item {
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}
Item {}