diff options
author | Paul Lemire <paul.lemire@kdab.com> | 2020-10-28 11:28:32 +0100 |
---|---|---|
committer | Paul Lemire <paul.lemire@kdab.com> | 2020-11-05 08:10:28 +0100 |
commit | 46f6e8f9521fa8a3e94b000be16862ace30b65b9 (patch) | |
tree | 4865a2eb274361c23fe271f33ab9bf1c9b489cdd | |
parent | db1a22f00c85aa2d2dde3a19a3611f98e2f0da5f (diff) |
Scene3DItem: release frontend/backend tree on destruction
We fully release the AspectEngine when the last of the threads between
main/sg thread is destroyed. We do it that way as the destruction order
of the threads is non deterministic and we can only destroy the
AspectEngine after it has been shut down from the SGThread.
However, that can lead to the Entity tree to never be properly
released/cleaned up in the case where the main thread is destroyed before
the render thread.
To account for that, we now release the root entity when the
AspectEngineDestroyer allowRelease is called in the context of the main
thread. That way, even if the AspectEngine is fully destroyed later
once SG thread exits(), the scene tree is still cleaned up while we have
an event loop.
Special handling is done for that behavior not to happen in the case
Scene3D is rendered from with a QQuickRenderControl.
This should fix asserts or crash observed when using Scene2D within a
Scene3D scene.
Change-Id: I77edff567d60e53a61023f88b551fc475885e5eb
Reviewed-by: Mike Krus <mike.krus@kdab.com>
-rw-r--r-- | src/quick3d/imports/scene3d/scene3ditem.cpp | 31 |
1 files changed, 28 insertions, 3 deletions
diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index a720b8aa0..377569f6a 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -100,13 +100,31 @@ public: m_targetAllowed = targetCount; } + Qt3DCore::QAspectEngine *aspectEngine() const + { + if (children().empty()) + return nullptr; + return qobject_cast<Qt3DCore::QAspectEngine *>(children().first()); + } + + bool releaseRootEntity() const { return m_releaseRootEntity; } + void setReleaseRootEntity(bool release) { m_releaseRootEntity = release; } + void allowRelease() { ++m_allowed; - if (m_allowed == m_targetAllowed) { - if (QThread::currentThread() == thread()) + const bool shouldSelfDestruct = m_allowed == m_targetAllowed; + if (QThread::currentThread() == thread()) { + // Force Backend Tree to be cleaned up + Qt3DCore::QAspectEngine *engine = aspectEngine(); + // If used in the regular Scene3D, take this opportunity to release the root + // Entity which will release the backend entities of Qt3D + if (m_releaseRootEntity && engine && engine->rootEntity()) + engine->setRootEntity(Qt3DCore::QEntityPtr()); + if (shouldSelfDestruct) delete this; - else + } else { + if (shouldSelfDestruct) deleteLater(); } } @@ -118,6 +136,7 @@ private: int m_allowed = 0; int m_targetAllowed = 0; bool m_sgNodeAlive = false; + bool m_releaseRootEntity = true; }; /*! @@ -881,6 +900,12 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode // If the render aspect wasn't created yet, do so now if (!managerNode->isInitialized()) { auto *rw = QQuickRenderControl::renderWindowFor(window()); + + // When using a RenderControl, the AspectEngineDestroyer shouldn't release the root entity + // as it could be reused if render control was moved to another window + if (rw) + m_aspectEngineDestroyer->setReleaseRootEntity(false); + auto renderAspectPriv = static_cast<QRenderAspectPrivate*>(QRenderAspectPrivate::get(renderAspect)); renderAspectPriv->m_screen = (rw ? rw->screen() : window()->screen()); updateWindowSurface(); |