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 "qaspectmanager_p.h" |
41 | |
42 | #include <Qt3DCore/qabstractaspect.h> |
43 | #include <Qt3DCore/qentity.h> |
44 | #include <QtCore/QAbstractEventDispatcher> |
45 | #include <QtCore/QEventLoop> |
46 | #include <QtCore/QThread> |
47 | #include <QtCore/QWaitCondition> |
48 | #include <QtGui/QSurface> |
49 | |
50 | #include <Qt3DCore/private/corelogging_p.h> |
51 | #include <Qt3DCore/private/qabstractaspect_p.h> |
52 | #include <Qt3DCore/private/qabstractaspectjobmanager_p.h> |
53 | #include <Qt3DCore/private/qabstractframeadvanceservice_p.h> |
54 | // TODO Make the kind of job manager configurable (e.g. ThreadWeaver vs Intel TBB) |
55 | #include <Qt3DCore/private/qaspectjobmanager_p.h> |
56 | #include <Qt3DCore/private/qaspectjob_p.h> |
57 | #include <Qt3DCore/private/qchangearbiter_p.h> |
58 | #include <Qt3DCore/private/qscheduler_p.h> |
59 | #include <Qt3DCore/private/qservicelocator_p.h> |
60 | #include <Qt3DCore/private/qthreadpooler_p.h> |
61 | #include <Qt3DCore/private/qtickclock_p.h> |
62 | #include <Qt3DCore/private/qtickclockservice_p.h> |
63 | |
64 | #if defined(QT3D_CORE_JOB_TIMING) |
65 | #include <QElapsedTimer> |
66 | #endif |
67 | |
68 | QT_BEGIN_NAMESPACE |
69 | |
70 | namespace Qt3DCore { |
71 | |
72 | /*! |
73 | \class Qt3DCore::QAspectManager |
74 | \internal |
75 | */ |
76 | QAspectManager::QAspectManager(QObject *parent) |
77 | : QObject(parent) |
78 | , m_root(nullptr) |
79 | , m_scheduler(new QScheduler(this)) |
80 | , m_jobManager(new QAspectJobManager(this)) |
81 | , m_changeArbiter(new QChangeArbiter(this)) |
82 | , m_serviceLocator(new QServiceLocator()) |
83 | , m_waitForEndOfSimulationLoop(0) |
84 | , m_waitForStartOfSimulationLoop(0) |
85 | , m_waitForEndOfExecLoop(0) |
86 | , m_waitForQuit(0) |
87 | { |
88 | qRegisterMetaType<QSurface *>("QSurface*" ); |
89 | m_runSimulationLoop.fetchAndStoreOrdered(0); |
90 | m_runMainLoop.fetchAndStoreOrdered(1); |
91 | qCDebug(Aspects) << Q_FUNC_INFO; |
92 | } |
93 | |
94 | QAspectManager::~QAspectManager() |
95 | { |
96 | delete m_changeArbiter; |
97 | delete m_jobManager; |
98 | delete m_scheduler; |
99 | } |
100 | |
101 | // Main thread (called by QAspectEngine) |
102 | void QAspectManager::enterSimulationLoop() |
103 | { |
104 | qCDebug(Aspects) << Q_FUNC_INFO; |
105 | m_runSimulationLoop.fetchAndStoreOrdered(1); |
106 | |
107 | // Wake up QAspectThread's event loop |
108 | thread()->eventDispatcher()->wakeUp(); |
109 | |
110 | // We wait for the setRootEntity on the aspectManager to have completed |
111 | // This ensures we cannot shutdown before the aspects have had a chance |
112 | // to be initialized |
113 | m_waitForStartOfSimulationLoop.acquire(1); |
114 | } |
115 | |
116 | // Main thread (called by QAspectEngine) |
117 | void QAspectManager::exitSimulationLoop() |
118 | { |
119 | qCDebug(Aspects) << Q_FUNC_INFO; |
120 | |
121 | // If this fails, simulation loop is already exited so nothing to do |
122 | if (!m_runSimulationLoop.testAndSetOrdered(1, 0)) { |
123 | qCDebug(Aspects) << "Simulation loop was not running. Nothing to do" ; |
124 | return; |
125 | } |
126 | |
127 | QAbstractFrameAdvanceService *frameAdvanceService = |
128 | m_serviceLocator->service<QAbstractFrameAdvanceService>(QServiceLocator::FrameAdvanceService); |
129 | if (frameAdvanceService) |
130 | frameAdvanceService->stop(); |
131 | |
132 | // Give any aspects a chance to unqueue any asynchronous work they |
133 | // may have scheduled that would otherwise potentially deadlock or |
134 | // cause races. For example, the QLogicAspect queues up a vector of |
135 | // QNodeIds to be processed by a callback on the main thread. However, |
136 | // if we don't unqueue this work and release its semaphore, the logic |
137 | // aspect would cause a deadlock when trying to exit the inner loop. |
138 | // This is because we call this function from the main thread and the |
139 | // logic aspect is waiting for the main thread to execute the |
140 | // QLogicComponent::onFrameUpdate() callback. |
141 | for (QAbstractAspect *aspect : qAsConst(m_aspects)) |
142 | aspect->d_func()->onEngineAboutToShutdown(); |
143 | |
144 | |
145 | qCDebug(Aspects) << "exitSimulationLoop waiting for exec loop to terminate" ; |
146 | // Wait until the simulation loop is fully exited and the aspects are done |
147 | // processing any final changes and have had onEngineShutdown() called on them |
148 | m_waitForEndOfSimulationLoop.acquire(1); |
149 | qCDebug(Aspects) << "exitSimulationLoop completed" ; |
150 | } |
151 | |
152 | bool QAspectManager::isShuttingDown() const |
153 | { |
154 | return !m_runSimulationLoop.load(); |
155 | } |
156 | |
157 | /*! |
158 | \internal |
159 | |
160 | Called by the QAspectThread's run() method immediately after the manager |
161 | has been created |
162 | */ |
163 | void QAspectManager::initialize() |
164 | { |
165 | qCDebug(Aspects) << Q_FUNC_INFO; |
166 | m_jobManager->initialize(); |
167 | m_scheduler->setAspectManager(this); |
168 | m_changeArbiter->initialize(m_jobManager); |
169 | } |
170 | |
171 | /*! |
172 | \internal |
173 | |
174 | Called by the QAspectThread's run() method immediately after the manager's |
175 | exec() function has returned. |
176 | */ |
177 | void QAspectManager::shutdown() |
178 | { |
179 | qCDebug(Aspects) << Q_FUNC_INFO; |
180 | |
181 | for (QAbstractAspect *aspect : qAsConst(m_aspects)) |
182 | m_changeArbiter->unregisterSceneObserver(aspect->d_func()); |
183 | |
184 | // Aspects must be deleted in the Thread they were created in |
185 | } |
186 | |
187 | // QAspectThread:: queued invoked by QAspectEngine::setRootEntity |
188 | void QAspectManager::setRootEntity(Qt3DCore::QEntity *root, const QVector<Qt3DCore::QNodeCreatedChangeBasePtr> &changes) |
189 | { |
190 | qCDebug(Aspects) << Q_FUNC_INFO; |
191 | |
192 | if (root == m_root) |
193 | return; |
194 | |
195 | if (m_root) { |
196 | // TODO: Delete all backend nodes. This is to be symmetric with how |
197 | // we create them below in the call to setRootAndCreateNodes |
198 | } |
199 | |
200 | m_root = root; |
201 | |
202 | if (m_root) { |
203 | for (QAbstractAspect *aspect : qAsConst(m_aspects)) |
204 | aspect->d_func()->setRootAndCreateNodes(m_root, changes); |
205 | } |
206 | } |
207 | |
208 | /*! |
209 | * \internal |
210 | * |
211 | * Registers a new \a aspect. |
212 | */ |
213 | void QAspectManager::registerAspect(QAbstractAspect *aspect) |
214 | { |
215 | qCDebug(Aspects) << "Registering aspect" ; |
216 | |
217 | if (aspect != nullptr) { |
218 | m_aspects.append(aspect); |
219 | QAbstractAspectPrivate::get(aspect)->m_aspectManager = this; |
220 | QAbstractAspectPrivate::get(aspect)->m_jobManager = m_jobManager; |
221 | QAbstractAspectPrivate::get(aspect)->m_arbiter = m_changeArbiter; |
222 | // Register sceneObserver with the QChangeArbiter |
223 | m_changeArbiter->registerSceneObserver(aspect->d_func()); |
224 | |
225 | // Allow the aspect to do some work now that it is registered |
226 | aspect->onRegistered(); |
227 | } |
228 | else { |
229 | qCWarning(Aspects) << "Failed to register aspect" ; |
230 | } |
231 | qCDebug(Aspects) << "Completed registering aspect" ; |
232 | } |
233 | |
234 | /*! |
235 | * \internal |
236 | * |
237 | * Calls QAbstractAspect::onUnregistered(), unregisters the aspect from the |
238 | * change arbiter and unsets the arbiter, job manager and aspect manager. |
239 | * Operations are performed in the reverse order to registerAspect. |
240 | */ |
241 | void QAspectManager::unregisterAspect(Qt3DCore::QAbstractAspect *aspect) |
242 | { |
243 | qCDebug(Aspects) << "Unregistering aspect" ; |
244 | Q_ASSERT(aspect); |
245 | aspect->onUnregistered(); |
246 | m_changeArbiter->unregisterSceneObserver(aspect->d_func()); |
247 | QAbstractAspectPrivate::get(aspect)->m_arbiter = nullptr; |
248 | QAbstractAspectPrivate::get(aspect)->m_jobManager = nullptr; |
249 | QAbstractAspectPrivate::get(aspect)->m_aspectManager = nullptr; |
250 | m_aspects.removeOne(aspect); |
251 | qCDebug(Aspects) << "Completed unregistering aspect" ; |
252 | } |
253 | |
254 | void QAspectManager::exec() |
255 | { |
256 | // Gentlemen, start your engines |
257 | QEventLoop eventLoop; |
258 | |
259 | // Enter the engine loop |
260 | qCDebug(Aspects) << Q_FUNC_INFO << "***** Entering main loop *****" ; |
261 | while (m_runMainLoop.load()) { |
262 | // Process events until we're told to start the simulation loop |
263 | while (m_runMainLoop.load() && !m_runSimulationLoop.load()) |
264 | eventLoop.processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); |
265 | |
266 | if (!m_runSimulationLoop.load()) |
267 | break; |
268 | |
269 | // Retrieve the frame advance service. Defaults to timer based if there is no renderer. |
270 | QAbstractFrameAdvanceService *frameAdvanceService = |
271 | m_serviceLocator->service<QAbstractFrameAdvanceService>(QServiceLocator::FrameAdvanceService); |
272 | |
273 | // Start the frameAdvanceService |
274 | frameAdvanceService->start(); |
275 | |
276 | // We are about to enter the simulation loop. Give aspects a chance to do any last |
277 | // pieces of initialization |
278 | qCDebug(Aspects) << "Calling onEngineStartup() for each aspect" ; |
279 | for (QAbstractAspect *aspect : qAsConst(m_aspects)) { |
280 | qCDebug(Aspects) << "\t" << aspect->objectName(); |
281 | aspect->onEngineStartup(); |
282 | } |
283 | qCDebug(Aspects) << "Done calling onEngineStartup() for each aspect" ; |
284 | m_waitForStartOfSimulationLoop.release(1); |
285 | |
286 | // Only enter main simulation loop once the renderer and other aspects are initialized |
287 | while (m_runSimulationLoop.load()) { |
288 | qint64 t = frameAdvanceService->waitForNextFrame(); |
289 | |
290 | // Distribute accumulated changes. This includes changes sent from the frontend |
291 | // to the backend nodes. We call this before the call to m_scheduler->update() to ensure |
292 | // that any property changes do not set dirty flags in a data race with the renderer's |
293 | // submission thread which may be looking for dirty flags, acting upon them and then |
294 | // clearing the dirty flags. |
295 | // |
296 | // Doing this as the first call in the new frame ensures the lock free approach works |
297 | // without any such data race. |
298 | #if QT_CONFIG(qt3d_profile_jobs) |
299 | const quint32 arbiterId = 4096; |
300 | JobRunStats changeArbiterStats; |
301 | changeArbiterStats.jobId.typeAndInstance[0] = arbiterId; |
302 | changeArbiterStats.jobId.typeAndInstance[1] = 0; |
303 | changeArbiterStats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId()); |
304 | changeArbiterStats.startTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); |
305 | #endif |
306 | m_changeArbiter->syncChanges(); |
307 | #if QT_CONFIG(qt3d_profile_jobs) |
308 | changeArbiterStats.endTime = QThreadPooler::m_jobsStatTimer.nsecsElapsed(); |
309 | QThreadPooler::addJobLogStatsEntry(changeArbiterStats); |
310 | #endif |
311 | |
312 | // For each Aspect |
313 | // Ask them to launch set of jobs for the current frame |
314 | // Updates matrices, bounding volumes, render bins ... |
315 | #if defined(QT3D_CORE_JOB_TIMING) |
316 | QElapsedTimer timer; |
317 | timer.start(); |
318 | #endif |
319 | m_scheduler->scheduleAndWaitForFrameAspectJobs(t); |
320 | #if defined(QT3D_CORE_JOB_TIMING) |
321 | qDebug() << "Jobs took" << timer.nsecsElapsed() / 1.0e6; |
322 | #endif |
323 | |
324 | // Process any pending events |
325 | eventLoop.processEvents(); |
326 | } // End of simulation loop |
327 | |
328 | // Process any pending changes from the frontend before we shut the aspects down |
329 | m_changeArbiter->syncChanges(); |
330 | |
331 | // Give aspects a chance to perform any shutdown actions. This may include unqueuing |
332 | // any blocking work on the main thread that could potentially deadlock during shutdown. |
333 | qCDebug(Aspects) << "Calling onEngineShutdown() for each aspect" ; |
334 | for (QAbstractAspect *aspect : qAsConst(m_aspects)) { |
335 | qCDebug(Aspects) << "\t" << aspect->objectName(); |
336 | aspect->onEngineShutdown(); |
337 | } |
338 | qCDebug(Aspects) << "Done calling onEngineShutdown() for each aspect" ; |
339 | |
340 | // Wake up the main thread which is waiting for us inside of exitSimulationLoop() |
341 | m_waitForEndOfSimulationLoop.release(1); |
342 | } // End of main loop |
343 | qCDebug(Aspects) << Q_FUNC_INFO << "***** Exited main loop *****" ; |
344 | |
345 | m_waitForEndOfExecLoop.release(1); |
346 | m_waitForQuit.acquire(1); |
347 | } |
348 | |
349 | void QAspectManager::quit() |
350 | { |
351 | qCDebug(Aspects) << Q_FUNC_INFO; |
352 | |
353 | Q_ASSERT_X(m_runSimulationLoop.load() == 0, "QAspectManagr::quit()" , "Inner loop is still running" ); |
354 | m_runMainLoop.fetchAndStoreOrdered(0); |
355 | |
356 | // Wake up QAspectThread's event loop if needed |
357 | thread()->eventDispatcher()->wakeUp(); |
358 | |
359 | // We need to wait for the QAspectManager exec loop to terminate |
360 | m_waitForEndOfExecLoop.acquire(1); |
361 | m_waitForQuit.release(1); |
362 | |
363 | qCDebug(Aspects) << Q_FUNC_INFO << "Exiting" ; |
364 | } |
365 | |
366 | const QVector<QAbstractAspect *> &QAspectManager::aspects() const |
367 | { |
368 | return m_aspects; |
369 | } |
370 | |
371 | QAbstractAspectJobManager *QAspectManager::jobManager() const |
372 | { |
373 | return m_jobManager; |
374 | } |
375 | |
376 | QChangeArbiter *QAspectManager::changeArbiter() const |
377 | { |
378 | return m_changeArbiter; |
379 | } |
380 | |
381 | QServiceLocator *QAspectManager::serviceLocator() const |
382 | { |
383 | return m_serviceLocator.data(); |
384 | } |
385 | |
386 | } // namespace Qt3DCore |
387 | |
388 | QT_END_NAMESPACE |
389 | |
390 | |