diff options
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 |