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

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