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/qaspectthread_p.h>
51#include <Qt3DCore/private/qaspectmanager_p.h>
52#include <Qt3DCore/private/qchangearbiter_p.h>
53#include <Qt3DCore/private/qeventfilterservice_p.h>
54#include <Qt3DCore/private/qnode_p.h>
55#include <Qt3DCore/private/qnodevisitor_p.h>
56#include <Qt3DCore/private/qnodecreatedchangegenerator_p.h>
57#include <Qt3DCore/private/qpostman_p.h>
58#include <Qt3DCore/private/qscene_p.h>
59#include <Qt3DCore/private/qservicelocator_p.h>
60#include <Qt3DCore/qt3dcore-config.h>
61
62#if QT_CONFIG(qt3d_profile_jobs)
63#include <Qt3DCore/private/aspectcommanddebugger_p.h>
64#endif
65
66QT_BEGIN_NAMESPACE
67
68namespace Qt3DCore {
69
70QAspectEnginePrivate *QAspectEnginePrivate::get(QAspectEngine *q)
71{
72 return q->d_func();
73}
74
75QAspectEnginePrivate::QAspectEnginePrivate()
76 : QObjectPrivate()
77 , m_postman(nullptr)
78 , m_scene(nullptr)
79 , m_initialized(false)
80 #if QT_CONFIG(qt3d_profile_jobs)
81 , m_commandDebugger(new Debug::AspectCommandDebugger(q_func()))
82 #endif
83{
84 qRegisterMetaType<Qt3DCore::QAbstractAspect *>();
85 qRegisterMetaType<Qt3DCore::QObserverInterface *>();
86 qRegisterMetaType<Qt3DCore::QNode *>();
87 qRegisterMetaType<Qt3DCore::QEntity *>();
88 qRegisterMetaType<Qt3DCore::QScene *>();
89 qRegisterMetaType<Qt3DCore::QAbstractPostman *>();
90}
91
92QAspectEnginePrivate::~QAspectEnginePrivate()
93{
94 qDeleteAll(m_aspects);
95}
96
97/*!
98 * \internal
99 *
100 * Used to init the scene tree when the Qt3D aspect is first started. Basically,
101 * sets the scene/change arbiter on the items and stores the entity - component
102 * pairs in the scene
103 */
104void QAspectEnginePrivate::initNode(QNode *node)
105{
106 m_scene->addObservable(node);
107 QNodePrivate::get(node)->setScene(m_scene);
108}
109
110void QAspectEnginePrivate::initEntity(QEntity *entity)
111{
112 const auto components = entity->components();
113 for (QComponent *comp : components) {
114 if (!m_scene->hasEntityForComponent(comp->id(), entity->id())) {
115 if (!comp->isShareable() && !m_scene->entitiesForComponent(comp->id()).isEmpty())
116 qWarning() << "Trying to assign a non shareable component to more than one Entity";
117 m_scene->addEntityForComponent(comp->id(), entity->id());
118 }
119 }
120}
121
122void QAspectEnginePrivate::generateCreationChanges(QNode *root)
123{
124 const QNodeCreatedChangeGenerator generator(root);
125 m_creationChanges = generator.creationChanges();
126}
127
128/*!
129 * \class Qt3DCore::QAspectEngine
130 * \inheaderfile Qt3DCore/QAspectEngine
131 * \inherits QObject
132 * \inmodule Qt3DCore
133 *
134 * \brief Responsible for handling all the QAbstractAspect subclasses that have
135 * been registered with the scene.
136 *
137 * The Qt3D run loop is controlled by the Qt3DRender::QAspectEngine.
138 *
139 * Qt3DCore::QAbstractAspect subclasses can be registered by calling
140 * Qt3DCore::QAspectEngine::registerAspect() which will take care of registering
141 * the aspect and in turn that will call Qt3DCore::QAbstractAspect::onRegistered();
142 *
143 * The simulation loop is launched as soon as a root Qt3DCore::QEntity
144 * is set on the Qt3DCore::QAspectEngine. This is followed by a call to
145 * onEngineStartup() on each aspect so that they can start their simulation
146 * work.
147 *
148 * The simulation loop is stopped when the root entity is set to
149 * Qt3DCore::QEntityPtr(). This calls onEngineShutdown() on each aspect so
150 * that they can stop performing their simulation work.
151 *
152 * Setting a new valid root entity would restart the simulation loop again.
153 */
154
155/*!
156 * \internal
157 * This loop is executed in a separate thread called the AspectThread in
158 * Qt3DCore::QAspectThread. This thread is started when the
159 * Qt3DCore::QAspectEngine is created and provides the
160 * Qt3DCore::QAspectManager which lives in this thread for as long as it's
161 * running.
162 *
163 * Once the AspectThread is running, it starts the run loop and waits for
164 * aspects to be registered.
165 *
166 * Destroying the Qt3DCore::QAspectEngine instance stops the AspectThread and
167 * properly terminates the run loop.
168 */
169
170/*!
171 * \typedef Qt3DCore::QEntityPtr
172 * \relates Qt3DCore::QAspectEngine
173 *
174 * A shared pointer for QEntity.
175 */
176
177/*!
178 * Constructs a new QAspectEngine with \a parent.
179 */
180QAspectEngine::QAspectEngine(QObject *parent)
181 : QObject(*new QAspectEnginePrivate, parent)
182{
183 qCDebug(Aspects) << Q_FUNC_INFO;
184 Q_D(QAspectEngine);
185 d->m_scene = new QScene(this);
186 d->m_postman = new QPostman(this);
187 d->m_postman->setScene(d->m_scene);
188 d->m_aspectThread = new QAspectThread(this);
189 d->m_aspectThread->waitForStart(QThread::HighestPriority);
190}
191
192/*!
193 * Destroys the engine.
194 */
195QAspectEngine::~QAspectEngine()
196{
197 Q_D(QAspectEngine);
198
199 // Shutdown the simulation loop by setting an empty scene
200 // Note: this sets an atomic which allows the AspectThread to break out of
201 // the inner simulation loop in the AspectThread::exec function
202 setRootEntity(QEntityPtr());
203
204 // Unregister all aspects and exit the main loop
205 const auto aspects = d->m_aspects;
206 for (auto aspect : aspects)
207 unregisterAspect(aspect);
208
209 // Wait for thread to have completed it's final loop of execution
210 d->m_aspectThread->aspectManager()->quit();
211 d->m_aspectThread->wait();
212
213 delete d->m_aspectThread;
214 delete d->m_postman;
215 delete d->m_scene;
216}
217
218void QAspectEnginePrivate::initNodeTree(QNode *node)
219{
220 // Set the root entity on the scene
221 m_scene->setRootNode(node);
222 QNodeVisitor visitor;
223 visitor.traverse(node, this, &QAspectEnginePrivate::initNode, &QAspectEnginePrivate::initEntity);
224}
225
226void QAspectEnginePrivate::initialize()
227{
228 QChangeArbiter *arbiter = m_aspectThread->aspectManager()->changeArbiter();
229 m_scene->setArbiter(arbiter);
230 QChangeArbiter::createUnmanagedThreadLocalChangeQueue(arbiter);
231 QMetaObject::invokeMethod(arbiter,
232 "setPostman",
233 Q_ARG(Qt3DCore::QAbstractPostman*, m_postman));
234 QMetaObject::invokeMethod(arbiter,
235 "setScene",
236 Q_ARG(Qt3DCore::QScene*, m_scene));
237 m_initialized = true;
238#if QT_CONFIG(qt3d_profile_jobs)
239 m_commandDebugger->setAspectEngine(q_func());
240 m_commandDebugger->initialize();
241#endif
242}
243
244/*!
245 * \internal
246 *
247 * Called when we unset the root entity. Causes the QAspectManager's simulation
248 * loop to be exited. The main loop should keep processing events ready
249 * to start up the simulation again with a new root entity.
250 */
251void QAspectEnginePrivate::shutdown()
252{
253 qCDebug(Aspects) << Q_FUNC_INFO;
254
255 // Flush any change batch waiting in the postman that may contain node
256 // destruction changes that the aspects should process before we exit
257 // the simulation loop
258 m_postman->submitChangeBatch();
259
260 // Exit the simulation loop. Waits for this to be completed on the aspect
261 // thread before returning
262 exitSimulationLoop();
263
264 // Cleanup the scene before quitting the backend
265 m_scene->setArbiter(nullptr);
266 QChangeArbiter *arbiter = m_aspectThread->aspectManager()->changeArbiter();
267 QChangeArbiter::destroyUnmanagedThreadLocalChangeQueue(arbiter);
268 m_initialized = false;
269}
270
271void QAspectEnginePrivate::exitSimulationLoop()
272{
273 m_aspectThread->aspectManager()->exitSimulationLoop();
274}
275
276/*!
277 Registers a new \a aspect to the AspectManager. The QAspectEngine takes
278 ownership of the aspect and will delete it when the aspect is unregistered.
279 //! Called in the main thread
280 */
281void QAspectEngine::registerAspect(QAbstractAspect *aspect)
282{
283 Q_D(QAspectEngine);
284 // The aspect is moved to the AspectThread
285 // AspectManager::registerAspect is called in the context
286 // of the AspectThread. This is turns call aspect->onInitialize
287 // still in the same AspectThread context
288 aspect->moveToThread(d->m_aspectThread);
289 d->m_aspects << aspect;
290 QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(),
291 "registerAspect",
292 Qt::BlockingQueuedConnection,
293 Q_ARG(Qt3DCore::QAbstractAspect*, aspect));
294}
295
296/*!
297 * Registers a new aspect to the AspectManager based on its \a name
298 * Uses the currently set aspect factory to create the actual aspect
299 * instance.
300 */
301void QAspectEngine::registerAspect(const QString &name)
302{
303 Q_D(QAspectEngine);
304 QAbstractAspect *aspect = d->m_factory.createAspect(QLatin1String(name.toUtf8()));
305 if (aspect) {
306 registerAspect(aspect);
307 d->m_namedAspects.insert(name, aspect);
308 }
309}
310
311/*!
312 * Unregisters and deletes the given \a aspect.
313 */
314void QAspectEngine::unregisterAspect(QAbstractAspect *aspect)
315{
316 Q_D(QAspectEngine);
317 if (!d->m_aspects.contains(aspect)) {
318 qWarning() << "Attempting to unregister an aspect that is not registered";
319 return;
320 }
321
322 // Cleanly shutdown this aspect by setting its root entity to null which
323 // will cause its onEngineShutdown() virtual to be called to allow it to
324 // cleanup any resources. Then remove it from the QAspectManager's list
325 // of aspects.
326 // TODO: Implement this once we are able to cleanly shutdown
327
328 // Tell the aspect manager to give the aspect a chance to do some cleanup
329 // in its QAbstractAspect::onUnregistered() virtual
330 QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(),
331 "unregisterAspect",
332 Qt::BlockingQueuedConnection,
333 Q_ARG(Qt3DCore::QAbstractAspect*, aspect));
334
335 // Remove from our collection of named aspects (if present)
336 const auto it = std::find_if(d->m_namedAspects.begin(), d->m_namedAspects.end(),
337 [aspect](QAbstractAspect *v) { return v == aspect; });
338 if (it != d->m_namedAspects.end())
339 d->m_namedAspects.erase(it);
340
341 // Finally, scheduly the aspect for deletion. Do this via the event loop
342 // in case we are unregistering the aspect in response to a signal from it.
343 aspect->deleteLater();
344 d->m_aspects.removeOne(aspect);
345}
346
347/*!
348 * Unregisters and deletes the aspect with the given \a name.
349 */
350void QAspectEngine::unregisterAspect(const QString &name)
351{
352 Q_D(QAspectEngine);
353 if (!d->m_namedAspects.contains(name)) {
354 qWarning() << "Attempting to unregister an aspect that is not registered";
355 return;
356 }
357
358 // Delegate unregistering and removal to the overload
359 QAbstractAspect *aspect = d->m_namedAspects.value(name);
360 unregisterAspect(aspect);
361}
362
363/*!
364 * \return the aspects owned by the aspect engine.
365 */
366QVector<QAbstractAspect *> QAspectEngine::aspects() const
367{
368 Q_D(const QAspectEngine);
369 return d->m_aspects;
370}
371
372/*!
373 * Executes the given \a command on aspect engine. Valid commands are:
374 * \list
375 * \li "list aspects"
376 * \endlist
377 *
378 * \return the reply for the command.
379 */
380QVariant QAspectEngine::executeCommand(const QString &command)
381{
382 Q_D(QAspectEngine);
383
384 if (command == QLatin1String("list aspects")) {
385 if (d->m_aspects.isEmpty())
386 return QLatin1String("No loaded aspect");
387
388 QString reply;
389 reply += QLatin1String("Loaded aspects:");
390 for (QAbstractAspect *aspect : qAsConst(d->m_aspects)) {
391 const QString name = d->m_factory.aspectName(aspect);
392 if (!name.isEmpty())
393 reply += QLatin1String("\n * ") + name;
394 else
395 reply += QLatin1String("\n * <unnamed>");
396 }
397 return reply;
398 }
399
400 QStringList args = command.split(QLatin1Char(' '));
401 QString aspectName = args.takeFirst();
402
403 for (QAbstractAspect *aspect : qAsConst(d->m_aspects)) {
404 if (aspectName == d->m_factory.aspectName(aspect))
405 return aspect->executeCommand(args);
406 }
407
408 return QVariant();
409}
410
411/*!
412 * Sets the \a root entity for the aspect engine.
413 */
414void QAspectEngine::setRootEntity(QEntityPtr root)
415{
416 qCDebug(Aspects) << Q_FUNC_INFO << "root =" << root;
417 Q_D(QAspectEngine);
418 if (d->m_root == root)
419 return;
420
421 const bool shutdownNeeded = d->m_root && d->m_initialized;
422
423 // Set the new root object. This will cause the old tree to be deleted
424 // and the deletion of the old frontend tree will cause the backends to
425 // free any related resources
426 d->m_root = root;
427
428 if (shutdownNeeded)
429 d->shutdown();
430
431 // Do we actually have a new scene?
432 if (!d->m_root)
433 return;
434
435 // Set postman/scene/arbiter ...
436 d->initialize();
437
438 // The aspect engine takes ownership of the scene root. We also set the
439 // parent of the scene root to be the engine
440 static_cast<QObject *>(d->m_root.data())->setParent(this);
441
442 // Prepare the frontend tree for use by giving each node a pointer to the
443 // scene object and adding each node to the scene
444 // TODO: We probably need a call symmetric to this one above in order to
445 // deregister the nodes from the scene
446 d->initNodeTree(root.data());
447
448 // Traverse tree to generate a vector of creation changes
449 d->generateCreationChanges(root.data());
450
451 // Finally, tell the aspects about the new scene object tree. This is done
452 // in a blocking manner to allow the aspects to get synchronized before the
453 // main thread starts triggering potentially more notifications
454
455 // TODO: Pass the creation changes via the arbiter rather than relying upon
456 // an invokeMethod call.
457 qCDebug(Aspects) << "Begin setting scene root on aspect manager";
458 QMetaObject::invokeMethod(d->m_aspectThread->aspectManager(),
459 "setRootEntity",
460 Qt::BlockingQueuedConnection,
461 Q_ARG(Qt3DCore::QEntity*, root.data()),
462 Q_ARG(QVector<Qt3DCore::QNodeCreatedChangeBasePtr>, d->m_creationChanges));
463 qCDebug(Aspects) << "Done setting scene root on aspect manager";
464
465 d->m_aspectThread->aspectManager()->enterSimulationLoop();
466}
467
468/*!
469 * \return the root entity of the aspect engine.
470 */
471QEntityPtr QAspectEngine::rootEntity() const
472{
473 Q_D(const QAspectEngine);
474 return d->m_root;
475}
476
477} // namespace Qt3DCore
478
479QT_END_NAMESPACE
480