summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2019-08-28 08:05:58 +0200
committerPaul Lemire <paul.lemire@kdab.com>2019-08-28 08:05:58 +0200
commita936ddd7acb3fb91d50f1d586fd770a1637f8166 (patch)
tree3f4bf866e16a1863f8b3d1e2dcb7d39bd7f8aa9b /src/core
parent3dcc9719808a77ab644921df38ea82c268ea7a97 (diff)
parentf3268fcb2a4e73d99dbd66aa5e262a118b5a5480 (diff)
Merge remote-tracking branch 'qt-gerrit/wip/refactor' into HEAD
Diffstat (limited to 'src/core')
-rw-r--r--src/core/aspects/aspects.pri6
-rw-r--r--src/core/aspects/qabstractaspect.cpp102
-rw-r--r--src/core/aspects/qabstractaspect.h13
-rw-r--r--src/core/aspects/qabstractaspect_p.h10
-rw-r--r--src/core/aspects/qaspectengine.cpp85
-rw-r--r--src/core/aspects/qaspectengine.h11
-rw-r--r--src/core/aspects/qaspectengine_p.h10
-rw-r--r--src/core/aspects/qaspectmanager.cpp280
-rw-r--r--src/core/aspects/qaspectmanager_p.h19
-rw-r--r--src/core/aspects/qaspectthread.cpp99
-rw-r--r--src/core/aspects/qaspectthread_p.h88
-rw-r--r--src/core/jobs/qthreadpooler.cpp10
-rw-r--r--src/core/nodes/qbackendnode.cpp16
-rw-r--r--src/core/nodes/qcomponent.cpp4
-rw-r--r--src/core/nodes/qentity.cpp4
-rw-r--r--src/core/nodes/qnode.cpp72
-rw-r--r--src/core/nodes/qnode_p.h2
-rw-r--r--src/core/qchangearbiter.cpp16
-rw-r--r--src/core/qchangearbiter_p.h14
-rw-r--r--src/core/qscene.cpp6
-rw-r--r--src/core/qscene_p.h5
-rw-r--r--src/core/services/qdownloadhelperservice.cpp4
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)