From 5b354f93fb74cbbaaed49bc1e4d3d1c4c2e2ffe5 Mon Sep 17 00:00:00 2001 From: Sean Harmer Date: Mon, 28 Sep 2020 13:16:56 +0100 Subject: Ensure Scene3D re-renders when a backend node is updated There are two ways in which backend nodes can receive changes from the frontend: 1) By syncing the state directly from the frontend nodes 2) By changs delivered by the change arbiter Scene3D is already equipped to deal with changes going via the first mechanism. However, it is possible for changes going via the second mechanism to not trigger a re-render of a Scene3D item using the OnDemand render mode. This is even true in the case of using a Scene3D inside of a QQuickWidget even though this disables the threaded Qt Quick 2 rendering. The way this manifests is that: * A change is made via mechanism 2) such as creating some new nodes. * The frontend changes trigger a re-render of the Scene3D item via the existing notification mechanism (connection to the change arbiter's receivedChange() signal. * The QQuickWidget uses a timerEvent() to trigger a possible update * This notices the Scene3D is dirty and causes it to render and mark its state as clean. This does not yet pick up the newly created frontend nodes though as they need to go through the deferred post creation init dance. * The change arbiter then processes the deferred node creation events and creates the backend nodes. * At the next timerEvent from the QQuickWidget it still sees that the Scene3D is clean and does not bother to re-render it. This change fixes this by adding a new signal to the change arbiter, syncedChanges() that gets emitted only when the arbiter has actually finished distributing the changes going via mechanism 2) to the backend nodes. This syncedChhanges() signal is emitted on the aspect thread and so Scene3DItem connects to this using a queued connection. This then ensures that once the new backend nodes are actually present and up to date that the Scene3D in OnDemand mode will then re-render the scene with the complete scene graph. Change-Id: I3246f34a3cfe15392a390eb6d97b4e90346288a6 Reviewed-by: Mike Krus --- src/core/qchangearbiter.cpp | 15 ++++++++++++--- src/core/qchangearbiter_p.h | 1 + src/quick3d/imports/scene3d/scene3ditem.cpp | 7 +++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/core/qchangearbiter.cpp b/src/core/qchangearbiter.cpp index e5745b017..3672777de 100644 --- a/src/core/qchangearbiter.cpp +++ b/src/core/qchangearbiter.cpp @@ -171,12 +171,21 @@ void QChangeArbiter::removeLockingChangeQueue(QChangeArbiter::QChangeQueue *queu void QChangeArbiter::syncChanges() { - const std::lock_guard locker(m_mutex);; - for (QChangeArbiter::QChangeQueue *changeQueue : qAsConst(m_changeQueues)) + const std::lock_guard locker(m_mutex); + + bool hasChanges = false; + for (QChangeArbiter::QChangeQueue *changeQueue : qAsConst(m_changeQueues)) { + hasChanges |= !changeQueue->empty(); distributeQueueChanges(changeQueue); + } - for (QChangeQueue *changeQueue : qAsConst(m_lockingChangeQueues)) + for (QChangeQueue *changeQueue : qAsConst(m_lockingChangeQueues)) { + hasChanges |= !changeQueue->empty(); distributeQueueChanges(changeQueue); + } + + if (hasChanges) + emit syncedChanges(); } void QChangeArbiter::setScene(QScene *scene) diff --git a/src/core/qchangearbiter_p.h b/src/core/qchangearbiter_p.h index 483b46575..e32fe7da4 100644 --- a/src/core/qchangearbiter_p.h +++ b/src/core/qchangearbiter_p.h @@ -138,6 +138,7 @@ public: Q_SIGNALS: void receivedChange(); + void syncedChanges(); protected: typedef std::vector QChangeQueue; diff --git a/src/quick3d/imports/scene3d/scene3ditem.cpp b/src/quick3d/imports/scene3d/scene3ditem.cpp index 586de9938..a720b8aa0 100644 --- a/src/quick3d/imports/scene3d/scene3ditem.cpp +++ b/src/quick3d/imports/scene3d/scene3ditem.cpp @@ -886,8 +886,15 @@ QSGNode *Scene3DItem::updatePaintNode(QSGNode *node, QQuickItem::UpdatePaintNode updateWindowSurface(); managerNode->init(); // Note: ChangeArbiter is only set after aspect was registered + + // This allows Scene3DItem to know when it needs to re-render as a result of frontend nodes receiving a change. QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::receivedChange, this, [this] { m_dirty = true; }, Qt::DirectConnection); + + // This allows Scene3DItem to know when it needs to re-render as a result of backend nodes receiving a change. + // For e.g. nodes being created/destroyed. + QObject::connect(renderAspectPriv->m_aspectManager->changeArbiter(), &Qt3DCore::QChangeArbiter::syncedChanges, + this, [this] { m_dirty = true; }, Qt::QueuedConnection); } const bool usesFBO = m_compositingMode == FBO; -- cgit v1.2.3