diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2019-08-28 08:05:58 +0200 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2019-08-28 08:05:58 +0200 |
commit | a936ddd7acb3fb91d50f1d586fd770a1637f8166 (patch) | |
tree | 3f4bf866e16a1863f8b3d1e2dcb7d39bd7f8aa9b /src/core | |
parent | 3dcc9719808a77ab644921df38ea82c268ea7a97 (diff) | |
parent | f3268fcb2a4e73d99dbd66aa5e262a118b5a5480 (diff) |
Merge remote-tracking branch 'qt-gerrit/wip/refactor' into HEAD
Change-Id: Id73bbecd18edaf46ba481aa018e837a336860223
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/aspects/aspects.pri | 6 | ||||
-rw-r--r-- | src/core/aspects/qabstractaspect.cpp | 102 | ||||
-rw-r--r-- | src/core/aspects/qabstractaspect.h | 13 | ||||
-rw-r--r-- | src/core/aspects/qabstractaspect_p.h | 10 | ||||
-rw-r--r-- | src/core/aspects/qaspectengine.cpp | 85 | ||||
-rw-r--r-- | src/core/aspects/qaspectengine.h | 11 | ||||
-rw-r--r-- | src/core/aspects/qaspectengine_p.h | 10 | ||||
-rw-r--r-- | src/core/aspects/qaspectmanager.cpp | 280 | ||||
-rw-r--r-- | src/core/aspects/qaspectmanager_p.h | 19 | ||||
-rw-r--r-- | src/core/aspects/qaspectthread.cpp | 99 | ||||
-rw-r--r-- | src/core/aspects/qaspectthread_p.h | 88 | ||||
-rw-r--r-- | src/core/jobs/qthreadpooler.cpp | 10 | ||||
-rw-r--r-- | src/core/nodes/qbackendnode.cpp | 16 | ||||
-rw-r--r-- | src/core/nodes/qcomponent.cpp | 4 | ||||
-rw-r--r-- | src/core/nodes/qentity.cpp | 4 | ||||
-rw-r--r-- | src/core/nodes/qnode.cpp | 72 | ||||
-rw-r--r-- | src/core/nodes/qnode_p.h | 2 | ||||
-rw-r--r-- | src/core/qchangearbiter.cpp | 16 | ||||
-rw-r--r-- | src/core/qchangearbiter_p.h | 14 | ||||
-rw-r--r-- | src/core/qscene.cpp | 6 | ||||
-rw-r--r-- | src/core/qscene_p.h | 5 | ||||
-rw-r--r-- | src/core/services/qdownloadhelperservice.cpp | 4 |
22 files changed, 421 insertions, 455 deletions
diff --git a/src/core/aspects/aspects.pri b/src/core/aspects/aspects.pri index ced33f310..5e8327192 100644 --- a/src/core/aspects/aspects.pri +++ b/src/core/aspects/aspects.pri @@ -4,8 +4,7 @@ SOURCES += \ $$PWD/qabstractaspect.cpp \ $$PWD/qaspectengine.cpp \ $$PWD/qaspectfactory.cpp \ - $$PWD/qaspectmanager.cpp \ - $$PWD/qaspectthread.cpp + $$PWD/qaspectmanager.cpp HEADERS += \ $$PWD/qabstractaspect.h \ @@ -13,8 +12,7 @@ HEADERS += \ $$PWD/qabstractaspect_p.h \ $$PWD/qaspectengine_p.h \ $$PWD/qaspectfactory_p.h \ - $$PWD/qaspectmanager_p.h \ - $$PWD/qaspectthread_p.h + $$PWD/qaspectmanager_p.h INCLUDEPATH += $$PWD diff --git a/src/core/aspects/qabstractaspect.cpp b/src/core/aspects/qabstractaspect.cpp index 7b08dec81..7636fd666 100644 --- a/src/core/aspects/qabstractaspect.cpp +++ b/src/core/aspects/qabstractaspect.cpp @@ -40,6 +40,9 @@ #include "qabstractaspect.h" #include "qabstractaspect_p.h" +#include <QMetaObject> +#include <QMetaProperty> + #include <Qt3DCore/qentity.h> #include <Qt3DCore/qpropertyupdatedchange.h> @@ -48,6 +51,7 @@ #include <Qt3DCore/private/qaspectmanager_p.h> #include <Qt3DCore/private/qchangearbiter_p.h> #include <Qt3DCore/private/qnodevisitor_p.h> +#include <Qt3DCore/private/qnode_p.h> #include <Qt3DCore/private/qscene_p.h> QT_BEGIN_NAMESPACE @@ -170,7 +174,14 @@ QNodeId QAbstractAspect::rootEntityId() const Q_DECL_NOEXCEPT void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor) { Q_D(QAbstractAspect); - d->m_backendCreatorFunctors.insert(&obj, functor); + d->m_backendCreatorFunctors.insert(&obj, {functor, QAbstractAspectPrivate::DefaultMapper}); +} + +void QAbstractAspect::registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor, bool supportsSyncing) +{ + Q_D(QAbstractAspect); + const auto f = supportsSyncing ? QAbstractAspectPrivate::SupportsSyncing : QAbstractAspectPrivate::DefaultMapper; + d->m_backendCreatorFunctors.insert(&obj, {functor, f}); } void QAbstractAspect::unregisterBackendType(const QMetaObject &obj) @@ -203,12 +214,96 @@ QVector<QAspectJobPtr> QAbstractAspect::jobsToExecute(qint64 time) return QVector<QAspectJobPtr>(); } +void QAbstractAspect::syncDirtyFrontEndNodes(const QVector<QNode *> &nodes) +{ + Q_D(QAbstractAspect); + d->syncDirtyFrontEndNodes(nodes); +} + +void QAbstractAspectPrivate::syncDirtyFrontEndNodes(const QVector<QNode *> &nodes) +{ + for (auto node: qAsConst(nodes)) { + const QMetaObject *metaObj = node->metaObject(); + QBackendNodeMapperPtr backendNodeMapper; + bool supportsSyncing = false; + while (metaObj != nullptr && backendNodeMapper.isNull()) { + auto v = m_backendCreatorFunctors.value(metaObj); + backendNodeMapper = v.first; + supportsSyncing = v.second & SupportsSyncing; + metaObj = metaObj->superClass(); + } + + if (!backendNodeMapper) + continue; + + QBackendNode *backend = backendNodeMapper->get(node->id()); + if (!backend) + continue; + if (supportsSyncing) + syncDirtyFrontEndNode(node, backend, false); + else + sendPropertyMessages(node, backend); + } +} + +void QAbstractAspectPrivate::syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const +{ + Q_UNUSED(firstTime); + Q_ASSERT(false); // overload in derived class + sendPropertyMessages(node, backend); +} + +void QAbstractAspectPrivate::sendPropertyMessages(QNode *node, QBackendNode *backend) const +{ + const int offset = QNode::staticMetaObject.propertyOffset(); + const auto metaObj = node->metaObject(); + const int count = metaObj->propertyCount(); + + const auto toBackendValue = [](const QVariant &data) -> QVariant + { + if (data.canConvert<QNode*>()) { + QNode *node = data.value<QNode*>(); + + // Ensure the node and all ancestors have issued their node creation changes. + // We can end up here if a newly created node with a parent is immediately set + // as a property on another node. In this case the deferred call to + // _q_postConstructorInit() will not have happened yet as the event + // loop will still be blocked. We need to do this for all ancestors, + // since the subtree of this node otherwise can end up on the backend + // with a reference to a non-existent parent. + if (node) + QNodePrivate::get(node)->_q_ensureBackendNodeCreated(); + + const QNodeId id = node ? node->id() : QNodeId(); + return QVariant::fromValue(id); + } + + return data; + }; + + QPropertyUpdatedChange change(node->id()); + QPropertyUpdatedChangePtr pchange(&change, [](QPropertyUpdatedChange *) { }); + for (int index = offset; index < count; index++) { + const QMetaProperty pro = metaObj->property(index); + change.setPropertyName(pro.name()); + change.setValue(toBackendValue(pro.read(node))); + backend->sceneChangeEvent(pchange); + } + + auto const dynamicProperties = node->dynamicPropertyNames(); + for (const QByteArray &name: dynamicProperties) { + change.setPropertyName(name.data()); + change.setValue(toBackendValue(node->property(name.data()))); + backend->sceneChangeEvent(pchange); + } +} + QBackendNode *QAbstractAspectPrivate::createBackendNode(const QNodeCreatedChangeBasePtr &change) const { const QMetaObject *metaObj = change->metaObject(); QBackendNodeMapperPtr backendNodeMapper; while (metaObj != nullptr && backendNodeMapper.isNull()) { - backendNodeMapper = m_backendCreatorFunctors.value(metaObj); + backendNodeMapper = m_backendCreatorFunctors.value(metaObj).first; metaObj = metaObj->superClass(); } @@ -248,6 +343,7 @@ QBackendNode *QAbstractAspectPrivate::createBackendNode(const QNodeCreatedChange return backend; } + void QAbstractAspectPrivate::clearBackendNode(const QNodeDestroyedChangePtr &change) const { // Each QNodeDestroyedChange may contain info about a whole sub-tree of nodes that @@ -259,7 +355,7 @@ void QAbstractAspectPrivate::clearBackendNode(const QNodeDestroyedChangePtr &cha // Find backend node mapper for this type while (metaObj != nullptr && backendNodeMapper.isNull()) { - backendNodeMapper = m_backendCreatorFunctors.value(metaObj); + backendNodeMapper = m_backendCreatorFunctors.value(metaObj).first; metaObj = metaObj->superClass(); } diff --git a/src/core/aspects/qabstractaspect.h b/src/core/aspects/qabstractaspect.h index 1ae85e4e6..a9f4f03fc 100644 --- a/src/core/aspects/qabstractaspect.h +++ b/src/core/aspects/qabstractaspect.h @@ -77,12 +77,17 @@ protected: template<class Frontend> void registerBackendType(const QBackendNodeMapperPtr &functor); - void registerBackendType(const QMetaObject &, const QBackendNodeMapperPtr &functor); + template<class Frontend, bool supportsSyncing> + void registerBackendType(const QBackendNodeMapperPtr &functor); + void registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor); template<class Frontend> void unregisterBackendType(); void unregisterBackendType(const QMetaObject &); private: + void syncDirtyFrontEndNodes(const QVector<QNode *> &nodes); + void registerBackendType(const QMetaObject &obj, const QBackendNodeMapperPtr &functor, bool supportsSyncing); + virtual QVariant executeCommand(const QStringList &args); virtual QVector<QAspectJobPtr> jobsToExecute(qint64 time); @@ -104,6 +109,12 @@ void QAbstractAspect::registerBackendType(const QBackendNodeMapperPtr &functor) registerBackendType(Frontend::staticMetaObject, functor); } +template<class Frontend, bool supportsSyncing> +void QAbstractAspect::registerBackendType(const QBackendNodeMapperPtr &functor) +{ + registerBackendType(Frontend::staticMetaObject, functor, supportsSyncing); +} + template<class Frontend> void QAbstractAspect::unregisterBackendType() { diff --git a/src/core/aspects/qabstractaspect_p.h b/src/core/aspects/qabstractaspect_p.h index e743dc63a..668b9670c 100644 --- a/src/core/aspects/qabstractaspect_p.h +++ b/src/core/aspects/qabstractaspect_p.h @@ -121,6 +121,9 @@ public: QBackendNode *createBackendNode(const QNodeCreatedChangeBasePtr &change) const override; void clearBackendNode(const QNodeDestroyedChangePtr &change) const; + void syncDirtyFrontEndNodes(const QVector<QNode *> &nodes); + virtual void syncDirtyFrontEndNode(QNode *node, QBackendNode *backend, bool firstTime) const; + void sendPropertyMessages(QNode *node, QBackendNode *backend) const; void sceneNodeAdded(Qt3DCore::QSceneChangePtr &e) override; void sceneNodeRemoved(Qt3DCore::QSceneChangePtr &e) override; @@ -134,12 +137,17 @@ public: Q_DECLARE_PUBLIC(QAbstractAspect) + enum NodeMapperInfo { + DefaultMapper = 0, + SupportsSyncing = 1 << 0 + }; + QEntity *m_root; QNodeId m_rootId; QAspectManager *m_aspectManager; QAbstractAspectJobManager *m_jobManager; QChangeArbiter *m_arbiter; - QHash<const QMetaObject*, QBackendNodeMapperPtr> m_backendCreatorFunctors; + QHash<const QMetaObject*, QPair<QBackendNodeMapperPtr, NodeMapperInfo>> m_backendCreatorFunctors; QMutex m_singleShotMutex; QVector<QAspectJobPtr> m_singleShotJobs; diff --git a/src/core/aspects/qaspectengine.cpp b/src/core/aspects/qaspectengine.cpp index 222c4c2af..c5e4b2b26 100644 --- a/src/core/aspects/qaspectengine.cpp +++ b/src/core/aspects/qaspectengine.cpp @@ -47,7 +47,6 @@ #include <QtCore/QMetaObject> #include <Qt3DCore/private/corelogging_p.h> -#include <Qt3DCore/private/qaspectthread_p.h> #include <Qt3DCore/private/qaspectmanager_p.h> #include <Qt3DCore/private/qchangearbiter_p.h> #include <Qt3DCore/private/qeventfilterservice_p.h> @@ -74,9 +73,11 @@ QAspectEnginePrivate *QAspectEnginePrivate::get(QAspectEngine *q) QAspectEnginePrivate::QAspectEnginePrivate() : QObjectPrivate() + , m_aspectManager(nullptr) , m_postman(nullptr) , m_scene(nullptr) , m_initialized(false) + , m_runMode(QAspectEngine::Automatic) #if QT_CONFIG(qt3d_profile_jobs) , m_commandDebugger(new Debug::AspectCommandDebugger(q_func())) #endif @@ -119,12 +120,6 @@ void QAspectEnginePrivate::initEntity(QEntity *entity) } } -void QAspectEnginePrivate::generateCreationChanges(QNode *root) -{ - const QNodeCreatedChangeGenerator generator(root); - m_creationChanges = generator.creationChanges(); -} - /*! * \class Qt3DCore::QAspectEngine * \inheaderfile Qt3DCore/QAspectEngine @@ -185,8 +180,7 @@ QAspectEngine::QAspectEngine(QObject *parent) d->m_scene = new QScene(this); d->m_postman = new QPostman(this); d->m_postman->setScene(d->m_scene); - d->m_aspectThread = new QAspectThread(this); - d->m_aspectThread->waitForStart(QThread::HighestPriority); + d->m_aspectManager = new QAspectManager(this); } /*! @@ -206,11 +200,6 @@ QAspectEngine::~QAspectEngine() for (auto aspect : aspects) unregisterAspect(aspect); - // Wait for thread to have completed it's final loop of execution - d->m_aspectThread->aspectManager()->quit(); - d->m_aspectThread->wait(); - - delete d->m_aspectThread; delete d->m_postman; delete d->m_scene; } @@ -225,15 +214,12 @@ void QAspectEnginePrivate::initNodeTree(QNode *node) void QAspectEnginePrivate::initialize() { - QChangeArbiter *arbiter = m_aspectThread->aspectManager()->changeArbiter(); + m_aspectManager->initialize(); + QChangeArbiter *arbiter = m_aspectManager->changeArbiter(); m_scene->setArbiter(arbiter); QChangeArbiter::createUnmanagedThreadLocalChangeQueue(arbiter); - QMetaObject::invokeMethod(arbiter, - "setPostman", - Q_ARG(Qt3DCore::QAbstractPostman*, m_postman)); - QMetaObject::invokeMethod(arbiter, - "setScene", - Q_ARG(Qt3DCore::QScene*, m_scene)); + arbiter->setPostman(m_postman); + arbiter->setScene(m_scene); m_initialized = true; #if QT_CONFIG(qt3d_profile_jobs) m_commandDebugger->setAspectEngine(q_func()); @@ -263,14 +249,14 @@ void QAspectEnginePrivate::shutdown() // Cleanup the scene before quitting the backend m_scene->setArbiter(nullptr); - QChangeArbiter *arbiter = m_aspectThread->aspectManager()->changeArbiter(); + QChangeArbiter *arbiter = m_aspectManager->changeArbiter(); QChangeArbiter::destroyUnmanagedThreadLocalChangeQueue(arbiter); m_initialized = false; } void QAspectEnginePrivate::exitSimulationLoop() { - m_aspectThread->aspectManager()->exitSimulationLoop(); + m_aspectManager->exitSimulationLoop(); } /*! @@ -285,12 +271,8 @@ void QAspectEngine::registerAspect(QAbstractAspect *aspect) // AspectManager::registerAspect is called in the context // of the AspectThread. This is turns call aspect->onInitialize // still in the same AspectThread context - aspect->moveToThread(d->m_aspectThread); d->m_aspects << aspect; - QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(), - "registerAspect", - Qt::BlockingQueuedConnection, - Q_ARG(Qt3DCore::QAbstractAspect*, aspect)); + d->m_aspectManager->registerAspect(aspect); } /*! @@ -327,10 +309,7 @@ void QAspectEngine::unregisterAspect(QAbstractAspect *aspect) // Tell the aspect manager to give the aspect a chance to do some cleanup // in its QAbstractAspect::onUnregistered() virtual - QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(), - "unregisterAspect", - Qt::BlockingQueuedConnection, - Q_ARG(Qt3DCore::QAbstractAspect*, aspect)); + d->m_aspectManager->unregisterAspect(aspect); // Remove from our collection of named aspects (if present) const auto it = std::find_if(d->m_namedAspects.begin(), d->m_namedAspects.end(), @@ -409,6 +388,19 @@ QVariant QAspectEngine::executeCommand(const QString &command) } /*! + * If using the manual run mode, this function executes the jobs for each aspect. + * It is blocking and won't return until all jobs have been completed. + * + * If you are using the QRenderAspect, + */ +void QAspectEngine::processFrame() +{ + Q_D(QAspectEngine); + Q_ASSERT(d->m_runMode == QAspectEngine::Manual); + d->m_aspectManager->processFrame(); +} + +/*! * Sets the \a root entity for the aspect engine. */ void QAspectEngine::setRootEntity(QEntityPtr root) @@ -446,7 +438,11 @@ void QAspectEngine::setRootEntity(QEntityPtr root) d->initNodeTree(root.data()); // Traverse tree to generate a vector of creation changes - d->generateCreationChanges(root.data()); + const QNodeCreatedChangeGenerator generator(root.data()); + auto creationChanges = generator.creationChanges(); + + // Specify if the AspectManager should be driving the simulation loop or not + d->m_aspectManager->setRunMode(d->m_runMode); // Finally, tell the aspects about the new scene object tree. This is done // in a blocking manner to allow the aspects to get synchronized before the @@ -455,14 +451,9 @@ void QAspectEngine::setRootEntity(QEntityPtr root) // TODO: Pass the creation changes via the arbiter rather than relying upon // an invokeMethod call. qCDebug(Aspects) << "Begin setting scene root on aspect manager"; - QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(), - "setRootEntity", - Qt::BlockingQueuedConnection, - Q_ARG(Qt3DCore::QEntity*, root.data()), - Q_ARG(QVector<Qt3DCore::QNodeCreatedChangeBasePtr>, d->m_creationChanges)); + d->m_aspectManager->setRootEntity(root.data(), creationChanges); qCDebug(Aspects) << "Done setting scene root on aspect manager"; - - d->m_aspectThread->aspectManager()->enterSimulationLoop(); + d->m_aspectManager->enterSimulationLoop(); } /*! @@ -474,6 +465,20 @@ QEntityPtr QAspectEngine::rootEntity() const return d->m_root; } +void QAspectEngine::setRunMode(QAspectEngine::RunMode mode) +{ + Q_D(QAspectEngine); + d->m_runMode = mode; + if (d->m_aspectManager) + d->m_aspectManager->setRunMode(mode); +} + +QAspectEngine::RunMode QAspectEngine::runMode() const +{ + Q_D(const QAspectEngine); + return d->m_runMode; +} + } // namespace Qt3DCore QT_END_NAMESPACE diff --git a/src/core/aspects/qaspectengine.h b/src/core/aspects/qaspectengine.h index 7f900ad52..1d5d7e220 100644 --- a/src/core/aspects/qaspectengine.h +++ b/src/core/aspects/qaspectengine.h @@ -60,12 +60,21 @@ class Q_3DCORESHARED_EXPORT QAspectEngine : public QObject { Q_OBJECT public: + enum RunMode { + Manual = 0, + Automatic + }; + Q_ENUM(RunMode) + explicit QAspectEngine(QObject *parent = nullptr); ~QAspectEngine(); void setRootEntity(QEntityPtr root); QEntityPtr rootEntity() const; + void setRunMode(RunMode mode); + RunMode runMode() const; + void registerAspect(QAbstractAspect *aspect); void registerAspect(const QString &name); void unregisterAspect(QAbstractAspect *aspect); @@ -75,6 +84,8 @@ public: QVariant executeCommand(const QString &command); + void processFrame(); + private: Q_DECLARE_PRIVATE(QAspectEngine) }; diff --git a/src/core/aspects/qaspectengine_p.h b/src/core/aspects/qaspectengine_p.h index 2d7d0fe93..7dad2a352 100644 --- a/src/core/aspects/qaspectengine_p.h +++ b/src/core/aspects/qaspectengine_p.h @@ -56,6 +56,7 @@ #include <QtCore/qsharedpointer.h> #include <Qt3DCore/private/qaspectfactory_p.h> +#include <Qt3DCore/private/qaspectengine_p.h> #include <QtCore/private/qobject_p.h> QT_BEGIN_NAMESPACE @@ -64,8 +65,7 @@ namespace Qt3DCore { class QEntity; class QNode; -class QAspectEngine; -class QAspectThread; +class QAspectManager; class QPostman; class QScene; @@ -84,13 +84,14 @@ public: Q_DECLARE_PUBLIC(QAspectEngine) QAspectFactory m_factory; - QAspectThread *m_aspectThread; + QAspectManager *m_aspectManager; QPostman *m_postman; QScene *m_scene; QSharedPointer<QEntity> m_root; QVector<QAbstractAspect*> m_aspects; QHash<QString, QAbstractAspect *> m_namedAspects; bool m_initialized; + QAspectEngine::RunMode m_runMode; #if QT_CONFIG(qt3d_profile_jobs) Debug::AspectCommandDebugger *m_commandDebugger; @@ -105,9 +106,6 @@ public: void initNode(QNode *node); void initEntity(QEntity *entity); - void generateCreationChanges(QNode *rootNode); - QVector<QNodeCreatedChangeBasePtr> m_creationChanges; - static QAspectEnginePrivate *get(QAspectEngine *engine); }; diff --git a/src/core/aspects/qaspectmanager.cpp b/src/core/aspects/qaspectmanager.cpp index e9642116a..0c16de882 100644 --- a/src/core/aspects/qaspectmanager.cpp +++ b/src/core/aspects/qaspectmanager.cpp @@ -61,6 +61,8 @@ #include <Qt3DCore/private/qtickclock_p.h> #include <Qt3DCore/private/qtickclockservice_p.h> +#include <QtCore/QCoreApplication> + #if defined(QT3D_CORE_JOB_TIMING) #include <QElapsedTimer> #endif @@ -69,6 +71,26 @@ QT_BEGIN_NAMESPACE namespace Qt3DCore { +namespace { + +class RequestFrameEvent : public QEvent +{ +public: + RequestFrameEvent() + : QEvent(static_cast<QEvent::Type>(RequestFrameEvent::requestEventType)) + {} + + static int eventType() { return RequestFrameEvent::requestEventType; } + +private: + static int requestEventType; +}; + +int RequestFrameEvent::requestEventType = QEvent::registerEventType(); + +} // anonymous + + /*! \class Qt3DCore::QAspectManager \internal @@ -80,14 +102,10 @@ QAspectManager::QAspectManager(QObject *parent) , m_jobManager(new QAspectJobManager(this)) , m_changeArbiter(new QChangeArbiter(this)) , m_serviceLocator(new QServiceLocator()) - , m_waitForEndOfSimulationLoop(0) - , m_waitForStartOfSimulationLoop(0) - , m_waitForEndOfExecLoop(0) - , m_waitForQuit(0) + , m_simulationLoopRunning(false) + , m_driveMode(QAspectEngine::Automatic) { qRegisterMetaType<QSurface *>("QSurface*"); - m_runSimulationLoop.fetchAndStoreOrdered(0); - m_runMainLoop.fetchAndStoreOrdered(1); qCDebug(Aspects) << Q_FUNC_INFO; } @@ -98,19 +116,37 @@ QAspectManager::~QAspectManager() delete m_scheduler; } +void QAspectManager::setRunMode(QAspectEngine::RunMode mode) +{ + qCDebug(Aspects) << Q_FUNC_INFO << "Running Loop Drive Mode set to" << mode; + m_driveMode = mode; +} + // Main thread (called by QAspectEngine) void QAspectManager::enterSimulationLoop() { qCDebug(Aspects) << Q_FUNC_INFO; - m_runSimulationLoop.fetchAndStoreOrdered(1); + m_simulationLoopRunning = true; + + // Retrieve the frame advance service. Defaults to timer based if there is no renderer. + QAbstractFrameAdvanceService *frameAdvanceService = + m_serviceLocator->service<QAbstractFrameAdvanceService>(QServiceLocator::FrameAdvanceService); - // Wake up QAspectThread's event loop - thread()->eventDispatcher()->wakeUp(); + // Start the frameAdvanceService + frameAdvanceService->start(); - // We wait for the setRootEntity on the aspectManager to have completed - // This ensures we cannot shutdown before the aspects have had a chance - // to be initialized - m_waitForStartOfSimulationLoop.acquire(1); + // We are about to enter the simulation loop. Give aspects a chance to do any last + // pieces of initialization + qCDebug(Aspects) << "Calling onEngineStartup() for each aspect"; + for (QAbstractAspect *aspect : qAsConst(m_aspects)) { + qCDebug(Aspects) << "\t" << aspect->objectName(); + aspect->onEngineStartup(); + } + qCDebug(Aspects) << "Done calling onEngineStartup() for each aspect"; + + // Start running loop if Qt3D is in charge of driving it + if (m_driveMode == QAspectEngine::Automatic) + requestNextFrame(); } // Main thread (called by QAspectEngine) @@ -119,7 +155,7 @@ void QAspectManager::exitSimulationLoop() qCDebug(Aspects) << Q_FUNC_INFO; // If this fails, simulation loop is already exited so nothing to do - if (!m_runSimulationLoop.testAndSetOrdered(1, 0)) { + if (!m_simulationLoopRunning) { qCDebug(Aspects) << "Simulation loop was not running. Nothing to do"; return; } @@ -141,17 +177,26 @@ void QAspectManager::exitSimulationLoop() for (QAbstractAspect *aspect : qAsConst(m_aspects)) aspect->d_func()->onEngineAboutToShutdown(); + // Process any pending changes from the frontend before we shut the aspects down + m_changeArbiter->syncChanges(); + + // Give aspects a chance to perform any shutdown actions. This may include unqueuing + // any blocking work on the main thread that could potentially deadlock during shutdown. + qCDebug(Aspects) << "Calling onEngineShutdown() for each aspect"; + for (QAbstractAspect *aspect : qAsConst(m_aspects)) { + qCDebug(Aspects) << "\t" << aspect->objectName(); + aspect->onEngineShutdown(); + } + qCDebug(Aspects) << "Done calling onEngineShutdown() for each aspect"; - qCDebug(Aspects) << "exitSimulationLoop waiting for exec loop to terminate"; - // Wait until the simulation loop is fully exited and the aspects are done - // processing any final changes and have had onEngineShutdown() called on them - m_waitForEndOfSimulationLoop.acquire(1); + + m_simulationLoopRunning = false; qCDebug(Aspects) << "exitSimulationLoop completed"; } bool QAspectManager::isShuttingDown() const { - return !m_runSimulationLoop.load(); + return !m_simulationLoopRunning; } /*! @@ -184,7 +229,7 @@ void QAspectManager::shutdown() // Aspects must be deleted in the Thread they were created in } -// QAspectThread:: queued invoked by QAspectEngine::setRootEntity +// MainThread called by QAspectEngine::setRootEntity void QAspectManager::setRootEntity(Qt3DCore::QEntity *root, const QVector<Qt3DCore::QNodeCreatedChangeBasePtr> &changes) { qCDebug(Aspects) << Q_FUNC_INFO; @@ -251,118 +296,6 @@ void QAspectManager::unregisterAspect(Qt3DCore::QAbstractAspect *aspect) qCDebug(Aspects) << "Completed unregistering aspect"; } -void QAspectManager::exec() -{ - // Gentlemen, start your engines - QEventLoop eventLoop; - - // Enter the engine loop - qCDebug(Aspects) << Q_FUNC_INFO << "***** Entering main loop *****"; - while (m_runMainLoop.load()) { - // Process events until we're told to start the simulation loop - while (m_runMainLoop.load() && !m_runSimulationLoop.load()) - eventLoop.processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); - - if (!m_runSimulationLoop.load()) - break; - - // Retrieve the frame advance service. Defaults to timer based if there is no renderer. - QAbstractFrameAdvanceService *frameAdvanceService = - m_serviceLocator->service<QAbstractFrameAdvanceService>(QServiceLocator::FrameAdvanceService); - - // Start the frameAdvanceService - frameAdvanceService->start(); - - // We are about to enter the simulation loop. Give aspects a chance to do any last - // pieces of initialization - qCDebug(Aspects) << "Calling onEngineStartup() for each aspect"; - for (QAbstractAspect *aspect : qAsConst(m_aspects)) { - qCDebug(Aspects) << "\t" << aspect->objectName(); - aspect->onEngineStartup(); - } - qCDebug(Aspects) << "Done calling onEngineStartup() for each aspect"; - m_waitForStartOfSimulationLoop.release(1); - - // Only enter main simulation loop once the renderer and other aspects are initialized - while (m_runSimulationLoop.load()) { - qint64 t = frameAdvanceService->waitForNextFrame(); - - // Distribute accumulated changes. This includes changes sent from the frontend - // to the backend nodes. We call this before the call to m_scheduler->update() to ensure - // that any property changes do not set dirty flags in a data race with the renderer's - // submission thread which may be looking for dirty flags, acting upon them and then - // clearing the dirty flags. - // - // Doing this as the first call in the new frame ensures the lock free approach works - // without any such data race. -#if QT_CONFIG(qt3d_profile_jobs) - const quint32 arbiterId = 4096; - JobRunStats changeArbiterStats; - changeArbiterStats.jobId.typeAndInstance[0] = arbiterId; - changeArbiterStats.jobId.typeAndInstance[1] = 0; - changeArbiterStats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); - changeArbiterStats.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); -#endif - m_changeArbiter->syncChanges(); -#if QT_CONFIG(qt3d_profile_jobs) - changeArbiterStats.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); - QThreadPooler::addJobLogStatsEntry(changeArbiterStats); -#endif - - // For each Aspect - // Ask them to launch set of jobs for the current frame - // Updates matrices, bounding volumes, render bins ... -#if defined(QT3D_CORE_JOB_TIMING) - QElapsedTimer timer; - timer.start(); -#endif - m_scheduler->scheduleAndWaitForFrameAspectJobs(t); -#if defined(QT3D_CORE_JOB_TIMING) - qDebug() << "Jobs took" << timer.nsecsElapsed() / 1.0e6; -#endif - - // Process any pending events - eventLoop.processEvents(); - } // End of simulation loop - - // Process any pending changes from the frontend before we shut the aspects down - m_changeArbiter->syncChanges(); - - // Give aspects a chance to perform any shutdown actions. This may include unqueuing - // any blocking work on the main thread that could potentially deadlock during shutdown. - qCDebug(Aspects) << "Calling onEngineShutdown() for each aspect"; - for (QAbstractAspect *aspect : qAsConst(m_aspects)) { - qCDebug(Aspects) << "\t" << aspect->objectName(); - aspect->onEngineShutdown(); - } - qCDebug(Aspects) << "Done calling onEngineShutdown() for each aspect"; - - // Wake up the main thread which is waiting for us inside of exitSimulationLoop() - m_waitForEndOfSimulationLoop.release(1); - } // End of main loop - qCDebug(Aspects) << Q_FUNC_INFO << "***** Exited main loop *****"; - - m_waitForEndOfExecLoop.release(1); - m_waitForQuit.acquire(1); -} - -void QAspectManager::quit() -{ - qCDebug(Aspects) << Q_FUNC_INFO; - - Q_ASSERT_X(m_runSimulationLoop.load() == 0, "QAspectManagr::quit()", "Inner loop is still running"); - m_runMainLoop.fetchAndStoreOrdered(0); - - // Wake up QAspectThread's event loop if needed - thread()->eventDispatcher()->wakeUp(); - - // We need to wait for the QAspectManager exec loop to terminate - m_waitForEndOfExecLoop.acquire(1); - m_waitForQuit.release(1); - - qCDebug(Aspects) << Q_FUNC_INFO << "Exiting"; -} - const QVector<QAbstractAspect *> &QAspectManager::aspects() const { return m_aspects; @@ -383,7 +316,88 @@ QServiceLocator *QAspectManager::serviceLocator() const return m_serviceLocator.data(); } +/*! + \internal + \brief Drives the Qt3D simulation loop in the main thread + */ +bool QAspectManager::event(QEvent *e) +{ + if (e->type() == RequestFrameEvent::eventType()) { + + // Process current frame + processFrame(); + + // Request next frame if we are still running and if Qt3D is driving + // the loop + if (m_simulationLoopRunning && m_driveMode == QAspectEngine::Automatic) + requestNextFrame(); + } + + return QObject::event(e); +} + +void QAspectManager::requestNextFrame() +{ + qCDebug(Aspects) << "Requesting new Frame"; + // Post event in the event loop to force + // next frame to be processed + qApp->postEvent(this, new RequestFrameEvent()); +} + +void QAspectManager::processFrame() +{ + qCDebug(Aspects) << "Processing Frame"; + + // Retrieve the frame advance service. Defaults to timer based if there is no renderer. + QAbstractFrameAdvanceService *frameAdvanceService = + m_serviceLocator->service<QAbstractFrameAdvanceService>(QServiceLocator::FrameAdvanceService); + + const qint64 t = frameAdvanceService->waitForNextFrame(); + + // Distribute accumulated changes. This includes changes sent from the frontend + // to the backend nodes. We call this before the call to m_scheduler->update() to ensure + // that any property changes do not set dirty flags in a data race with the renderer's + // submission thread which may be looking for dirty flags, acting upon them and then + // clearing the dirty flags. + // + // Doing this as the first call in the new frame ensures the lock free approach works + // without any such data race. +#if QT_CONFIG(qt3d_profile_jobs) + const quint32 arbiterId = 4096; + JobRunStats changeArbiterStats; + changeArbiterStats.jobId.typeAndInstance[0] = arbiterId; + changeArbiterStats.jobId.typeAndInstance[1] = 0; + changeArbiterStats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); + changeArbiterStats.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); +#endif + const auto dirtyFrontEndNodes = m_changeArbiter->takeDirtyFrontEndNodes(); + if (dirtyFrontEndNodes.size()) + for (QAbstractAspect *aspect : qAsConst(m_aspects)) + aspect->syncDirtyFrontEndNodes(dirtyFrontEndNodes); + // TO DO: Having this done in the main thread actually means aspects could just + // as simply read info out of the Frontend classes without risk of introducing + // races. This could therefore be removed for Qt 6. + m_changeArbiter->syncChanges(); +#if QT_CONFIG(qt3d_profile_jobs) + changeArbiterStats.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); + QThreadPooler::addJobLogStatsEntry(changeArbiterStats); +#endif + + // For each Aspect + // Ask them to launch set of jobs for the current frame + // Updates matrices, bounding volumes, render bins ... +#if defined(QT3D_CORE_JOB_TIMING) + QElapsedTimer timer; + timer.start(); +#endif + m_scheduler->scheduleAndWaitForFrameAspectJobs(t); +#if defined(QT3D_CORE_JOB_TIMING) + qDebug() << "Jobs took" << timer.nsecsElapsed() / 1.0e6; +#endif + + // TODO sync backend changes to frontend +} + } // namespace Qt3DCore QT_END_NAMESPACE - diff --git a/src/core/aspects/qaspectmanager_p.h b/src/core/aspects/qaspectmanager_p.h index de978b8e9..d654f7b5a 100644 --- a/src/core/aspects/qaspectmanager_p.h +++ b/src/core/aspects/qaspectmanager_p.h @@ -52,6 +52,7 @@ // #include <Qt3DCore/qnodecreatedchange.h> +#include <Qt3DCore/qaspectengine.h> #include <QtCore/QObject> #include <QtCore/QScopedPointer> #include <QtCore/QSemaphore> @@ -82,6 +83,7 @@ public: explicit QAspectManager(QObject *parent = 0); ~QAspectManager(); + void setRunMode(QAspectEngine::RunMode mode); void enterSimulationLoop(); void exitSimulationLoop(); @@ -90,14 +92,12 @@ public: public Q_SLOTS: void initialize(); void shutdown(); + void processFrame(); void setRootEntity(Qt3DCore::QEntity *root, const QVector<Qt3DCore::QNodeCreatedChangeBasePtr> &changes); void registerAspect(Qt3DCore::QAbstractAspect *aspect); void unregisterAspect(Qt3DCore::QAbstractAspect *aspect); - void exec(); - void quit(); - public: const QVector<QAbstractAspect *> &aspects() const; QAbstractAspectJobManager *jobManager() const; @@ -105,19 +105,20 @@ public: QServiceLocator *serviceLocator() const; private: + bool event(QEvent *event) override; + void requestNextFrame(); + QVector<QAbstractAspect *> m_aspects; QEntity *m_root; QVariantMap m_data; QScheduler *m_scheduler; QAbstractAspectJobManager *m_jobManager; QChangeArbiter *m_changeArbiter; - QAtomicInt m_runSimulationLoop; - QAtomicInt m_runMainLoop; QScopedPointer<QServiceLocator> m_serviceLocator; - QSemaphore m_waitForEndOfSimulationLoop; - QSemaphore m_waitForStartOfSimulationLoop; - QSemaphore m_waitForEndOfExecLoop; - QSemaphore m_waitForQuit; + bool m_mainLoopRunning; + bool m_simulationLoopRunning; + QAspectEngine::RunMode m_driveMode; + }; } // namespace Qt3DCore diff --git a/src/core/aspects/qaspectthread.cpp b/src/core/aspects/qaspectthread.cpp deleted file mode 100644 index 31715b04e..000000000 --- a/src/core/aspects/qaspectthread.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt3D module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qaspectthread_p.h" - -#include <QtCore/QMutexLocker> - -#include <Qt3DCore/private/qaspectmanager_p.h> -#include <Qt3DCore/private/corelogging_p.h> - -QT_BEGIN_NAMESPACE - -namespace Qt3DCore { - -QAspectThread::QAspectThread(QObject *parent) - : QThread(parent), - m_aspectManager(nullptr), - m_semaphore(0) -{ - qCDebug(Aspects) << Q_FUNC_INFO; -} - -QAspectThread::~QAspectThread() -{ -} - -void QAspectThread::waitForStart(Priority priority) -{ - qCDebug(Aspects) << "Starting QAspectThread and going to sleep until it is ready for us..."; - start(priority); - m_semaphore.acquire(); - qCDebug(Aspects) << "QAspectThead is now ready & calling thread is now awake again"; -} - -void QAspectThread::run() -{ - qCDebug(Aspects) << "Entering void QAspectThread::run()"; - - m_aspectManager = new QAspectManager; - - // Load and initialize the aspects and any other core services - // Done before releasing condition to make sure that Qml Components - // Are exposed prior to Qml Engine source being set - m_aspectManager->initialize(); - - // Wake up the calling thread now that our worker objects are ready for action - m_semaphore.release(); - - // Enter the main loop - m_aspectManager->exec(); - - // Clean up - m_aspectManager->shutdown(); - - // Delete the aspect manager while we're still in the thread - delete m_aspectManager; - - qCDebug(Aspects) << "Exiting void QAspectThread::run()"; -} - -} // namespace Qt3DCore - -QT_END_NAMESPACE diff --git a/src/core/aspects/qaspectthread_p.h b/src/core/aspects/qaspectthread_p.h deleted file mode 100644 index 8ae9ff86d..000000000 --- a/src/core/aspects/qaspectthread_p.h +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the Qt3D module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT3DCORE_QASPECTTHREAD_P_H -#define QT3DCORE_QASPECTTHREAD_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists for the convenience -// of other Qt classes. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <Qt3DCore/qt3dcore_global.h> -#include <QtCore/QSemaphore> -#include <QtCore/QThread> - - -QT_BEGIN_NAMESPACE - -namespace Qt3DCore { - -class QAspectManager; - -class QAspectThread : public QThread -{ - Q_OBJECT -public: - explicit QAspectThread(QObject *parent = 0); - ~QAspectThread(); - - void waitForStart(Priority priority); - - QAspectManager *aspectManager() const { return m_aspectManager; } - -protected: - void run() override; - -private: - QAspectManager *m_aspectManager; - QSemaphore m_semaphore; -}; - -} // namespace Qt3DCore - -QT_END_NAMESPACE - -#endif // QT3DCORE_QASPECTTHREAD_P_H diff --git a/src/core/jobs/qthreadpooler.cpp b/src/core/jobs/qthreadpooler.cpp index ca123ddad..0e9af58dd 100644 --- a/src/core/jobs/qthreadpooler.cpp +++ b/src/core/jobs/qthreadpooler.cpp @@ -212,7 +212,7 @@ void QThreadPooler::addJobLogStatsEntry(JobRunStats &stats) jobStatsCached.localData()->push_back(stats); } -// Called after jobs have been executed (AspectThread QAspectJobManager::enqueueJobs) +// Called after jobs have been executed (MainThread QAspectJobManager::enqueueJobs) void QThreadPooler::writeFrameJobLogStats() { static QScopedPointer<QFile> traceFile; @@ -268,7 +268,7 @@ void QThreadPooler::writeFrameJobLogStats() ++frameId; } -// Called from Submission thread +// Called from Submission thread (which can be main thread in Manual drive mode) void QThreadPooler::addSubmissionLogStatsEntry(JobRunStats &stats) { QMutexLocker lock(&localStoragesMutex); @@ -276,6 +276,12 @@ void QThreadPooler::addSubmissionLogStatsEntry(JobRunStats &stats) submissionStorage = new QVector<JobRunStats>; jobStatsCached.setLocalData(submissionStorage); } + + // Handle the case where submission thread is also the main thread (Scene/Manual drive modes with no RenderThread) + if (submissionStorage == nullptr && jobStatsCached.hasLocalData()) + submissionStorage = new QVector<JobRunStats>; + + // When having no submission thread this can be null submissionStorage->push_back(stats); } diff --git a/src/core/nodes/qbackendnode.cpp b/src/core/nodes/qbackendnode.cpp index f10bf5769..3eb1cd9f7 100644 --- a/src/core/nodes/qbackendnode.cpp +++ b/src/core/nodes/qbackendnode.cpp @@ -256,16 +256,16 @@ void QBackendNode::setEnabled(bool enabled) Q_DECL_NOTHROW void QBackendNode::sceneChangeEvent(const QSceneChangePtr &e) { Q_D(QBackendNode); - auto propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e); switch (e->type()) { - case PropertyUpdated: { - if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) - d->m_enabled = propertyChange->value().toBool(); - break; - } - default: - break; + case PropertyUpdated: { + auto propertyChange = qSharedPointerCast<QPropertyUpdatedChange>(e); + if (propertyChange->propertyName() == QByteArrayLiteral("enabled")) + d->m_enabled = propertyChange->value().toBool(); + break; + } + default: + break; } } diff --git a/src/core/nodes/qcomponent.cpp b/src/core/nodes/qcomponent.cpp index f67989b1e..2fe3c8094 100644 --- a/src/core/nodes/qcomponent.cpp +++ b/src/core/nodes/qcomponent.cpp @@ -73,7 +73,7 @@ void QComponentPrivate::addEntity(QEntity *entity) m_scene->addEntityForComponent(m_id, entity->id()); } - const auto componentAddedChange = QComponentAddedChangePtr::create(q, entity); + const auto componentAddedChange = QComponentAddedChangePtr::create(q, entity); // TODOSYNC notify backend directly notifyObservers(componentAddedChange); Q_EMIT q->addedToEntity(entity); } @@ -86,7 +86,7 @@ void QComponentPrivate::removeEntity(QEntity *entity) m_entities.removeAll(entity); - const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, entity); + const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, entity); // TODOSYNC notify backend directly notifyObservers(componentRemovedChange); Q_EMIT q->removedFromEntity(entity); } diff --git a/src/core/nodes/qentity.cpp b/src/core/nodes/qentity.cpp index 1d16e828e..a1f7d2859 100644 --- a/src/core/nodes/qentity.cpp +++ b/src/core/nodes/qentity.cpp @@ -105,7 +105,7 @@ void QEntityPrivate::removeDestroyedComponent(QComponent *comp) Q_Q(QEntity); if (m_changeArbiter) { - const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, comp); + const auto componentRemovedChange = QComponentRemovedChangePtr::create(q, comp); // TODOSYNC notify backend directly notifyObservers(componentRemovedChange); } @@ -187,7 +187,7 @@ void QEntity::addComponent(QComponent *comp) d->registerPrivateDestructionHelper(comp, &QEntityPrivate::removeDestroyedComponent); if (d->m_changeArbiter) { - const auto componentAddedChange = QComponentAddedChangePtr::create(this, comp); + const auto componentAddedChange = QComponentAddedChangePtr::create(this, comp); // TODOSYNC notify backend directly d->notifyObservers(componentAddedChange); } static_cast<QComponentPrivate *>(QComponentPrivate::get(comp))->addEntity(this); diff --git a/src/core/nodes/qnode.cpp b/src/core/nodes/qnode.cpp index 900c3f8ce..45d58bd8c 100644 --- a/src/core/nodes/qnode.cpp +++ b/src/core/nodes/qnode.cpp @@ -385,48 +385,13 @@ void QNodePrivate::unregisterNotifiedProperties() void QNodePrivate::propertyChanged(int propertyIndex) { + Q_UNUSED(propertyIndex); + // Bail out early if we can to avoid the cost below if (m_blockNotifications) return; - const auto toBackendValue = [](const QVariant &data) -> QVariant - { - if (data.canConvert<QNode*>()) { - QNode *node = data.value<QNode*>(); - - // Ensure the node and all ancestors have issued their node creation changes. - // We can end up here if a newly created node with a parent is immediately set - // as a property on another node. In this case the deferred call to - // _q_postConstructorInit() will not have happened yet as the event - // loop will still be blocked. We need to do this for all ancestors, - // since the subtree of this node otherwise can end up on the backend - // with a reference to a non-existent parent. - if (node) - QNodePrivate::get(node)->_q_ensureBackendNodeCreated(); - - const QNodeId id = node ? node->id() : QNodeId(); - return QVariant::fromValue(id); - } - - return data; - }; - - Q_Q(QNode); - - const QMetaProperty property = q->metaObject()->property(propertyIndex); - - const QVariant data = property.read(q); - - if (data.type() == QVariant::List) { - QSequentialIterable iterable = data.value<QSequentialIterable>(); - QVariantList variants; - variants.reserve(iterable.size()); - for (const auto &v : iterable) - variants.append(toBackendValue(v)); - notifyPropertyChange(property.name(), variants); - } else { - notifyPropertyChange(property.name(), toBackendValue(data)); - } + update(); } /*! @@ -498,8 +463,13 @@ void QNodePrivate::addEntityComponentToScene(QNode *root) // Called in the main thread by QScene -> following QEvent::childAdded / addChild void QNodePrivate::setArbiter(QLockableObserverInterface *arbiter) { - if (m_changeArbiter && m_changeArbiter != arbiter) + if (m_changeArbiter && m_changeArbiter != arbiter) { unregisterNotifiedProperties(); + + // Remove node from dirtyFrontendNodeList on old arbiter + Q_Q(QNode); + m_changeArbiter->removeDirtyFrontEndNode(q); + } m_changeArbiter = static_cast<QAbstractArbiter *>(arbiter); if (m_changeArbiter) registerNotifiedProperties(); @@ -626,26 +596,26 @@ QScene *QNodePrivate::scene() const */ void QNodePrivate::notifyPropertyChange(const char *name, const QVariant &value) { + Q_UNUSED(name); + Q_UNUSED(value); + // Bail out early if we can to avoid operator new if (m_blockNotifications) return; - auto e = QPropertyUpdatedChangePtr::create(m_id); - e->setPropertyName(name); - e->setValue(value); - notifyObservers(e); + update(); } void QNodePrivate::notifyDynamicPropertyChange(const QByteArray &name, const QVariant &value) { + Q_UNUSED(name); + Q_UNUSED(value); + // Bail out early if we can to avoid operator new if (m_blockNotifications) return; - auto e = QDynamicPropertyUpdatedChangePtr::create(m_id); - e->setPropertyName(name); - e->setValue(value); - notifyObservers(e); + update(); } /*! @@ -704,6 +674,14 @@ void QNodePrivate::updatePropertyTrackMode() } } +void QNodePrivate::update() +{ + if (m_changeArbiter) { + Q_Q(QNode); + m_changeArbiter->addDirtyFrontEndNode(q); + } +} + /*! \internal */ diff --git a/src/core/nodes/qnode_p.h b/src/core/nodes/qnode_p.h index 511a0e562..990c2caeb 100644 --- a/src/core/nodes/qnode_p.h +++ b/src/core/nodes/qnode_p.h @@ -89,6 +89,8 @@ public: void insertTree(QNode *treeRoot, int depth = 0); void updatePropertyTrackMode(); + void update(); + Q_DECLARE_PUBLIC(QNode) // For now this just protects access to the m_changeArbiter. diff --git a/src/core/qchangearbiter.cpp b/src/core/qchangearbiter.cpp index 8cfc4b066..6ad72c895 100644 --- a/src/core/qchangearbiter.cpp +++ b/src/core/qchangearbiter.cpp @@ -266,6 +266,22 @@ void QChangeArbiter::sceneChangeEventWithLock(const QSceneChangeList &e) emit receivedChange(); } +void QChangeArbiter::addDirtyFrontEndNode(QNode *node) +{ + if (!m_dirtyFrontEndNodes.contains(node)) + m_dirtyFrontEndNodes += node; +} + +void QChangeArbiter::removeDirtyFrontEndNode(QNode *node) +{ + m_dirtyFrontEndNodes.removeOne(node); +} + +QVector<QNode *> QChangeArbiter::takeDirtyFrontEndNodes() +{ + return std::move(m_dirtyFrontEndNodes); +} + // Either we have the postman or we could make the QChangeArbiter agnostic to the postman // but that would require adding it to every QObserverList in m_aspectObservations. void QChangeArbiter::setPostman(QAbstractPostman *postman) diff --git a/src/core/qchangearbiter_p.h b/src/core/qchangearbiter_p.h index 1f453ff56..48fc4ca8c 100644 --- a/src/core/qchangearbiter_p.h +++ b/src/core/qchangearbiter_p.h @@ -81,6 +81,8 @@ class Q_3DCORE_PRIVATE_EXPORT QAbstractArbiter : public QLockableObserverInterfa { public: virtual QAbstractPostman *postman() const = 0; + virtual void addDirtyFrontEndNode(QNode *node) = 0; + virtual void removeDirtyFrontEndNode(QNode *node) = 0; }; class Q_3DCORE_PRIVATE_EXPORT QChangeArbiter final @@ -89,7 +91,7 @@ class Q_3DCORE_PRIVATE_EXPORT QChangeArbiter final { Q_OBJECT public: - explicit QChangeArbiter(QObject *parent = 0); + explicit QChangeArbiter(QObject *parent = nullptr); ~QChangeArbiter(); void initialize(Qt3DCore::QAbstractAspectJobManager *jobManager); @@ -109,8 +111,12 @@ public: void sceneChangeEventWithLock(const QSceneChangePtr &e) override; // QLockableObserverInterface impl void sceneChangeEventWithLock(const QSceneChangeList &e) override; // QLockableObserverInterface impl - Q_INVOKABLE void setPostman(Qt3DCore::QAbstractPostman *postman); - Q_INVOKABLE void setScene(Qt3DCore::QScene *scene); + void addDirtyFrontEndNode(QNode *node) override; + void removeDirtyFrontEndNode(QNode *node) override; + QVector<QNode *> takeDirtyFrontEndNodes(); + + void setPostman(Qt3DCore::QAbstractPostman *postman); + void setScene(Qt3DCore::QScene *scene); QAbstractPostman *postman() const final; QScene *scene() const; @@ -155,6 +161,8 @@ private: QList<QChangeQueue *> m_lockingChangeQueues; QAbstractPostman *m_postman; QScene *m_scene; + + QVector<QNode *> m_dirtyFrontEndNodes; }; } // namespace Qt3DCore diff --git a/src/core/qscene.cpp b/src/core/qscene.cpp index c94272e90..1b8996e8d 100644 --- a/src/core/qscene.cpp +++ b/src/core/qscene.cpp @@ -68,7 +68,7 @@ public: QMultiHash<QNodeId, QObservableInterface *> m_observablesLookupTable; QHash<QObservableInterface *, QNodeId> m_observableToUuid; QHash<QNodeId, QScene::NodePropertyTrackData> m_nodePropertyTrackModeLookupTable; - QLockableObserverInterface *m_arbiter; + QAbstractArbiter *m_arbiter; QScopedPointer<NodePostConstructorInit> m_postConstructorInit; mutable QReadWriteLock m_lock; mutable QReadWriteLock m_nodePropertyTrackModeLock; @@ -183,13 +183,13 @@ QNode *QScene::rootNode() const return d->m_rootNode; } -void QScene::setArbiter(QLockableObserverInterface *arbiter) +void QScene::setArbiter(QAbstractArbiter *arbiter) { Q_D(QScene); d->m_arbiter = arbiter; } -QLockableObserverInterface *QScene::arbiter() const +QAbstractArbiter *QScene::arbiter() const { Q_D(const QScene); return d->m_arbiter; diff --git a/src/core/qscene_p.h b/src/core/qscene_p.h index afcfb9b40..cdb85ebe6 100644 --- a/src/core/qscene_p.h +++ b/src/core/qscene_p.h @@ -65,6 +65,7 @@ namespace Qt3DCore { class QScenePrivate; class QAspectEngine; class NodePostConstructorInit; +class QAbstractArbiter; typedef QList<QObservableInterface *> QObservableList; @@ -88,8 +89,8 @@ public: QNode *rootNode() const; - void setArbiter(Qt3DCore::QLockableObserverInterface *arbiter); - Qt3DCore::QLockableObserverInterface *arbiter() const; + void setArbiter(QAbstractArbiter *arbiter); + QAbstractArbiter *arbiter() const; // Component -> Entities QVector<QNodeId> entitiesForComponent(QNodeId id) const; diff --git a/src/core/services/qdownloadhelperservice.cpp b/src/core/services/qdownloadhelperservice.cpp index 4cd89c5d0..deddf3ae0 100644 --- a/src/core/services/qdownloadhelperservice.cpp +++ b/src/core/services/qdownloadhelperservice.cpp @@ -39,10 +39,10 @@ #include "qdownloadhelperservice_p.h" #include "qdownloadnetworkworker_p.h" +#include <QtCore/QThread> #include <Qt3DCore/QAspectEngine> #include <Qt3DCore/private/qabstractserviceprovider_p.h> #include <Qt3DCore/private/qaspectengine_p.h> -#include <Qt3DCore/private/qaspectthread_p.h> #include <Qt3DCore/private/qaspectmanager_p.h> #include <Qt3DCore/private/qservicelocator_p.h> @@ -196,7 +196,7 @@ QString QDownloadHelperService::urlToLocalFileOrQrc(const QUrl &url) QDownloadHelperService *QDownloadHelperService::getService(QAspectEngine *engine) { auto enginePrivate = Qt3DCore::QAspectEnginePrivate::get(engine); - return enginePrivate->m_aspectThread->aspectManager()->serviceLocator()->downloadHelperService(); + return enginePrivate->m_aspectManager->serviceLocator()->downloadHelperService(); } bool QDownloadHelperService::isLocal(const QUrl &url) |