1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquick3dscenemanager_p.h" |
5 | #include "qquick3dobject_p.h" |
6 | #include "qquick3dviewport_p.h" |
7 | #include "qquick3dmodel_p.h" |
8 | |
9 | #include <QtQuick/QQuickWindow> |
10 | |
11 | #include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h> |
12 | #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h> |
13 | #include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h> |
14 | |
15 | #include <QtQuick3DUtils/private/qssgassert_p.h> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | static constexpr char qtQQ3DWAPropName[] { "_qtquick3dWindowAttachment" }; |
20 | |
21 | QQuick3DSceneManager::QQuick3DSceneManager(QObject *parent) |
22 | : QObject(parent) |
23 | { |
24 | } |
25 | |
26 | // Should be deleted by QQuick3DWindowAttachment to ensure it's done |
27 | // on the render thread. |
28 | QQuick3DSceneManager::~QQuick3DSceneManager() |
29 | { |
30 | cleanupNodes(); |
31 | if (wattached) |
32 | wattached->unregisterSceneManager(manager&: *this); |
33 | } |
34 | |
35 | void QQuick3DSceneManager::setWindow(QQuickWindow *window) |
36 | { |
37 | if (window == m_window) |
38 | return; |
39 | |
40 | if (window != m_window) { |
41 | if (wattached) { |
42 | // Unregister from old windows attached object |
43 | wattached->unregisterSceneManager(manager&: *this); |
44 | wattached = nullptr; |
45 | } |
46 | m_window = window; |
47 | if (m_window) { |
48 | wattached = getOrSetWindowAttachment(window&: *m_window); |
49 | if (wattached) |
50 | wattached->registerSceneManager(manager&: *this); |
51 | } |
52 | |
53 | emit windowChanged(); |
54 | } |
55 | } |
56 | |
57 | QQuickWindow *QQuick3DSceneManager::window() |
58 | { |
59 | return m_window; |
60 | } |
61 | |
62 | void QQuick3DSceneManager::dirtyItem(QQuick3DObject *item) |
63 | { |
64 | Q_UNUSED(item); |
65 | emit needsUpdate(); |
66 | } |
67 | |
68 | void QQuick3DSceneManager::requestUpdate() |
69 | { |
70 | emit needsUpdate(); |
71 | } |
72 | |
73 | void QQuick3DSceneManager::cleanup(QSSGRenderGraphObject *item) |
74 | { |
75 | Q_ASSERT(!cleanupNodeList.contains(item)); |
76 | cleanupNodeList.append(t: item); |
77 | |
78 | if (auto front = m_nodeMap[item]) { |
79 | auto *po = QQuick3DObjectPrivate::get(item: front); |
80 | sharedResourceRemoved |= po->sharedResource; |
81 | po->spatialNode = nullptr; |
82 | } |
83 | |
84 | // The front-end object is no longer reachable (destroyed) so make sure we don't return it |
85 | // when doing a node look-up. |
86 | m_nodeMap[item] = nullptr; |
87 | } |
88 | |
89 | void QQuick3DSceneManager::polishItems() |
90 | { |
91 | |
92 | } |
93 | |
94 | void QQuick3DSceneManager::forcePolish() |
95 | { |
96 | |
97 | } |
98 | |
99 | void QQuick3DSceneManager::sync() |
100 | { |
101 | |
102 | } |
103 | |
104 | void QQuick3DSceneManager::updateBoundingBoxes(QSSGBufferManager &mgr) |
105 | { |
106 | const QList<QQuick3DObject *> dirtyList = dirtyBoundingBoxList; |
107 | for (auto object : dirtyList) { |
108 | QQuick3DObjectPrivate *itemPriv = QQuick3DObjectPrivate::get(item: object); |
109 | if (itemPriv->sceneManager == nullptr) |
110 | continue; |
111 | auto model = static_cast<QSSGRenderModel *>(itemPriv->spatialNode); |
112 | if (model) { |
113 | QSSGBounds3 bounds = mgr.getModelBounds(model); |
114 | static_cast<QQuick3DModel *>(object)->setBounds(min: bounds.minimum, max: bounds.maximum); |
115 | } |
116 | dirtyBoundingBoxList.removeOne(t: object); |
117 | } |
118 | } |
119 | |
120 | bool QQuick3DSceneManager::updateDirtyResourceNodes() |
121 | { |
122 | auto it = std::begin(arr&: dirtyResources); |
123 | const auto end = std::end(arr&: dirtyResources); |
124 | bool ret = false; |
125 | for (; it != end; ++it) |
126 | ret |= updateResources(listHead: it); |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | void QQuick3DSceneManager::updateDirtySpatialNodes() |
132 | { |
133 | auto it = std::begin(arr&: dirtyNodes); |
134 | const auto end = std::end(arr&: dirtyNodes); |
135 | for (; it != end; ++it) |
136 | updateNodes(listHead: it); |
137 | } |
138 | |
139 | void QQuick3DSceneManager::updateDiryExtensions() |
140 | { |
141 | auto it = std::begin(arr&: dirtyExtensions); |
142 | const auto end = std::end(arr&: dirtyExtensions); |
143 | for (; it != end; ++it) |
144 | updateExtensions(listHead: it); |
145 | } |
146 | |
147 | void QQuick3DSceneManager::updateDirtyResource(QQuick3DObject *resourceObject) |
148 | { |
149 | QQuick3DObjectPrivate *itemPriv = QQuick3DObjectPrivate::get(item: resourceObject); |
150 | quint32 dirty = itemPriv->dirtyAttributes; |
151 | Q_UNUSED(dirty); |
152 | itemPriv->dirtyAttributes = 0; |
153 | |
154 | // Check if an Image2D has either acquired or lost its SourceItem. |
155 | // This is used to update inputHandlingEnabled counter that View3D uses to |
156 | // implicitly enable or disable internal input processing. |
157 | |
158 | bool beforeSourceItemValid = false; |
159 | if (itemPriv->spatialNode && itemPriv->spatialNode->type == QSSGRenderGraphObject::Type::Image2D) { |
160 | auto image = static_cast<QSSGRenderImage *>(itemPriv->spatialNode); |
161 | beforeSourceItemValid = image && image->m_qsgTexture != nullptr; |
162 | } |
163 | |
164 | itemPriv->spatialNode = resourceObject->updateSpatialNode(node: itemPriv->spatialNode); |
165 | if (itemPriv->spatialNode) { |
166 | m_nodeMap.insert(key: itemPriv->spatialNode, value: resourceObject); |
167 | if (itemPriv->spatialNode->type == QSSGRenderGraphObject::Type::ResourceLoader) { |
168 | resourceLoaders.insert(value: itemPriv->spatialNode); |
169 | } else if (itemPriv->spatialNode->type == QSSGRenderGraphObject::Type::Image2D) { |
170 | auto image = static_cast<QSSGRenderImage *>(itemPriv->spatialNode); |
171 | bool afterSouceItemValid = image && image->m_qsgTexture != nullptr; |
172 | if (beforeSourceItemValid != afterSouceItemValid) |
173 | inputHandlingEnabled += (afterSouceItemValid) ? 1 : -1; |
174 | } |
175 | } |
176 | |
177 | // resource nodes dont go in the tree, so we dont need to parent them |
178 | } |
179 | |
180 | void QQuick3DSceneManager::updateDirtySpatialNode(QQuick3DNode *spatialNode) |
181 | { |
182 | QQuick3DObjectPrivate *itemPriv = QQuick3DObjectPrivate::get(item: spatialNode); |
183 | quint32 dirty = itemPriv->dirtyAttributes; |
184 | itemPriv->dirtyAttributes = 0; |
185 | QSSGRenderGraphObject *oldNode = itemPriv->spatialNode; |
186 | itemPriv->spatialNode = spatialNode->updateSpatialNode(node: oldNode); |
187 | // NOTE: We always update the node map, as we can end-up with the a node map where the mapping |
188 | // has been 'disconnected', e.g., the front-end object removed from the scene only to be later |
189 | // re-used. |
190 | if (itemPriv->spatialNode) { |
191 | m_nodeMap.insert(key: itemPriv->spatialNode, value: spatialNode); |
192 | if (itemPriv->type == QQuick3DObjectPrivate::Type::Item2D && itemPriv->spatialNode != oldNode) |
193 | ++inputHandlingEnabled; |
194 | } |
195 | |
196 | QSSGRenderNode *graphNode = static_cast<QSSGRenderNode *>(itemPriv->spatialNode); |
197 | |
198 | if (graphNode && graphNode->parent && dirty & QQuick3DObjectPrivate::ParentChanged) { |
199 | QQuick3DNode *nodeParent = qobject_cast<QQuick3DNode *>(object: spatialNode->parentItem()); |
200 | if (nodeParent) { |
201 | QSSGRenderNode *parentGraphNode = static_cast<QSSGRenderNode *>( |
202 | QQuick3DObjectPrivate::get(item: nodeParent)->spatialNode); |
203 | if (parentGraphNode) { |
204 | graphNode->parent->removeChild(inChild&: *graphNode); |
205 | parentGraphNode->addChild(inChild&: *graphNode); |
206 | } |
207 | } |
208 | } |
209 | |
210 | if (graphNode && graphNode->parent == nullptr) { |
211 | QQuick3DNode *nodeParent = qobject_cast<QQuick3DNode *>(object: spatialNode->parentItem()); |
212 | if (nodeParent) { |
213 | QSSGRenderNode *parentGraphNode = static_cast<QSSGRenderNode *>(QQuick3DObjectPrivate::get(item: nodeParent)->spatialNode); |
214 | if (!parentGraphNode) { |
215 | // The parent spatial node hasn't been created yet |
216 | auto parentNode = QQuick3DObjectPrivate::get(item: nodeParent); |
217 | parentNode->spatialNode = nodeParent->updateSpatialNode(node: parentNode->spatialNode); |
218 | if (parentNode->spatialNode) |
219 | m_nodeMap.insert(key: parentNode->spatialNode, value: nodeParent); |
220 | parentGraphNode = static_cast<QSSGRenderNode *>(parentNode->spatialNode); |
221 | } |
222 | if (parentGraphNode) |
223 | parentGraphNode->addChild(inChild&: *graphNode); |
224 | } else { |
225 | QQuick3DViewport *viewParent = qobject_cast<QQuick3DViewport *>(object: spatialNode->parent()); |
226 | if (viewParent) { |
227 | auto sceneRoot = QQuick3DObjectPrivate::get(item: viewParent->scene()); |
228 | if (!sceneRoot->spatialNode) // must have a scene root spatial node first |
229 | sceneRoot->spatialNode = viewParent->scene()->updateSpatialNode(node: sceneRoot->spatialNode); |
230 | if (sceneRoot->spatialNode) { |
231 | m_nodeMap.insert(key: sceneRoot->spatialNode, value: viewParent->scene()); |
232 | static_cast<QSSGRenderNode *>(sceneRoot->spatialNode)->addChild(inChild&: *graphNode); |
233 | } |
234 | } |
235 | } |
236 | } |
237 | } |
238 | |
239 | QQuick3DObject *QQuick3DSceneManager::lookUpNode(const QSSGRenderGraphObject *node) const |
240 | { |
241 | return m_nodeMap[node]; |
242 | } |
243 | |
244 | QQuick3DWindowAttachment *QQuick3DSceneManager::getOrSetWindowAttachment(QQuickWindow &window) |
245 | { |
246 | |
247 | QQuick3DWindowAttachment *wa = nullptr; |
248 | if (auto aProperty = window.property(name: qtQQ3DWAPropName); aProperty.isValid()) |
249 | wa = aProperty.value<QQuick3DWindowAttachment *>(); |
250 | |
251 | if (!wa) { |
252 | // WindowAttachment will not be created under 'window'. |
253 | // It should be deleted after all the cleanups related with 'window', |
254 | // otherwise some resourses deleted after it, will not be cleaned correctly. |
255 | wa = new QQuick3DWindowAttachment(&window); |
256 | window.setProperty(name: qtQQ3DWAPropName, value: QVariant::fromValue(value: wa)); |
257 | } |
258 | |
259 | return wa; |
260 | } |
261 | |
262 | bool QQuick3DSceneManager::cleanupNodes() |
263 | { |
264 | bool ret = sharedResourceRemoved; |
265 | sharedResourceRemoved = false; |
266 | for (auto node : std::as_const(t&: cleanupNodeList)) { |
267 | // Remove "spatial" nodes from scenegraph |
268 | if (QSSGRenderGraphObject::isNodeType(type: node->type)) { |
269 | QSSGRenderNode *spatialNode = static_cast<QSSGRenderNode *>(node); |
270 | spatialNode->removeFromGraph(); |
271 | } |
272 | |
273 | if (node->type == QQuick3DObjectPrivate::Type::Item2D) { |
274 | --inputHandlingEnabled; |
275 | } else if (node->type == QQuick3DObjectPrivate::Type::Image2D) { |
276 | auto image = static_cast<QSSGRenderImage *>(node); |
277 | if (image && image->m_qsgTexture != nullptr ) { |
278 | --inputHandlingEnabled; |
279 | } |
280 | } |
281 | |
282 | // Remove all nodes from the node map because they will no |
283 | // longer be usable from this point from the frontend |
284 | m_nodeMap.remove(key: node); |
285 | |
286 | // Some nodes will trigger resource cleanups that need to |
287 | // happen at a specified time (when graphics backend is active) |
288 | // So build another queue for graphics assets marked for removal |
289 | if (node->hasGraphicsResources()) { |
290 | wattached->queueForCleanup(obj: node); |
291 | if (node->type == QSSGRenderGraphObject::Type::ResourceLoader) |
292 | resourceLoaders.remove(value: node); |
293 | } else { |
294 | delete node; |
295 | } |
296 | } |
297 | |
298 | // Nodes are now "cleaned up" so clear the cleanup list |
299 | cleanupNodeList.clear(); |
300 | |
301 | return ret; |
302 | } |
303 | |
304 | bool QQuick3DSceneManager::updateResources(QQuick3DObject **listHead) |
305 | { |
306 | // Detach the current list head first, and consume all reachable entries. |
307 | // New entries may be added to the new list while traversing, which will be |
308 | // visited on the next updateDirtyNodes() call. |
309 | bool ret = false; |
310 | QQuick3DObject *updateList = *listHead; |
311 | *listHead = nullptr; |
312 | if (updateList) |
313 | QQuick3DObjectPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
314 | |
315 | QQuick3DObject *item = updateList; |
316 | while (item) { |
317 | // Different processing for resource nodes vs hierarchical nodes etc. |
318 | Q_ASSERT(!QSSGRenderGraphObject::isNodeType(QQuick3DObjectPrivate::get(item)->type) || !QSSGRenderGraphObject::isExtension(QQuick3DObjectPrivate::get(item)->type)); |
319 | // handle hierarchical nodes |
320 | updateDirtyResource(resourceObject: item); |
321 | auto *po = QQuick3DObjectPrivate::get(item); |
322 | ret |= po->sharedResource; |
323 | po->removeFromDirtyList(); |
324 | item = updateList; |
325 | } |
326 | |
327 | return ret; |
328 | } |
329 | |
330 | void QQuick3DSceneManager::updateNodes(QQuick3DObject **listHead) |
331 | { |
332 | // Detach the current list head first, and consume all reachable entries. |
333 | // New entries may be added to the new list while traversing, which will be |
334 | // visited on the next updateDirtyNodes() call. |
335 | QQuick3DObject *updateList = *listHead; |
336 | *listHead = nullptr; |
337 | if (updateList) |
338 | QQuick3DObjectPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
339 | |
340 | QQuick3DObject *item = updateList; |
341 | while (item) { |
342 | // Different processing for resource nodes vs hierarchical nodes (anything that's _not_ a resource) |
343 | Q_ASSERT(QSSGRenderGraphObject::isNodeType(QQuick3DObjectPrivate::get(item)->type)); |
344 | // handle hierarchical nodes |
345 | updateDirtySpatialNode(spatialNode: static_cast<QQuick3DNode *>(item)); |
346 | QQuick3DObjectPrivate::get(item)->removeFromDirtyList(); |
347 | item = updateList; |
348 | } |
349 | } |
350 | |
351 | void QQuick3DSceneManager::updateExtensions(QQuick3DObject **listHead) |
352 | { |
353 | const auto updateDirtyExtensionNode = [this](QQuick3DObject *extension) { |
354 | QQuick3DObjectPrivate *po = QQuick3DObjectPrivate::get(item: extension); |
355 | po->dirtyAttributes = 0; // Not used, but we should still reset it. |
356 | QSSGRenderGraphObject *node = po->spatialNode; |
357 | po->spatialNode = extension->updateSpatialNode(node); |
358 | if (po->spatialNode) |
359 | m_nodeMap.insert(key: po->spatialNode, value: extension); |
360 | }; |
361 | |
362 | // Detach the current list head first, and consume all reachable entries. |
363 | // New entries may be added to the new list while traversing, which will be |
364 | // visited on the next updateDirtyNodes() call. |
365 | QQuick3DObject *updateList = *listHead; |
366 | *listHead = nullptr; |
367 | if (updateList) |
368 | QQuick3DObjectPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
369 | |
370 | QQuick3DObject *item = updateList; |
371 | while (item) { |
372 | // Different processing for resource nodes vs hierarchical nodes (anything that's _not_ a resource) |
373 | Q_ASSERT(QSSGRenderGraphObject::isExtension(QQuick3DObjectPrivate::get(item)->type)); |
374 | // handle hierarchical nodes |
375 | updateDirtyExtensionNode(item); |
376 | QQuick3DObjectPrivate::get(item)->removeFromDirtyList(); |
377 | item = updateList; |
378 | } |
379 | } |
380 | |
381 | void QQuick3DSceneManager::preSync() |
382 | { |
383 | for (auto it = std::begin(arr&: dirtyResources), end = std::end(arr&: dirtyResources); it != end; ++it) { |
384 | QQuick3DObject *next = *it; |
385 | while (next) { |
386 | next->preSync(); |
387 | next = QQuick3DObjectPrivate::get(item: next)->nextDirtyItem; |
388 | } |
389 | } |
390 | |
391 | for (auto it = std::begin(arr&: dirtyNodes), end = std::end(arr&: dirtyNodes); it != end; ++it) { |
392 | QQuick3DObject *next = *it; |
393 | while (next) { |
394 | next->preSync(); |
395 | next = QQuick3DObjectPrivate::get(item: next)->nextDirtyItem; |
396 | } |
397 | } |
398 | |
399 | for (auto it = std::begin(arr&: dirtyExtensions), end = std::end(arr&: dirtyExtensions); it != end; ++it) { |
400 | QQuick3DObject *next = *it; |
401 | while (next) { |
402 | next->preSync(); |
403 | next = QQuick3DObjectPrivate::get(item: next)->nextDirtyItem; |
404 | } |
405 | } |
406 | } |
407 | |
408 | //////// |
409 | /// QQuick3DWindowAttachment |
410 | //////// |
411 | |
412 | QQuick3DWindowAttachment::QQuick3DWindowAttachment(QQuickWindow *window) |
413 | : m_window(window) |
414 | { |
415 | if (window) { |
416 | // Act when the application calls window->releaseResources() and the |
417 | // render loop emits the corresponding signal in order to forward the |
418 | // event to us as well. (do not confuse with other release-resources |
419 | // type of functions, this is about dropping pipeline and other resource |
420 | // caches than can be automatically recreated if needed on the next frame) |
421 | QQuickWindowPrivate *wd = QQuickWindowPrivate::get(c: window); |
422 | QSGRenderContext *rc = wd->context; |
423 | if (QSSG_GUARD_X(rc, "QQuickWindow has no QSGRenderContext, this should not happen" )) { |
424 | // QSGRenderContext signals are emitted on the render thread, if there is one; use DirectConnection |
425 | connect(sender: rc, signal: &QSGRenderContext::releaseCachedResourcesRequested, context: this, slot: &QQuick3DWindowAttachment::onReleaseCachedResources, type: Qt::DirectConnection); |
426 | connect(sender: rc, signal: &QSGRenderContext::invalidated, context: this, slot: &QQuick3DWindowAttachment::onInvalidated, type: Qt::DirectConnection); |
427 | } |
428 | |
429 | // We put this in the back of the queue to allow any clean-up of resources to happen first. |
430 | connect(sender: window, signal: &QQuickWindow::destroyed, context: this, slot: &QObject::deleteLater); |
431 | // afterAnimating is emitted on the main thread. |
432 | connect(sender: window, signal: &QQuickWindow::afterAnimating, context: this, slot: &QQuick3DWindowAttachment::preSync); |
433 | // afterFrameEnd is emitted on render thread. |
434 | connect(sender: window, signal: &QQuickWindow::afterFrameEnd, context: this, slot: &QQuick3DWindowAttachment::cleanupResources, type: Qt::DirectConnection); |
435 | } |
436 | } |
437 | |
438 | QQuick3DWindowAttachment::~QQuick3DWindowAttachment() |
439 | { |
440 | for (auto manager: sceneManagerCleanupQueue) { |
441 | sceneManagers.removeOne(t: manager); |
442 | delete manager; |
443 | } |
444 | // remaining sceneManagers should also be removed |
445 | qDeleteAll(c: sceneManagers); |
446 | qDeleteAll(c: resourceCleanupQueue); |
447 | qDeleteAll(c: pendingResourceCleanupQueue); |
448 | |
449 | QSSG_CHECK_X(!m_rci || m_rci.use_count() == 1, "RCI has unexpected reference count!" ); |
450 | } |
451 | |
452 | void QQuick3DWindowAttachment::preSync() |
453 | { |
454 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
455 | sceneManager->preSync(); |
456 | } |
457 | |
458 | // Called from the render thread |
459 | void QQuick3DWindowAttachment::cleanupResources() |
460 | { |
461 | // Pass the scene managers list of resources marked for |
462 | // removal to the render context for deletion |
463 | // The render context will take ownership of the nodes |
464 | // and clear the list |
465 | |
466 | // In special cases there is no rci because synchronize() is never called. |
467 | // This can happen when running with the software backend of Qt Quick. |
468 | // Handle this gracefully. |
469 | if (!m_rci) |
470 | return; |
471 | |
472 | // Check if there's orphaned resources that needs to be |
473 | // cleaned out first. |
474 | if (resourceCleanupQueue.size() != 0) |
475 | m_rci->cleanupResources(resources&: resourceCleanupQueue); |
476 | } |
477 | |
478 | // Called on the render thread, if there is one |
479 | void QQuick3DWindowAttachment::onReleaseCachedResources() |
480 | { |
481 | if (m_rci) |
482 | m_rci->releaseCachedResources(); |
483 | Q_EMIT releaseCachedResources(); |
484 | } |
485 | |
486 | void QQuick3DWindowAttachment::onInvalidated() |
487 | { |
488 | // If the SG RenderContex is invalidated and we're the only one holding onto the SSG |
489 | // RenderContextInterface then just release it. If the application is not going down |
490 | // a new RCI will be created/set during the next sync. |
491 | if (m_rci.use_count() == 1) { |
492 | onReleaseCachedResources(); |
493 | m_rci.reset(); |
494 | emit renderContextInterfaceChanged(); |
495 | } |
496 | } |
497 | |
498 | bool QQuick3DWindowAttachment::synchronize(QSet<QSSGRenderGraphObject *> &resourceLoaders) |
499 | { |
500 | // Terminate old scene managers |
501 | for (auto manager: sceneManagerCleanupQueue) { |
502 | sceneManagers.removeOne(t: manager); |
503 | delete manager; |
504 | } |
505 | // Terminate old scene managers |
506 | sceneManagerCleanupQueue = {}; |
507 | |
508 | bool sharedUpdateNeeded = false; |
509 | |
510 | // Cleanup |
511 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
512 | sharedUpdateNeeded |= sceneManager->cleanupNodes(); |
513 | |
514 | // Resources |
515 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
516 | sharedUpdateNeeded |= sceneManager->updateDirtyResourceNodes(); |
517 | // Spatial Nodes |
518 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
519 | sceneManager->updateDirtySpatialNodes(); |
520 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
521 | sceneManager->updateDiryExtensions(); |
522 | // Bounding Boxes |
523 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
524 | sceneManager->updateBoundingBoxes(mgr&: *m_rci->bufferManager()); |
525 | // Resource Loaders |
526 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
527 | resourceLoaders.unite(other: sceneManager->resourceLoaders); |
528 | |
529 | if (sharedUpdateNeeded) { |
530 | // We know there are shared resources in the scene, so notify the "world". |
531 | // Ideally we should be more targeted, but for now this will do the job. |
532 | for (auto &sceneManager : std::as_const(t&: sceneManagers)) |
533 | sceneManager->requestUpdate(); |
534 | } |
535 | |
536 | // Prepare pending (adopted) resources for clean-up (will happen as a result of afterFrameEnd()). |
537 | for (const auto &pr : std::as_const(t&: pendingResourceCleanupQueue)) |
538 | resourceCleanupQueue.insert(value: pr); |
539 | pendingResourceCleanupQueue.clear(); |
540 | |
541 | return sharedUpdateNeeded; |
542 | } |
543 | |
544 | void QQuick3DWindowAttachment::requestUpdate() |
545 | { |
546 | for (const auto &sm : std::as_const(t&: sceneManagers)) |
547 | sm->requestUpdate(); |
548 | } |
549 | |
550 | QQuickWindow *QQuick3DWindowAttachment::window() const { return m_window; } |
551 | |
552 | void QQuick3DWindowAttachment::setRci(const std::shared_ptr<QSSGRenderContextInterface> &rciptr) |
553 | { |
554 | QSSG_CHECK_X(m_rci == nullptr || m_rci.use_count() == 1, "Old render context was not released!" ); |
555 | m_rci = rciptr; |
556 | emit renderContextInterfaceChanged(); |
557 | } |
558 | |
559 | void QQuick3DWindowAttachment::registerSceneManager(QQuick3DSceneManager &manager) |
560 | { |
561 | if (!sceneManagers.contains(t: &manager)) |
562 | sceneManagers.push_back(t: &manager); |
563 | } |
564 | |
565 | void QQuick3DWindowAttachment::unregisterSceneManager(QQuick3DSceneManager &manager) |
566 | { |
567 | sceneManagers.removeOne(t: &manager); |
568 | } |
569 | |
570 | void QQuick3DWindowAttachment::queueForCleanup(QSSGRenderGraphObject *obj) |
571 | { |
572 | Q_ASSERT(obj->hasGraphicsResources()); |
573 | pendingResourceCleanupQueue.push_back(t: obj); |
574 | } |
575 | |
576 | void QQuick3DWindowAttachment::queueForCleanup(QQuick3DSceneManager *manager) |
577 | { |
578 | if (!sceneManagerCleanupQueue.contains(t: manager)) |
579 | sceneManagerCleanupQueue.push_back(t: manager); |
580 | } |
581 | |
582 | QT_END_NAMESPACE |
583 | |