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 | |
61 | QT_BEGIN_NAMESPACE |
62 | |
63 | namespace{ |
64 | |
65 | QVector<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 | |
89 | QVector<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 | |
108 | namespace Qt3DCore { |
109 | |
110 | QAspectEnginePrivate *QAspectEnginePrivate::get(QAspectEngine *q) |
111 | { |
112 | return q->d_func(); |
113 | } |
114 | |
115 | QAspectEnginePrivate::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 | |
131 | QAspectEnginePrivate::~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 | */ |
143 | void QAspectEnginePrivate::initNode(QNode *node) |
144 | { |
145 | m_scene->addObservable(observable: node); |
146 | QNodePrivate::get(q: node)->setScene(m_scene); |
147 | } |
148 | |
149 | void 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 | |
161 | void QAspectEnginePrivate::addNode(QNode *node) |
162 | { |
163 | m_aspectManager->addNodes(nodes: getNodesForCreation(root: node)); |
164 | } |
165 | |
166 | void 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 | */ |
223 | QAspectEngine::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 | */ |
237 | QAspectEngine::~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 | |
255 | void 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 | |
263 | void 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 | */ |
282 | void 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 | |
302 | void 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 | */ |
313 | void 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 | */ |
329 | void 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 | */ |
342 | void 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 | */ |
375 | void 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 | */ |
391 | QVector<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 | */ |
405 | QVariant 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 | */ |
438 | void 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 | */ |
448 | void 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 | */ |
502 | QEntityPtr QAspectEngine::rootEntity() const |
503 | { |
504 | Q_D(const QAspectEngine); |
505 | return d->m_root; |
506 | } |
507 | |
508 | void 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 | |
516 | QAspectEngine::RunMode QAspectEngine::runMode() const |
517 | { |
518 | Q_D(const QAspectEngine); |
519 | return d->m_runMode; |
520 | } |
521 | |
522 | } // namespace Qt3DCore |
523 | |
524 | QT_END_NAMESPACE |
525 | |