summaryrefslogtreecommitdiffstats
path: root/src/api
diff options
context:
space:
mode:
authorMiikka Heikkinen <miikka.heikkinen@qt.io>2019-08-07 16:52:00 +0300
committerMiikka Heikkinen <miikka.heikkinen@qt.io>2019-08-13 11:52:25 +0300
commit7ea21a4315b1c6df104d80a700680dd1f9d204dc (patch)
tree8b5dca8ca23a2acdab6f8ad1f911faad1fdb1ddc /src/api
parentd2a1092b93e9669288dc1d5825bfec849bec9f95 (diff)
Make runtime initialization optionally not block QML
Runtime initialization made non-blocking by offloading it to a worker thread. This causes various object thread affinities in runtime to be incorrect. This is relevant at least for the singaling, where having affinity to non-existing initialization thread would cause signals to not be delivered. To work around this issue, signal proxy thread affinitions and signal connections are set after initialization has completed. Similarly, behavior script QML engine initialization is deferred so that it can be done in correct thread. Task-number: QT3DS-3805 Change-Id: Ie8b64f4ecd93e4c422e369e625080652d67bde27 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src/api')
-rw-r--r--src/api/studio3d/q3dssurfaceviewer.cpp4
-rw-r--r--src/api/studio3dqml/q3dsrenderer.cpp112
-rw-r--r--src/api/studio3dqml/q3dsrenderer_p.h11
-rw-r--r--src/api/studio3dqml/q3dsruntimeInitializerthread.cpp73
-rw-r--r--src/api/studio3dqml/q3dsruntimeInitializerthread_p.h94
-rw-r--r--src/api/studio3dqml/q3dsstudio3d.cpp33
-rw-r--r--src/api/studio3dqml/q3dsstudio3d_p.h5
-rw-r--r--src/api/studio3dqml/studio3dqml.pro6
8 files changed, 301 insertions, 37 deletions
diff --git a/src/api/studio3d/q3dssurfaceviewer.cpp b/src/api/studio3d/q3dssurfaceviewer.cpp
index 899d771..d8a9756 100644
--- a/src/api/studio3d/q3dssurfaceviewer.cpp
+++ b/src/api/studio3d/q3dssurfaceviewer.cpp
@@ -650,9 +650,9 @@ bool Q3DSSurfaceViewerPrivate::initializeRuntime()
if (!m_viewerApp->InitializeApp(int(m_size.width() * m_pixelRatio),
int(m_size.height() * m_pixelRatio),
- m_context->format(), m_fboId, localSource,
+ m_context->format(), int(m_fboId), localSource,
m_presentation->variantList(),
- m_presentation->delayedLoading(),
+ m_presentation->delayedLoading(), true,
m_presentation->d_ptr->streamProxy())) {
setError(m_viewerApp->error());
releaseRuntime();
diff --git a/src/api/studio3dqml/q3dsrenderer.cpp b/src/api/studio3dqml/q3dsrenderer.cpp
index 12f2e4d..172ec69 100644
--- a/src/api/studio3dqml/q3dsrenderer.cpp
+++ b/src/api/studio3dqml/q3dsrenderer.cpp
@@ -32,6 +32,7 @@
#include "Qt3DSViewerApp.h"
#include "Qt3DSAudioPlayerImpl.h"
#include "q3dspresentationitem_p.h"
+#include "q3dsruntimeInitializerthread_p.h"
#include <QtStudio3D/private/q3dscommandqueue_p.h>
#include <QtStudio3D/private/q3dsviewersettings_p.h>
@@ -41,6 +42,7 @@
#include <QtCore/qdebug.h>
#include <QtCore/qrunnable.h>
+#include <QtCore/qthread.h>
#include <QtGui/qwindow.h>
#include <QtGui/qopenglcontext.h>
#include <QtQuick/qquickwindow.h>
@@ -50,10 +52,10 @@ using namespace Q3DSViewer;
QT_BEGIN_NAMESPACE
Q3DSRenderer::Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor,
- QElapsedTimer *startupTimer)
+ QElapsedTimer *startupTimer, bool asyncInit)
: m_visibleFlag(visibleFlag)
, m_initElements(false)
- , m_runtime(0)
+ , m_runtime(nullptr)
, m_window(nullptr)
, m_initialized(false)
, m_initializationFailure(false)
@@ -61,6 +63,7 @@ Q3DSRenderer::Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisi
, m_settings(new Q3DSViewerSettings(this))
, m_presentation(new Q3DSPresentation(this))
, m_startupTimer(startupTimer)
+ , m_asyncInit(asyncInit)
{
}
@@ -77,7 +80,7 @@ QOpenGLFramebufferObject *Q3DSRenderer::createFramebufferObject(const QSize &siz
QOpenGLFramebufferObject *frameBuffer =
new QOpenGLFramebufferObject(size, theFormat);
if (m_runtime && m_runtime->IsInitialised())
- m_runtime->setOffscreenId(frameBuffer->handle());
+ m_runtime->setOffscreenId(int(frameBuffer->handle()));
return frameBuffer;
}
@@ -123,11 +126,27 @@ void Q3DSRenderer::releaseRuntime()
m_presentation->d_ptr->setViewerApp(nullptr);
if (m_runtime) {
+ if (m_asyncInit && m_runtimeInitializerThread) {
+ m_runtimeInitializerThread->quit();
+ m_runtimeInitializerThread->wait();
+ delete m_runtimeInitializerThread;
+ m_runtimeInitializerThread = nullptr;
+ }
m_runtime->Release();
m_runtime = nullptr;
}
}
+void Q3DSRenderer::registerCallbacks()
+{
+ m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_INIT,
+ reinterpret_cast<qml_Function>(Q3DSRenderer::onInitHandler),
+ this);
+ m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_UPDATE,
+ reinterpret_cast<qml_Function>(Q3DSRenderer::onUpdateHandler),
+ this);
+}
+
class RuntimeInitializer : public QRunnable
{
public:
@@ -149,7 +168,6 @@ private:
Q3DSRenderer *m_renderer;
};
-
/** Invoked by the QML scene graph indicating that it's time to render.
* Calls `draw()` if the plugin is visible, or `processCommands()` otherwise.
*
@@ -163,9 +181,14 @@ void Q3DSRenderer::render()
// We may start in a non visible state but we still need
// to init the runtime otherwise the commands are never processed
if (!m_initialized && !m_initializationFailure) {
- auto *ri = new RuntimeInitializer(this);
- // Initialize runtime after the first frame has been drawn
- m_window->scheduleRenderJob(ri, QQuickWindow::AfterSwapStage);
+ if (m_asyncInit) {
+ if (!m_runtimeInitializerThread)
+ initializeRuntime(framebufferObject());
+ } else {
+ auto *ri = new RuntimeInitializer(this);
+ // Initialize runtime after the first frame has been drawn
+ m_window->scheduleRenderJob(ri, QQuickWindow::AfterSwapStage);
+ }
}
// Don't render if the plugin is hidden; however, if hidden, but sure
@@ -210,28 +233,37 @@ bool Q3DSRenderer::initializeRuntime(QOpenGLFramebufferObject *inFbo)
const QString localSource = Q3DSUtils::urlToLocalFileOrQrc(m_presentation->source());
- if (!m_runtime->InitializeApp(theWidth, theHeight,
- QOpenGLContext::currentContext()->format(),
- inFbo->handle(), localSource,
- m_presentation->variantList(),
- m_presentation->delayedLoading(),
- m_visitor)) {
- m_error = m_runtime->error();
- releaseRuntime();
- return false;
+ if (m_asyncInit) {
+ auto currentContext = QOpenGLContext::currentContext();
+ auto context = new QOpenGLContext();
+ context->setFormat(currentContext->format());
+ context->setShareContext(currentContext);
+ context->create();
+ m_runtimeInitializerThread = new Q3DSRuntimeInitializerThread(
+ m_runtime, theWidth, theHeight, QOpenGLContext::currentContext()->format(),
+ int(inFbo->handle()), localSource, m_presentation->variantList(),
+ m_presentation->delayedLoading(), m_visitor, context,
+ currentContext->surface());
+ connect(m_runtimeInitializerThread, &Q3DSRuntimeInitializerThread::initDone,
+ this, &Q3DSRenderer::handleRuntimeInitializedAsync, Qt::QueuedConnection);
+ context->moveToThread(m_runtimeInitializerThread);
+ m_runtimeInitializerThread->start();
+ } else {
+ if (!m_runtime->InitializeApp(theWidth, theHeight,
+ QOpenGLContext::currentContext()->format(),
+ int(inFbo->handle()), localSource,
+ m_presentation->variantList(),
+ m_presentation->delayedLoading(), true,
+ m_visitor)) {
+ m_error = m_runtime->error();
+ releaseRuntime();
+ return false;
+ }
+ m_settings->d_ptr->setViewerApp(m_runtime);
+ m_presentation->d_ptr->setViewerApp(m_runtime, false);
+ registerCallbacks();
}
- m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_INIT,
- reinterpret_cast<qml_Function>(Q3DSRenderer::onInitHandler),
- this);
- m_runtime->RegisterScriptCallback(Q3DSViewer::ViewerCallbackType::Enum::CALLBACK_ON_UPDATE,
- reinterpret_cast<qml_Function>(Q3DSRenderer::onUpdateHandler),
- this);
-
- m_settings->d_ptr->setViewerApp(m_runtime);
- m_presentation->d_ptr->setViewerApp(m_runtime, false);
-
- // Connect signals
connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideEntered,
this, &Q3DSRenderer::enterSlide);
connect(m_runtime, &Q3DSViewer::Q3DSViewerApp::SigSlideExited,
@@ -332,7 +364,7 @@ void Q3DSRenderer::processCommands()
m_presentation->goToTime(cmd.m_elementPath, cmd.m_floatValue);
break;
case CommandType_GoToSlide:
- m_presentation->goToSlide(cmd.m_elementPath, cmd.m_intValues[0]);
+ m_presentation->goToSlide(cmd.m_elementPath, quint32(cmd.m_intValues[0]));
break;
case CommandType_GoToSlideByName:
m_presentation->goToSlide(cmd.m_elementPath, cmd.m_stringValue);
@@ -496,4 +528,28 @@ void Q3DSRenderer::processCommands()
m_commands.clear(false);
}
+void Q3DSRenderer::handleRuntimeInitializedAsync()
+{
+ m_initialized = m_runtimeInitializerThread->wasSuccess();
+ m_initializationFailure = !m_initialized;
+
+ if (m_initializationFailure) {
+ m_commands.clear(true);
+ m_error = m_runtime->error();
+ releaseRuntime();
+ } else {
+ m_runtime->finishAsyncInit();
+ registerCallbacks();
+ m_settings->d_ptr->setViewerApp(m_runtime);
+ m_presentation->d_ptr->setViewerApp(m_runtime, false);
+ m_window->setClearBeforeRendering(false);
+
+ connect(m_runtimeInitializerThread, &QThread::finished, [this]() {
+ m_runtimeInitializerThread->deleteLater();
+ m_runtimeInitializerThread = nullptr;
+ });
+ m_runtimeInitializerThread->quit();
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/api/studio3dqml/q3dsrenderer_p.h b/src/api/studio3dqml/q3dsrenderer_p.h
index 61e8770..f0b6cfe 100644
--- a/src/api/studio3dqml/q3dsrenderer_p.h
+++ b/src/api/studio3dqml/q3dsrenderer_p.h
@@ -52,6 +52,7 @@ QT_BEGIN_NAMESPACE
class Q3DSViewerSettings;
class Q3DSPresentation;
+class Q3DSRuntimeInitializerThread;
class Q3DSRenderer : public QObject,
public QQuickFramebufferObject::Renderer
@@ -60,8 +61,8 @@ class Q3DSRenderer : public QObject,
public:
Q3DSRenderer(bool visibleFlag, qt3ds::Qt3DSAssetVisitor *assetVisitor,
- QElapsedTimer *startupTimer);
- ~Q3DSRenderer();
+ QElapsedTimer *startupTimer, bool asyncInit);
+ ~Q3DSRenderer() override;
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override;
@@ -80,6 +81,9 @@ Q_SIGNALS:
void meshesCreated(const QStringList &meshNames, const QString &error);
void dataOutputValueUpdated(const QString &name, const QVariant &newValue);
+protected Q_SLOTS:
+ void handleRuntimeInitializedAsync();
+
protected:
static void onInitHandler(void *userData);
static void onUpdateHandler(void *userData);
@@ -88,6 +92,7 @@ protected:
void render() override;
void synchronize(QQuickFramebufferObject *inView) override;
void releaseRuntime();
+ void registerCallbacks();
protected:
bool m_visibleFlag; // Is the plugin visible? Prevents rendering hidden content.
@@ -104,6 +109,8 @@ protected:
Q3DSPresentation *m_presentation;
QString m_error;
QElapsedTimer *m_startupTimer;
+ Q3DSRuntimeInitializerThread *m_runtimeInitializerThread = nullptr;
+ bool m_asyncInit = false;
friend class RuntimeInitializer;
};
diff --git a/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp b/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp
new file mode 100644
index 0000000..677b61a
--- /dev/null
+++ b/src/api/studio3dqml/q3dsruntimeInitializerthread.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "q3dsruntimeInitializerthread_p.h"
+#include "Qt3DSViewerApp.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+Q3DSRuntimeInitializerThread::Q3DSRuntimeInitializerThread(
+ Q3DSViewer::Q3DSViewerApp *runtime,
+ int width, int height, const QSurfaceFormat &format, int offscreenID, const QString &source,
+ const QStringList &variantList, bool delayedLoading, qt3ds::Qt3DSAssetVisitor *assetVisitor,
+ QOpenGLContext *context, QSurface *surface)
+ : m_runtime(runtime)
+ , m_width(width)
+ , m_height(height)
+ , m_format(format)
+ , m_offscreenId(offscreenID)
+ , m_source(source)
+ , m_variantList(variantList)
+ , m_delayedLoading(delayedLoading)
+ , m_assetVisitor(assetVisitor)
+ , m_context(context)
+ , m_surface(surface)
+{
+
+}
+
+void Q3DSRuntimeInitializerThread::run()
+{
+ m_context->makeCurrent(m_surface);
+ m_success = m_runtime->InitializeApp(m_width, m_height, m_format, m_offscreenId,
+ m_source, m_variantList, m_delayedLoading, false,
+ m_assetVisitor);
+ m_context->doneCurrent();
+ delete m_context;
+
+ Q_EMIT initDone();
+
+ // Enter event loop to ensure thread is alive for long enough for initDone to be delivered
+ exec();
+}
+
+QT_END_NAMESPACE
diff --git a/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h b/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h
new file mode 100644
index 0000000..42dcf67
--- /dev/null
+++ b/src/api/studio3dqml/q3dsruntimeInitializerthread_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DS_RUNTIME_INITIALIZER_THEAD_H
+#define Q3DS_RUNTIME_INITIALIZER_THEAD_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the QtStudio3D 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 "Qt3DSViewerApp.h"
+#include "q3dsstudio3d_p.h"
+
+#include <QtCore/qthread.h>
+#include <QtGui/qopenglframebufferobject.h>
+#include <QtGui/qsurfaceformat.h>
+#include <QtGui/qopenglcontext.h>
+#include <QtGui/qsurface.h>
+#include <QtQuick/qquickframebufferobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSRenderer;
+
+class Q3DSRuntimeInitializerThread : public QThread
+{
+ Q_OBJECT
+public:
+ Q3DSRuntimeInitializerThread(Q3DSViewer::Q3DSViewerApp *runtime,
+ int width, int height, const QSurfaceFormat &format,
+ int offscreenID, const QString &source,
+ const QStringList &variantList, bool delayedLoading,
+ qt3ds::Qt3DSAssetVisitor *assetVisitor, QOpenGLContext *context,
+ QSurface *surface);
+
+ void run() override;
+
+ bool wasSuccess() const { return m_success; }
+
+Q_SIGNALS:
+ void initDone();
+
+private:
+ Q3DSViewer::Q3DSViewerApp *m_runtime = nullptr;
+ Q3DSRenderer *m_renderer = nullptr;
+ int m_width = 0;
+ int m_height = 0;
+ QSurfaceFormat m_format;
+ int m_offscreenId = 0;
+ QString m_source;
+ QStringList m_variantList;
+ bool m_delayedLoading = false;
+ qt3ds::Qt3DSAssetVisitor *m_assetVisitor;
+ QOpenGLContext *m_context;
+ QSurface *m_surface;
+ bool m_success = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DS_RUNTIME_INITIALIZER_THEAD_H
diff --git a/src/api/studio3dqml/q3dsstudio3d.cpp b/src/api/studio3dqml/q3dsstudio3d.cpp
index 8183d7b..736846e 100644
--- a/src/api/studio3dqml/q3dsstudio3d.cpp
+++ b/src/api/studio3dqml/q3dsstudio3d.cpp
@@ -211,6 +211,31 @@ void Q3DSStudio3D::setError(const QString &error)
}
/*!
+ \qmlproperty bool Studio3D::asyncInit
+
+ If set to \c{true}, indicates that renderer initialization should be done asynchronously
+ in a helper thread. This improves UI responsiveness while initialization is happening,
+ but can lead to slower initialization. Asynchronous initialization may not work properly
+ on all platforms.
+
+ Defaults to \c{false}.
+
+ Changing this property after renderer is created doesn't do anything.
+*/
+bool Q3DSStudio3D::asyncInit() const
+{
+ return m_asyncInit;
+}
+
+void Q3DSStudio3D::setAsyncInit(bool enabled)
+{
+ if (enabled != m_asyncInit) {
+ m_asyncInit = enabled;
+ Q_EMIT asyncInitChanged(m_asyncInit);
+ }
+}
+
+/*!
\qmlproperty EventIgnoreFlags Studio3D::ignoredEvents
This property can be used to ignore mouse/wheel/keyboard events.
@@ -295,7 +320,10 @@ void Q3DSStudio3D::handleWindowChanged(QQuickWindow *window)
if (!window)
return;
- window->setClearBeforeRendering(false);
+ // We need to enable clearing until the presentation starts rendering and takes care of that.
+ // Note that having this flag as false assumes presentation is always full screen.
+ window->setClearBeforeRendering(m_asyncInit);
+
m_pixelRatio = window->devicePixelRatio();
// Call tick every frame of the GUI thread to notify QML about new frame via frameUpdate signal
@@ -380,8 +408,7 @@ QQuickFramebufferObject::Renderer *Q3DSStudio3D::createRenderer() const
// and the plugin, and vice-versa. The only valid time the two
// may communicate is during Q3DSRenderer::synchronize().
Q3DSRenderer *renderer = new Q3DSRenderer(isVisible(), m_presentation->d_ptr->streamProxy(),
- m_startupTimer.get());
-
+ m_startupTimer.get(), m_asyncInit);
connect(renderer, &Q3DSRenderer::enterSlide,
m_presentation->d_ptr, &Q3DSPresentationPrivate::handleSlideEntered);
connect(renderer, &Q3DSRenderer::dataOutputValueUpdated,
diff --git a/src/api/studio3dqml/q3dsstudio3d_p.h b/src/api/studio3dqml/q3dsstudio3d_p.h
index 0fa91d6..45d0695 100644
--- a/src/api/studio3dqml/q3dsstudio3d_p.h
+++ b/src/api/studio3dqml/q3dsstudio3d_p.h
@@ -61,6 +61,7 @@ class Q3DSStudio3D : public QQuickFramebufferObject
Q_PROPERTY(Q3DSViewerSettings *viewerSettings READ viewerSettings CONSTANT)
Q_PROPERTY(QString error READ error NOTIFY errorChanged)
Q_PROPERTY(EventIgnoreFlags ignoredEvents READ ignoredEvents WRITE setIgnoredEvents NOTIFY ignoredEventsChanged)
+ Q_PROPERTY(bool asyncInit READ asyncInit WRITE setAsyncInit NOTIFY asyncInitChanged)
public:
enum EventIgnoreFlag {
@@ -83,6 +84,8 @@ public:
Q3DSViewerSettings *viewerSettings() const;
QString error() const;
void setError(const QString &error);
+ bool asyncInit() const;
+ void setAsyncInit(bool enabled);
void getCommands(bool emitInitialize, CommandQueue &renderQueue);
@@ -105,6 +108,7 @@ Q_SIGNALS:
void ignoredEventsChanged();
void presentationReady();
void presentationLoaded();
+ void asyncInitChanged(bool enabled);
public Q_SLOTS:
void reset();
@@ -130,6 +134,7 @@ protected:
qreal m_pixelRatio;
QString m_error;
QScopedPointer<QElapsedTimer> m_startupTimer;
+ bool m_asyncInit = false;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Q3DSStudio3D::EventIgnoreFlags)
diff --git a/src/api/studio3dqml/studio3dqml.pro b/src/api/studio3dqml/studio3dqml.pro
index b62a342..df685fc 100644
--- a/src/api/studio3dqml/studio3dqml.pro
+++ b/src/api/studio3dqml/studio3dqml.pro
@@ -16,13 +16,15 @@ SOURCES += \
q3dsplugin.cpp \
q3dsstudio3d.cpp \
q3dsrenderer.cpp \
- q3dspresentationitem.cpp
+ q3dspresentationitem.cpp \
+ q3dsruntimeinitializerthread.cpp
HEADERS += \
q3dsplugin.h \
q3dsrenderer_p.h \
q3dsstudio3d_p.h \
- q3dspresentationitem_p.h
+ q3dspresentationitem_p.h \
+ q3dsruntimeinitializerthread_p.h
LIBS += \
-lqt3dsopengl$$qtPlatformTargetSuffix() \