1// Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qaspectengine.h"
5#include "qaspectengine_p.h"
6
7#include <Qt3DCore/qabstractaspect.h>
8#include <Qt3DCore/qcomponent.h>
9#include <Qt3DCore/qentity.h>
10#include <Qt3DCore/qnode.h>
11#include <QtCore/QMetaObject>
12
13#include <Qt3DCore/private/corelogging_p.h>
14#include <Qt3DCore/private/qaspectmanager_p.h>
15#include <Qt3DCore/private/qchangearbiter_p.h>
16#include <Qt3DCore/private/qnode_p.h>
17#include <Qt3DCore/private/qnodevisitor_p.h>
18#include <Qt3DCore/private/qscene_p.h>
19#include <Qt3DCore/private/qservicelocator_p.h>
20#include <Qt3DCore/private/qsysteminformationservice_p.h>
21#include <Qt3DCore/qt3dcore-config.h>
22
23QT_BEGIN_NAMESPACE
24
25namespace{
26
27QList<Qt3DCore::QNode *> getNodesForCreation(Qt3DCore::QNode *root)
28{
29 using namespace Qt3DCore;
30
31 QList<QNode *> nodes;
32 QNodeVisitor visitor;
33 visitor.traverse(rootNode_: root, fN: [&nodes](QNode *node) {
34 nodes.append(t: node);
35
36 // Store the metaobject of the node in the QNode so that we have it available
37 // to us during destruction in the QNode destructor. This allows us to send
38 // the QNodeId and the metaobject as typeinfo to the backend aspects so they
39 // in turn can find the correct QBackendNodeMapper object to handle the destruction
40 // of the corresponding backend nodes.
41 QNodePrivate *d = QNodePrivate::get(q: node);
42 d->m_typeInfo = const_cast<QMetaObject*>(QNodePrivate::findStaticMetaObject(metaObject: node->metaObject()));
43
44 // Mark this node as having been handled for creation so that it is picked up
45 d->m_hasBackendNode = true;
46 });
47
48 return nodes;
49}
50
51QList<Qt3DCore::QNode *> getNodesForRemoval(Qt3DCore::QNode *root)
52{
53 using namespace Qt3DCore;
54
55 QList<QNode *> nodes;
56 QNodeVisitor visitor;
57 visitor.traverse(rootNode_: root, fN: [&nodes](QNode *node) {
58 nodes.append(t: node);
59
60 // Mark this node as having been handled for destruction so we don't
61 // repeat it unnecessarily in an O(n^2) manner
62 QNodePrivate::get(q: node)->m_hasBackendNode = false;
63 });
64
65 return nodes;
66}
67
68}
69
70namespace Qt3DCore {
71
72QAspectEnginePrivate *QAspectEnginePrivate::get(QAspectEngine *q)
73{
74 return q->d_func();
75}
76
77QAspectEnginePrivate::QAspectEnginePrivate()
78 : QObjectPrivate()
79 , m_aspectManager(nullptr)
80 , m_scene(nullptr)
81 , m_initialized(false)
82 , m_runMode(QAspectEngine::Automatic)
83{
84 qRegisterMetaType<Qt3DCore::QAbstractAspect *>();
85 qRegisterMetaType<Qt3DCore::QNode *>();
86 qRegisterMetaType<Qt3DCore::QEntity *>();
87 qRegisterMetaType<Qt3DCore::QScene *>();
88}
89
90QAspectEnginePrivate::~QAspectEnginePrivate()
91{
92 qDeleteAll(c: m_aspects);
93}
94
95/*!
96 * \internal
97 *
98 * Used to init the scene tree when the Qt3D aspect is first started. Basically,
99 * sets the scene/change arbiter on the items and stores the entity - component
100 * pairs in the scene
101 */
102void QAspectEnginePrivate::initNode(QNode *node)
103{
104 m_scene->addObservable(observable: node);
105 QNodePrivate::get(q: node)->setScene(m_scene);
106}
107
108void QAspectEnginePrivate::initEntity(QEntity *entity)
109{
110 const auto components = entity->components();
111 for (QComponent *comp : components) {
112 if (!m_scene->hasEntityForComponent(componentUuid: comp->id(), entityUuid: entity->id())) {
113 if (!comp->isShareable() && !m_scene->entitiesForComponent(id: comp->id()).isEmpty())
114 qWarning() << "Trying to assign a non shareable component to more than one Entity";
115 m_scene->addEntityForComponent(componentUuid: comp->id(), entityUuid: entity->id());
116 }
117 }
118}
119
120void QAspectEnginePrivate::addNode(QNode *node)
121{
122 m_aspectManager->addNodes(nodes: getNodesForCreation(root: node));
123}
124
125void QAspectEnginePrivate::removeNode(QNode *node)
126{
127 m_aspectManager->removeNodes(nodes: getNodesForRemoval(root: node));
128}
129
130/*!
131 * \class Qt3DCore::QAspectEngine
132 * \inheaderfile Qt3DCore/QAspectEngine
133 * \inherits QObject
134 * \inmodule Qt3DCore
135 *
136 * \brief Responsible for handling all the QAbstractAspect subclasses that have
137 * been registered with the scene.
138 *
139 * The Qt3D run loop is controlled by the Qt3DRender::QAspectEngine.
140 *
141 * Qt3DCore::QAbstractAspect subclasses can be registered by calling
142 * Qt3DCore::QAspectEngine::registerAspect() which will take care of registering
143 * the aspect and in turn that will call Qt3DCore::QAbstractAspect::onRegistered();
144 *
145 * The simulation loop is launched as soon as a root Qt3DCore::QEntity
146 * is set on the Qt3DCore::QAspectEngine. This is followed by a call to
147 * onEngineStartup() on each aspect so that they can start their simulation
148 * work.
149 *
150 * The simulation loop is stopped when the root entity is set to
151 * Qt3DCore::QEntityPtr(). This calls onEngineShutdown() on each aspect so
152 * that they can stop performing their simulation work.
153 *
154 * Setting a new valid root entity would restart the simulation loop again.
155 */
156
157/*!
158 * \internal
159 * This loop is executed in a separate thread called the AspectThread in
160 * Qt3DCore::QAspectThread. This thread is started when the
161 * Qt3DCore::QAspectEngine is created and provides the
162 * Qt3DCore::QAspectManager which lives in this thread for as long as it's
163 * running.
164 *
165 * Once the AspectThread is running, it starts the run loop and waits for
166 * aspects to be registered.
167 *
168 * Destroying the Qt3DCore::QAspectEngine instance stops the AspectThread and
169 * properly terminates the run loop.
170 */
171
172/*!
173 * \typedef Qt3DCore::QEntityPtr
174 * \relates Qt3DCore::QAspectEngine
175 *
176 * A shared pointer for QEntity.
177 */
178
179/*!
180 * Constructs a new QAspectEngine with \a parent.
181 */
182QAspectEngine::QAspectEngine(QObject *parent)
183 : QObject(*new QAspectEnginePrivate, parent)
184{
185 qCDebug(Aspects) << Q_FUNC_INFO;
186 Q_D(QAspectEngine);
187 d->m_scene = new QScene(this);
188 d->m_aspectManager = new QAspectManager(this);
189}
190
191/*!
192 * Destroys the engine.
193 */
194QAspectEngine::~QAspectEngine()
195{
196 Q_D(QAspectEngine);
197
198 // Shutdown the simulation loop by setting an empty scene
199 // Note: this sets an atomic which allows the AspectThread to break out of
200 // the inner simulation loop in the AspectThread::exec function
201 setRootEntity(QEntityPtr());
202
203 // Unregister all aspects and exit the main loop
204 const auto aspects = d->m_aspects;
205 for (auto aspect : aspects)
206 unregisterAspect(aspect);
207
208 delete d->m_scene;
209}
210
211void QAspectEnginePrivate::initNodeTree(QNode *node)
212{
213 // Set the root entity on the scene
214 m_scene->setRootNode(node);
215 QNodeVisitor visitor;
216 visitor.traverse(rootNode_: node, instance: this, fN: &QAspectEnginePrivate::initNode, fE: &QAspectEnginePrivate::initEntity);
217}
218
219void QAspectEnginePrivate::initialize()
220{
221 m_aspectManager->initialize();
222 QChangeArbiter *arbiter = m_aspectManager->changeArbiter();
223 m_scene->setArbiter(arbiter);
224 arbiter->setScene(m_scene);
225 m_initialized = true;
226 m_aspectManager->setPostConstructorInit(m_scene->postConstructorInit());
227}
228
229/*!
230 * \internal
231 *
232 * Called when we unset the root entity. Causes the QAspectManager's simulation
233 * loop to be exited. The main loop should keep processing events ready
234 * to start up the simulation again with a new root entity.
235 */
236void QAspectEnginePrivate::shutdown()
237{
238 qCDebug(Aspects) << Q_FUNC_INFO;
239
240 // Exit the simulation loop. Waits for this to be completed on the aspect
241 // thread before returning
242 exitSimulationLoop();
243
244 // Cleanup the scene before quitting the backend
245 m_scene->setArbiter(nullptr);
246 m_initialized = false;
247}
248
249void QAspectEnginePrivate::exitSimulationLoop()
250{
251 if (m_aspectManager != nullptr)
252 m_aspectManager->exitSimulationLoop();
253}
254
255QNode *QAspectEnginePrivate::lookupNode(QNodeId id) const
256{
257 return m_scene ? m_scene->lookupNode(id) : nullptr;
258}
259
260QList<QNode *> QAspectEnginePrivate::lookupNodes(const QList<QNodeId> &ids) const
261{
262 return m_scene ? m_scene->lookupNodes(ids) : QList<QNode *>{};
263}
264
265/*!
266 Registers a new \a aspect to the AspectManager. The QAspectEngine takes
267 ownership of the aspect and will delete it when the aspect is unregistered.
268 //! Called in the main thread
269 */
270void QAspectEngine::registerAspect(QAbstractAspect *aspect)
271{
272 Q_D(QAspectEngine);
273
274 const QStringList dependencies = aspect->dependencies();
275 for (const auto &name: dependencies) {
276 if (!d->m_namedAspects.contains(key: name))
277 registerAspect(name);
278 }
279
280 d->m_aspects << aspect;
281 d->m_aspectManager->registerAspect(aspect);
282}
283
284/*!
285 * Registers a new aspect to the AspectManager based on its \a name
286 * Uses the currently set aspect factory to create the actual aspect
287 * instance.
288 */
289void QAspectEngine::registerAspect(const QString &name)
290{
291 Q_D(QAspectEngine);
292 QAbstractAspect *aspect = d->m_factory.createAspect(aspect: QLatin1String(name.toUtf8()));
293 if (aspect) {
294 registerAspect(aspect);
295 d->m_namedAspects.insert(key: name, value: aspect);
296 }
297}
298
299/*!
300 * Unregisters and deletes the given \a aspect.
301 */
302void QAspectEngine::unregisterAspect(QAbstractAspect *aspect)
303{
304 Q_D(QAspectEngine);
305 if (!d->m_aspects.contains(t: aspect)) {
306 qWarning() << "Attempting to unregister an aspect that is not registered";
307 return;
308 }
309
310 // Cleanly shutdown this aspect by setting its root entity to null which
311 // will cause its onEngineShutdown() virtual to be called to allow it to
312 // cleanup any resources. Then remove it from the QAspectManager's list
313 // of aspects.
314 // TODO: Implement this once we are able to cleanly shutdown
315
316 // Tell the aspect manager to give the aspect a chance to do some cleanup
317 // in its QAbstractAspect::onUnregistered() virtual
318 d->m_aspectManager->unregisterAspect(aspect);
319
320 // Remove from our collection of named aspects (if present)
321 const auto it = std::find_if(first: d->m_namedAspects.begin(), last: d->m_namedAspects.end(),
322 pred: [aspect](QAbstractAspect *v) { return v == aspect; });
323 if (it != d->m_namedAspects.end())
324 d->m_namedAspects.erase(it);
325
326 // Finally, scheduly the aspect for deletion. Do this via the event loop
327 // in case we are unregistering the aspect in response to a signal from it.
328 aspect->deleteLater();
329 d->m_aspects.removeOne(t: aspect);
330}
331
332/*!
333 * Unregisters and deletes the aspect with the given \a name.
334 */
335void QAspectEngine::unregisterAspect(const QString &name)
336{
337 Q_D(QAspectEngine);
338 if (!d->m_namedAspects.contains(key: name)) {
339 qWarning() << "Attempting to unregister an aspect that is not registered";
340 return;
341 }
342
343 // Delegate unregistering and removal to the overload
344 QAbstractAspect *aspect = d->m_namedAspects.value(key: name);
345 unregisterAspect(aspect);
346}
347
348/*!
349 * \return the aspects owned by the aspect engine.
350 */
351QList<QAbstractAspect *> QAspectEngine::aspects() const
352{
353 Q_D(const QAspectEngine);
354 return d->m_aspects;
355}
356
357/*!
358 * \return the asepect matching the \a name
359 *
360 * \note Required that the aspect was registered by name
361 */
362QAbstractAspect *QAspectEngine::aspect(const QString &name) const
363{
364 Q_D(const QAspectEngine);
365 return d->m_namedAspects.value(key: name, defaultValue: nullptr);
366}
367
368/*!
369 * Executes the given \a command on aspect engine. Valid commands are:
370 * \list
371 * \li "list aspects"
372 * \endlist
373 *
374 * \return the reply for the command.
375 */
376QVariant QAspectEngine::executeCommand(const QString &command)
377{
378 Q_D(QAspectEngine);
379
380 if (command == QLatin1String("list aspects")) {
381 if (d->m_aspects.isEmpty())
382 return QLatin1String("No loaded aspect");
383
384 const QStringList names = d->m_aspectManager->serviceLocator()->systemInformation()->aspectNames();
385 return names.join(sep: QLatin1String("\n"));
386 }
387 if (command == QLatin1String("dump jobs")) {
388 d->m_aspectManager->dumpJobsOnNextFrame();
389 return QLatin1String("Dump in next frame in working directory");
390 }
391
392 QStringList args = command.split(sep: QLatin1Char(' '));
393 QString aspectName = args.takeFirst();
394
395 for (QAbstractAspect *aspect : std::as_const(t&: d->m_aspects)) {
396 if (aspectName == d->m_factory.aspectName(aspect))
397 return aspect->executeCommand(args);
398 }
399
400 return QVariant();
401}
402
403/*!
404 * If using the manual run mode, this function executes the jobs for each aspect.
405 * It is blocking and won't return until all jobs have been completed.
406 *
407 * If you are using the QRenderAspect,
408 */
409void QAspectEngine::processFrame()
410{
411 Q_D(QAspectEngine);
412 Q_ASSERT(d->m_runMode == QAspectEngine::Manual);
413 d->m_aspectManager->processFrame();
414}
415
416QNode *QAspectEngine::lookupNode(QNodeId id) const
417{
418 Q_D(const QAspectEngine);
419 return d->lookupNode(id);
420}
421
422QList<QNode *> QAspectEngine::lookupNodes(const QList<QNodeId> &ids) const
423{
424 Q_D(const QAspectEngine);
425 return d->lookupNodes(ids);
426}
427
428/*!
429 * Sets the \a root entity for the aspect engine.
430 */
431void QAspectEngine::setRootEntity(QEntityPtr root)
432{
433 qCDebug(Aspects) << Q_FUNC_INFO << "root =" << root;
434 Q_D(QAspectEngine);
435 if (d->m_root == root)
436 return;
437
438 const bool shutdownNeeded = d->m_root && d->m_initialized;
439
440 // Set the new root object. This will cause the old tree to be deleted
441 // and the deletion of the old frontend tree will cause the backends to
442 // free any related resources
443 d->m_root = root;
444
445 if (shutdownNeeded)
446 d->shutdown();
447
448 // Do we actually have a new scene?
449 if (!d->m_root)
450 return;
451
452 // Set postman/scene/arbiter ...
453 d->initialize();
454
455 // The aspect engine takes ownership of the scene root. We also set the
456 // parent of the scene root to be the engine
457 static_cast<QObject *>(d->m_root.data())->setParent(this);
458
459 // Prepare the frontend tree for use by giving each node a pointer to the
460 // scene object and adding each node to the scene
461 // TODO: We probably need a call symmetric to this one above in order to
462 // deregister the nodes from the scene
463 d->initNodeTree(node: root.data());
464
465 const QList<QNode *> nodes = getNodesForCreation(root: root.data());
466
467 // Specify if the AspectManager should be driving the simulation loop or not
468 d->m_aspectManager->setRunMode(d->m_runMode);
469
470 // Finally, tell the aspects about the new scene object tree. This is done
471 // in a blocking manner to allow the aspects to get synchronized before the
472 // main thread starts triggering potentially more notifications
473
474 // TODO: Pass the creation changes via the arbiter rather than relying upon
475 // an invokeMethod call.
476 qCDebug(Aspects) << "Begin setting scene root on aspect manager";
477 d->m_aspectManager->setRootEntity(root: root.data(), nodes);
478 qCDebug(Aspects) << "Done setting scene root on aspect manager";
479 d->m_aspectManager->enterSimulationLoop();
480}
481
482/*!
483 * \return the root entity of the aspect engine.
484 */
485QEntityPtr QAspectEngine::rootEntity() const
486{
487 Q_D(const QAspectEngine);
488 return d->m_root;
489}
490
491void QAspectEngine::setRunMode(QAspectEngine::RunMode mode)
492{
493 Q_D(QAspectEngine);
494 d->m_runMode = mode;
495 if (d->m_aspectManager)
496 d->m_aspectManager->setRunMode(mode);
497}
498
499QAspectEngine::RunMode QAspectEngine::runMode() const
500{
501 Q_D(const QAspectEngine);
502 return d->m_runMode;
503}
504
505} // namespace Qt3DCore
506
507QT_END_NAMESPACE
508
509#include "moc_qaspectengine.cpp"
510

source code of qt3d/src/core/aspects/qaspectengine.cpp