summaryrefslogtreecommitdiffstats
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
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>
-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
-rw-r--r--src/engine/Qt3DSRuntimeView.cpp29
-rw-r--r--src/engine/Qt3DSRuntimeView.h5
-rw-r--r--src/runtime/Qt3DSApplication.cpp20
-rw-r--r--src/runtime/Qt3DSApplication.h3
-rw-r--r--src/runtime/Qt3DSIScriptBridge.h2
-rw-r--r--src/runtime/Qt3DSPresentation.cpp8
-rw-r--r--src/runtime/Qt3DSPresentation.h2
-rw-r--r--src/runtime/Qt3DSQmlEngine.cpp57
-rw-r--r--src/runtime/Qt3DSQmlEngine.h2
-rw-r--r--src/uipparser/Qt3DSUIPParser.h3
-rw-r--r--src/uipparser/Qt3DSUIPParserImpl.cpp17
-rw-r--r--src/uipparser/Qt3DSUIPParserImpl.h9
-rw-r--r--src/viewer/Qt3DSViewerApp.cpp65
-rw-r--r--src/viewer/Qt3DSViewerApp.h4
-rw-r--r--tools/viewer/viewer.pro2
23 files changed, 455 insertions, 111 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() \
diff --git a/src/engine/Qt3DSRuntimeView.cpp b/src/engine/Qt3DSRuntimeView.cpp
index 90a2562..f9a4768 100644
--- a/src/engine/Qt3DSRuntimeView.cpp
+++ b/src/engine/Qt3DSRuntimeView.cpp
@@ -170,7 +170,10 @@ public:
bool BeginLoad(const QString &sourcePath, const QStringList &variantList) override;
bool HasOfflineLoadingCompleted() override;
- bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading) override;
+ bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
+ bool initInRenderThread) override;
+ void connectSignals() override;
+ void finishAsyncInit() override;
void Cleanup() override;
@@ -251,6 +254,8 @@ CRuntimeView::CRuntimeView(ITimeProvider &inTimeProvider, IWindowSystem &inWindo
, m_startupTimer(startupTimer)
, m_startupTime(-1)
{
+ // Signal proxy thread affinity is set later when signals are connected to ensure it is correct
+ signalProxy()->moveToThread(nullptr);
}
CRuntimeView::~CRuntimeView()
@@ -292,7 +297,8 @@ bool CRuntimeView::HasOfflineLoadingCompleted()
return true;
}
-bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading)
+bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
+ bool initInRenderThread)
{
m_ApplicationCore->EndLoad();
// Next call will initialize the render portion of the scenes. This *must* have a loaded
@@ -300,7 +306,7 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed
m_RuntimeFactory = m_RuntimeFactoryCore->CreateRenderFactory(format, delayedLoading);
m_Application
= m_ApplicationCore->CreateApplication(*m_InputEngine, m_AudioPlayer,
- *m_RuntimeFactory);
+ *m_RuntimeFactory, initInRenderThread);
if (!m_Application->createSuccessful())
return false;
@@ -308,6 +314,15 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed
m_RenderEngine = &m_RuntimeFactory->CreateRenderEngine();
m_Presentation = m_Application->GetPrimaryPresentation();
+ m_TimeProvider.Reset();
+ return true;
+}
+
+void CRuntimeView::connectSignals()
+{
+ m_Presentation->signalProxy()->moveToThread(QThread::currentThread());
+ signalProxy()->moveToThread(QThread::currentThread());
+
QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigSlideEntered,
signalProxy(), &QRuntimeViewSignalProxy::SigSlideEntered);
QObject::connect(m_Presentation->signalProxy(), &QPresentationSignalProxy::SigSlideExited,
@@ -325,9 +340,13 @@ bool CRuntimeView::InitializeGraphics(const QSurfaceFormat &format, bool delayed
&QPresentationSignalProxy::SigDataOutputValueUpdated,
signalProxy(),
&QRuntimeViewSignalProxy::SigDataOutputValueUpdated);
+}
- m_TimeProvider.Reset();
- return true;
+void CRuntimeView::finishAsyncInit()
+{
+ Q3DStudio::CQmlEngine &bridgeEngine
+ = static_cast<Q3DStudio::CQmlEngine &>(m_RuntimeFactoryCore->GetScriptEngineQml());
+ bridgeEngine.loadDeferredScripts();
}
void CRuntimeView::Cleanup()
diff --git a/src/engine/Qt3DSRuntimeView.h b/src/engine/Qt3DSRuntimeView.h
index 7d86022..bfcb31b 100644
--- a/src/engine/Qt3DSRuntimeView.h
+++ b/src/engine/Qt3DSRuntimeView.h
@@ -161,7 +161,10 @@ public:
public: // loading
virtual bool BeginLoad(const QString &sourcePath, const QStringList &variantList) = 0;
virtual bool HasOfflineLoadingCompleted() = 0;
- virtual bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading) = 0;
+ virtual bool InitializeGraphics(const QSurfaceFormat &format, bool delayedLoading,
+ bool initInRenderThread) = 0;
+ virtual void connectSignals() = 0;
+ virtual void finishAsyncInit() = 0;
virtual void Cleanup() = 0;
diff --git a/src/runtime/Qt3DSApplication.cpp b/src/runtime/Qt3DSApplication.cpp
index c885371..60e5b56 100644
--- a/src/runtime/Qt3DSApplication.cpp
+++ b/src/runtime/Qt3DSApplication.cpp
@@ -405,7 +405,7 @@ class IAppLoadContext : public NVRefCounted
{
public:
virtual void EndLoad() = 0;
- virtual bool OnGraphicsInitialized(IRuntimeFactory &inFactory) = 0;
+ virtual bool OnGraphicsInitialized(IRuntimeFactory &inFactory, bool initInRenderThread) = 0;
virtual bool HasCompletedLoading() = 0;
static IAppLoadContext &CreateXMLLoadContext(
SApp &inApp, const char8_t *inScaleMode);
@@ -1218,7 +1218,8 @@ struct SApp : public IApplication
}
bool LoadUIP(SPresentationAsset &inAsset,
- NVConstDataRef<SElementAttributeReference> inExternalReferences)
+ NVConstDataRef<SElementAttributeReference> inExternalReferences,
+ bool initInRenderThread)
{
GetMetaData();
eastl::string theFile;
@@ -1243,7 +1244,7 @@ struct SApp : public IApplication
m_CoreFactory->GetStringTable()));
Q3DStudio::IScene *newScene = NULL;
m_PresentationBuffer.clear();
- if (theUIPParser->Load(*thePresentation, inExternalReferences)) {
+ if (theUIPParser->Load(*thePresentation, inExternalReferences, initInRenderThread)) {
// Load the scene graph portion of the scene.
newScene = m_RuntimeFactory->GetSceneManager().LoadScene(
thePresentation, theUIPParser.mPtr,
@@ -1691,7 +1692,8 @@ struct SApp : public IApplication
// of resources that need to be uploaded to opengl. Maintains reference to runtime factory
IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
Q3DStudio::IAudioPlayer *inAudioPlayer,
- Q3DStudio::IRuntimeFactory &inFactory) override
+ Q3DStudio::IRuntimeFactory &inFactory,
+ bool initInRenderThread) override
{
{
SStackPerfTimer __loadTimer(m_CoreFactory->GetPerfTimer(),
@@ -1708,7 +1710,8 @@ struct SApp : public IApplication
SStackPerfTimer __timer(m_CoreFactory->GetPerfTimer(),
"Application: Load Context Graphics Initialized");
if (m_AppLoadContext)
- m_createSuccessful = m_AppLoadContext->OnGraphicsInitialized(inFactory);
+ m_createSuccessful = m_AppLoadContext->OnGraphicsInitialized(
+ inFactory, initInRenderThread);
// Guarantees the end of the multithreaded access to the various components
m_AppLoadContext = NULL;
if (!m_createSuccessful)
@@ -1957,7 +1960,7 @@ struct SXMLLoader : public IAppLoadContext
bool HasCompletedLoading() override { return true; }
- bool OnGraphicsInitialized(IRuntimeFactory &inFactory) override
+ bool OnGraphicsInitialized(IRuntimeFactory &inFactory, bool initInRenderThread) override
{
eastl::vector<SElementAttributeReference> theUIPReferences;
eastl::string tempString;
@@ -1978,7 +1981,8 @@ struct SXMLLoader : public IAppLoadContext
if (!m_App.LoadUIP(thePresentationAsset,
toConstDataRef(theUIPReferences.data(),
- (QT3DSU32)theUIPReferences.size()))) {
+ (QT3DSU32)theUIPReferences.size()),
+ initInRenderThread)) {
qCCritical(INVALID_OPERATION, "Unable to load presentation %s",
thePathStr.c_str());
}
@@ -2061,7 +2065,7 @@ CAppStr &CAppStr::operator=(const CAppStr &inOther)
}
IApplication &IApplication::CreateApplicationCore(Q3DStudio::IRuntimeFactoryCore &inFactory,
- const char8_t *inApplicationDirectory)
+ const char8_t *inApplicationDirectory)
{
return *QT3DS_NEW(inFactory.GetFoundation().getAllocator(), SApp)(inFactory,
inApplicationDirectory);
diff --git a/src/runtime/Qt3DSApplication.h b/src/runtime/Qt3DSApplication.h
index 510845e..7624a39 100644
--- a/src/runtime/Qt3DSApplication.h
+++ b/src/runtime/Qt3DSApplication.h
@@ -249,7 +249,8 @@ public:
// of resources that need to be uploaded to opengl. Maintains reference to runtime factory
virtual IApplication &CreateApplication(Q3DStudio::CInputEngine &inInputEngine,
Q3DStudio::IAudioPlayer *inAudioPlayer,
- Q3DStudio::IRuntimeFactory &inFactory) = 0;
+ Q3DStudio::IRuntimeFactory &inFactory,
+ bool initInRenderThread) = 0;
// maintains reference to runtime factory core. AppDir is where the executable is located;
// the system will expect res directory
diff --git a/src/runtime/Qt3DSIScriptBridge.h b/src/runtime/Qt3DSIScriptBridge.h
index 889968d..16bf299 100644
--- a/src/runtime/Qt3DSIScriptBridge.h
+++ b/src/runtime/Qt3DSIScriptBridge.h
@@ -139,7 +139,7 @@ public: // Scripts
// LoadScript goes further by registering scriptIndex->inPresentation, and inOwner->m_ScriptID=
// scriptIndex
virtual void LoadScript(IPresentation *inPresentation, TElement *inOwner,
- const CHAR *inName) = 0;
+ const CHAR *inName, bool initInRenderThread) = 0;
virtual Q3DStudio::INT32 InitializeApplicationBehavior(const char *inProjectRelativePath) = 0;
public: // Script functions and Callbacks
diff --git a/src/runtime/Qt3DSPresentation.cpp b/src/runtime/Qt3DSPresentation.cpp
index 0792119..504e7b6 100644
--- a/src/runtime/Qt3DSPresentation.cpp
+++ b/src/runtime/Qt3DSPresentation.cpp
@@ -94,6 +94,9 @@ CPresentation::CPresentation(const QString &inName, const QString &projectPath,
ILogicSystem::CreateLogicSystem(inApplication->GetRuntimeFactoryCore().GetFoundation());
m_ParametersSystem = IParametersSystem::CreateParametersSystem(
inApplication->GetRuntimeFactoryCore().GetFoundation());
+
+ // Signal proxy thread affinity is set later when signals are connected to ensure it is correct
+ m_SignalProxy.moveToThread(nullptr);
}
#ifdef _WIN32
#pragma warning(pop)
@@ -833,9 +836,4 @@ void CPresentation::SetSize(const SPresentationSize &inSize)
m_Size = inSize;
}
-QPresentationSignalProxy *CPresentation::signalProxy()
-{
- return &m_SignalProxy;
-}
-
} // namespace Q3DStudio
diff --git a/src/runtime/Qt3DSPresentation.h b/src/runtime/Qt3DSPresentation.h
index 76b0a6e..5917d7b 100644
--- a/src/runtime/Qt3DSPresentation.h
+++ b/src/runtime/Qt3DSPresentation.h
@@ -161,7 +161,7 @@ public: // Commands and Events
void FlushEventCommandQueue(void) override;
void ProcessEvent(SEventCommand &inEvent) override;
- QPresentationSignalProxy *signalProxy();
+ QPresentationSignalProxy *signalProxy() { return &m_SignalProxy; }
public: // Data Output
void AddToDataOutputMap(const QHash<qt3ds::foundation::CRegisteredString,
diff --git a/src/runtime/Qt3DSQmlEngine.cpp b/src/runtime/Qt3DSQmlEngine.cpp
index b9b2067..a945f14 100644
--- a/src/runtime/Qt3DSQmlEngine.cpp
+++ b/src/runtime/Qt3DSQmlEngine.cpp
@@ -385,12 +385,13 @@ private:
CScriptCallbacks m_ScriptCallbacks;
- QQmlEngine m_engine;
+ QScopedPointer<QQmlEngine> m_engine;
QMap<QString, QQmlComponent *> m_components;
QVector<Q3DSQmlScript *> m_scripts;
QSet<int> m_availableIds;
QHash<TElement *, int> m_elementIdMap;
+ QVector<QPair<TElement *, QString>> m_deferredScriptLoads;
public:
CQmlEngineImpl(NVFoundationBase &fnd, ITimeProvider &inTimeProvider);
@@ -406,7 +407,10 @@ public:
qt3ds::runtime::IApplication *GetApplication() override;
void Initialize() override;
- void LoadScript(IPresentation *presentation, TElement *element, const CHAR *path) override;
+ void loadScript(TElement *element, const QString &sourcePath);
+ void LoadScript(IPresentation *presentation, TElement *element, const CHAR *path,
+ bool initInRenderThread) override;
+ void loadDeferredScripts() override;
Q3DStudio::INT32 InitializeApplicationBehavior(const char *) override
{
return -1;
@@ -667,20 +671,15 @@ bool CQmlEngineImpl::GetAttribute(const char *element, const char *attName, char
return false;
}
-void CQmlEngineImpl::LoadScript(IPresentation *presentation, TElement *element, const CHAR *path)
+void CQmlEngineImpl::loadScript(TElement *element, const QString &sourcePath)
{
- QString presPath = QFileInfo(presentation->GetFilePath()).absolutePath();
-
- QString sourcePath(presPath + QLatin1Char('/') + path);
- sourcePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
-
- TElement *parent = element->GetParent();
- if (!parent)
- return;
+ if (m_engine.isNull())
+ m_engine.reset(new QQmlEngine);
if (!m_components.contains(sourcePath)) {
- m_components[sourcePath] = new QQmlComponent(&m_engine, QUrl::fromLocalFile(sourcePath),
- &m_engine);
+ m_components[sourcePath] = new QQmlComponent(m_engine.data(),
+ QUrl::fromLocalFile(sourcePath),
+ m_engine.data());
}
QQmlComponent *component = m_components[sourcePath];
@@ -705,6 +704,32 @@ void CQmlEngineImpl::LoadScript(IPresentation *presentation, TElement *element,
}
}
+void CQmlEngineImpl::LoadScript(IPresentation *presentation, TElement *element, const CHAR *path,
+ bool initInRenderThread)
+{
+ QString presPath = QFileInfo(presentation->GetFilePath()).absolutePath();
+
+ QString sourcePath(presPath + QLatin1Char('/') + path);
+ sourcePath.replace(QLatin1Char('\\'), QLatin1Char('/'));
+
+ TElement *parent = element->GetParent();
+ if (!parent)
+ return;
+
+ // Defer engine initialization until we are in render thread
+ if (initInRenderThread)
+ loadScript(element, sourcePath);
+ else
+ m_deferredScriptLoads.append({element, sourcePath});
+}
+
+void CQmlEngineImpl::loadDeferredScripts()
+{
+ for (const auto &data : qAsConst(m_deferredScriptLoads))
+ loadScript(data.first, data.second);
+ m_deferredScriptLoads.clear();
+}
+
void CQmlEngineImpl::FireEvent(const char *element, const char *evtName)
{
TElement *theElement = getTarget(element);
@@ -2001,7 +2026,7 @@ void CQmlEngineImpl::createComponent(QQmlComponent *component, TElement *element
if (!parent)
return;
- QQmlContext *context = new QQmlContext(&m_engine, &m_engine);
+ QQmlContext *context = new QQmlContext(m_engine.data(), m_engine.data());
QObject *obj = component->beginCreate(context);
if (!obj) {
context->deleteLater();
@@ -2439,7 +2464,7 @@ bool CQmlEngineImpl::getAttributeVector2(QVector<QByteArray> &outAttVec,
QJSValue CQmlEngineImpl::buildJSFunc(const QString &userFunc)
{
- auto res = this->m_engine.evaluate(userFunc);
+ auto res = this->m_engine->evaluate(userFunc);
if (res.isError()) {
qWarning() << __FUNCTION__
<< "Uncaught exception during datainput evaluation. Evaluator function" << userFunc;
@@ -2457,7 +2482,7 @@ QVariant CQmlEngineImpl::callJSFunc(const QString &controllerName,
// get the most recent set values for datainput sources (arguments) in the expression
for (auto diVal : sourceDIs)
- args << this->m_engine.toScriptValue(diMap[diVal].value);
+ args << this->m_engine->toScriptValue(diMap[diVal].value);
if (diDef.evalFunc.isCallable()) {
QJSValue res = diDef.evalFunc.call(args);
diff --git a/src/runtime/Qt3DSQmlEngine.h b/src/runtime/Qt3DSQmlEngine.h
index a5ad382..c515df1 100644
--- a/src/runtime/Qt3DSQmlEngine.h
+++ b/src/runtime/Qt3DSQmlEngine.h
@@ -209,6 +209,8 @@ public: // Public functions but not functions on the script bridge
virtual void Initialize() = 0;
+ virtual void loadDeferredScripts() = 0;
+
public:
/**
* @brief Create QML engine
diff --git a/src/uipparser/Qt3DSUIPParser.h b/src/uipparser/Qt3DSUIPParser.h
index 6657139..7b4fc3f 100644
--- a/src/uipparser/Qt3DSUIPParser.h
+++ b/src/uipparser/Qt3DSUIPParser.h
@@ -131,7 +131,8 @@ protected:
virtual ~IUIPParser() {}
public: // Parse UIP file
virtual BOOL Load(IPresentation &inPresentation,
- NVConstDataRef<SElementAttributeReference> inStateReferences) = 0;
+ NVConstDataRef<SElementAttributeReference> inStateReferences,
+ bool initInRenderThread) = 0;
virtual qt3dsdm::IDOMReader &GetDOMReader() = 0;
virtual IRuntimeMetaData &GetMetaData() = 0;
// Mapping back from file id to element id, needed to hook elements up to their respective
diff --git a/src/uipparser/Qt3DSUIPParserImpl.cpp b/src/uipparser/Qt3DSUIPParserImpl.cpp
index a2f3c7c..b899248 100644
--- a/src/uipparser/Qt3DSUIPParserImpl.cpp
+++ b/src/uipparser/Qt3DSUIPParserImpl.cpp
@@ -514,7 +514,8 @@ CUIPParserImpl::~CUIPParserImpl()
* @return a flag indicating whether or not we successfully loaded the file
*/
BOOL CUIPParserImpl::Load(IPresentation &inPresentation,
- NVConstDataRef<SElementAttributeReference> inStateReferences)
+ NVConstDataRef<SElementAttributeReference> inStateReferences,
+ bool initInRenderThread)
{
m_CurrentPresentation = &inPresentation;
if (!m_DOMReader) {
@@ -582,7 +583,7 @@ BOOL CUIPParserImpl::Load(IPresentation &inPresentation,
// Now we are ready to load the scene graph
if (theLoadResult)
- theLoadResult &= LoadGraph(inPresentation, *m_DOMReader);
+ theLoadResult &= LoadGraph(inPresentation, *m_DOMReader, initInRenderThread);
if (theLoadResult)
theLoadResult &= LoadLogic(inPresentation, *m_DOMReader);
@@ -673,7 +674,8 @@ BOOL CUIPParserImpl::LoadClasses(IPresentation & /*inPresentation*/, IDOMReader
return true;
}
-BOOL CUIPParserImpl::LoadGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader)
+BOOL CUIPParserImpl::LoadGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader,
+ bool initInRenderThread)
{
IDOMReader::Scope __childScope(inReader);
@@ -683,7 +685,7 @@ BOOL CUIPParserImpl::LoadGraph(IPresentation &inPresentation, qt3dsdm::IDOMReade
bool theLoadResult = inReader.MoveToFirstChild("Graph");
if (theLoadResult) {
- theLoadResult &= LoadSceneGraph(inPresentation, inReader);
+ theLoadResult &= LoadSceneGraph(inPresentation, inReader, nullptr, initInRenderThread);
if (theLoadResult)
PatchSceneElementRef();
}
@@ -1009,7 +1011,8 @@ EElementType GetElementType(const char *inType)
* @return a flag indicating whether or not we successfully loaded the file
*/
BOOL CUIPParserImpl::LoadSceneGraph(IPresentation &inPresentation, IDOMReader &inReader,
- qt3ds::runtime::element::SElement *inNewStyleParent)
+ qt3ds::runtime::element::SElement *inNewStyleParent,
+ bool initInRenderThread)
{
IDOMReader::Scope __childScope(inReader);
IScriptBridge *theScriptBridgeQml = inPresentation.GetScriptBridgeQml();
@@ -1141,10 +1144,10 @@ BOOL CUIPParserImpl::LoadSceneGraph(IPresentation &inPresentation, IDOMReader &i
if (isBehavior) {
if (theFileString.find(".qml") != eastl::string::npos) {
theScriptBridgeQml->LoadScript(&inPresentation, &theNewElem,
- theFileString.c_str());
+ theFileString.c_str(), initInRenderThread);
}
}
- LoadSceneGraph(inPresentation, inReader, &theNewElem);
+ LoadSceneGraph(inPresentation, inReader, &theNewElem, initInRenderThread);
}
return true;
diff --git a/src/uipparser/Qt3DSUIPParserImpl.h b/src/uipparser/Qt3DSUIPParserImpl.h
index bca984b..3c44e21 100644
--- a/src/uipparser/Qt3DSUIPParserImpl.h
+++ b/src/uipparser/Qt3DSUIPParserImpl.h
@@ -527,7 +527,8 @@ public: // Construction
public: // Parse UIP file
BOOL Load(IPresentation &inPresentation,
- NVConstDataRef<SElementAttributeReference> inStateReferences) override;
+ NVConstDataRef<SElementAttributeReference> inStateReferences,
+ bool initInRenderThread) override;
qt3dsdm::IDOMReader &GetDOMReader() override;
IRuntimeMetaData &GetMetaData() override;
SElementAndType GetElementForID(const char *inStringId) override;
@@ -549,9 +550,11 @@ public: // Parse UIP file
protected: // Operation
BOOL LoadProjectSettings(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader);
BOOL LoadClasses(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader);
- BOOL LoadGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader);
+ BOOL LoadGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader,
+ bool initInRenderThread);
BOOL LoadSceneGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader,
- qt3ds::runtime::element::SElement *inNewStyleParent = NULL);
+ qt3ds::runtime::element::SElement *inNewStyleParent,
+ bool initInRenderThread);
BOOL LoadLogic(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader);
BOOL LoadStateGraph(IPresentation &inPresentation, qt3dsdm::IDOMReader &inReader);
diff --git a/src/viewer/Qt3DSViewerApp.cpp b/src/viewer/Qt3DSViewerApp.cpp
index 1402ef0..e7b153c 100644
--- a/src/viewer/Qt3DSViewerApp.cpp
+++ b/src/viewer/Qt3DSViewerApp.cpp
@@ -390,7 +390,7 @@ void Q3DSViewerApp::setOffscreenId(int offscreenID)
bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFormat &format,
int offscreenID, const QString &source,
const QStringList &variantList,
- bool delayedLoading,
+ bool delayedLoading, bool initInRenderThread,
qt3ds::Qt3DSAssetVisitor *assetVisitor)
{
bool hasValidPresentationFile = !source.isEmpty();
@@ -427,7 +427,8 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
return false;
}
- bool success = m_Impl.m_view->InitializeGraphics(format, delayedLoading);
+ bool success = m_Impl.m_view->InitializeGraphics(format, delayedLoading,
+ initInRenderThread);
if (!success) {
m_Impl.m_error = QObject::tr("Viewer launch failure! Failed to load: '%1'").arg(source);
m_Impl.m_error.append("\n");
@@ -435,28 +436,8 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
return false;
}
- // Connect signals
- connect(m_Impl.m_view->signalProxy(),
- &QRuntimeViewSignalProxy::SigSlideEntered, this, &Q3DSViewerApp::SigSlideEntered);
- connect(m_Impl.m_view->signalProxy(),
- &QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited);
- connect(m_Impl.m_view->signalProxy(),
- &QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal);
- connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigDataOutputValueUpdated,
- this, &Q3DSViewerApp::SigDataOutputValueUpdated);
- QMetaObject::Connection *presReadyconn = new QMetaObject::Connection();
- *presReadyconn = connect(m_Impl.m_view->signalProxy(),
- &QRuntimeViewSignalProxy::SigPresentationReady, [&, presReadyconn]{
- // We receive presentation ready signal from runtime when animations and properties
- // have been updated.
- Q_EMIT SigPresentationReady();
- disconnect(*presReadyconn);
- delete presReadyconn;
- });
- connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated, this,
- &Q3DSViewerApp::SigElementsCreated);
- connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated, this,
- &Q3DSViewerApp::SigMaterialsCreated);
+ if (initInRenderThread)
+ this->connectSignals();
Resize(winWidth, winHeight);
@@ -465,6 +446,42 @@ bool Q3DSViewerApp::InitializeApp(int winWidth, int winHeight, const QSurfaceFor
return true;
}
+void Q3DSViewerApp::connectSignals()
+{
+ if (!m_Impl.m_view)
+ return;
+
+ m_Impl.m_view->connectSignals();
+
+ connect(m_Impl.m_view->signalProxy(),
+ &QRuntimeViewSignalProxy::SigSlideEntered, this, &Q3DSViewerApp::SigSlideEntered);
+ connect(m_Impl.m_view->signalProxy(),
+ &QRuntimeViewSignalProxy::SigSlideExited, this, &Q3DSViewerApp::SigSlideExited);
+ connect(m_Impl.m_view->signalProxy(),
+ &QRuntimeViewSignalProxy::SigCustomSignal, this, &Q3DSViewerApp::SigCustomSignal);
+ connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigDataOutputValueUpdated,
+ this, &Q3DSViewerApp::SigDataOutputValueUpdated);
+ QMetaObject::Connection *presReadyconn = new QMetaObject::Connection();
+ *presReadyconn = connect(m_Impl.m_view->signalProxy(),
+ &QRuntimeViewSignalProxy::SigPresentationReady, [&, presReadyconn]{
+ // We receive presentation ready signal from runtime when animations and properties
+ // have been updated.
+ Q_EMIT SigPresentationReady();
+ disconnect(*presReadyconn);
+ delete presReadyconn;
+ });
+ connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigElementsCreated, this,
+ &Q3DSViewerApp::SigElementsCreated);
+ connect(m_Impl.m_view->signalProxy(), &QRuntimeViewSignalProxy::SigMaterialsCreated, this,
+ &Q3DSViewerApp::SigMaterialsCreated);
+}
+
+void Q3DSViewerApp::finishAsyncInit()
+{
+ connectSignals();
+ m_Impl.m_view->finishAsyncInit();
+}
+
bool Q3DSViewerApp::IsInitialised(void)
{
return m_Impl.m_view != nullptr && m_Impl.m_appInitSuccessful;
diff --git a/src/viewer/Qt3DSViewerApp.h b/src/viewer/Qt3DSViewerApp.h
index 3086d55..153f9d7 100644
--- a/src/viewer/Qt3DSViewerApp.h
+++ b/src/viewer/Qt3DSViewerApp.h
@@ -219,9 +219,11 @@ public:
bool InitializeApp(int winWidth, int winHeight, const QSurfaceFormat& format,
int offscreenID, const QString &source,
const QStringList &variantList,
- bool delayedLoading,
+ bool delayedLoading, bool initInRenderThread,
qt3ds::Qt3DSAssetVisitor *assetVisitor = nullptr);
+ void connectSignals();
+ void finishAsyncInit();
bool IsInitialised(void);
void setOffscreenId(int offscreenID);
diff --git a/tools/viewer/viewer.pro b/tools/viewer/viewer.pro
index 927100f..fc750b7 100644
--- a/tools/viewer/viewer.pro
+++ b/tools/viewer/viewer.pro
@@ -21,6 +21,7 @@ SOURCES += \
$$PWD/../../src/api/studio3dqml/q3dsstudio3d.cpp \
$$PWD/../../src/api/studio3dqml/q3dsrenderer.cpp \
$$PWD/../../src/api/studio3dqml/q3dspresentationitem.cpp \
+ $$PWD/../../src/api/studio3dqml/q3dsruntimeinitializerthread.cpp \
main.cpp \
viewer.cpp \
remotedeploymentreceiver.cpp
@@ -29,6 +30,7 @@ HEADERS += \
$$PWD/../../src/api/studio3dqml/q3dsstudio3d_p.h \
$$PWD/../../src/api/studio3dqml/q3dsrenderer_p.h \
$$PWD/../../src/api/studio3dqml/q3dspresentationitem_p.h \
+ $$PWD/../../src/api/studio3dqml/q3dsruntimeinitializerthread_p.h \
viewer.h \
remotedeploymentreceiver.h