aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2023-12-19 21:52:05 +0100
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-01-16 14:25:26 +0000
commite34b15f0b7c1d686376af878ee58560cea0c517e (patch)
tree4116e5bfa477a520883bc458bdb46a38dcf5343c
parentf48bc1291f26bfedd5d43dd5cbc40e7ade066eb2 (diff)
QQuickWidget: Clean up if RHI goes away under our feet
The QQuickWidget doesn't normally own the RHI; the QWidgetRepaintManager does, via QBackingStoreRhiSupport. If the top level widget is destroyed, so is its QBackingStore, and the corresponding RHI. But the QQuickWidget may outlive its top level parent, in which case it needs to update its cached reference to the RHI, and do proper cleanup before it goes away. QRhiWidget already does the same thing, for the same use-case. This was observed when recreating the top level QWidget via destroy/create as part of the RHI widget compositor logic. Fixes: QTBUG-119760 Pick-to: 6.5 Change-Id: Ic44449abcfe4271660a3ac4e132d0c4a71a21b65 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> (cherry picked from commit 0d342e83123b27befde95fc35760a358dbff0b16) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit df2314d7082e2da8dc33ab2ab6417e5f618b0515) Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
-rw-r--r--src/quickwidgets/qquickwidget.cpp18
-rw-r--r--tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp22
2 files changed, 39 insertions, 1 deletions
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index fb3c8554be..5b3e4394ce 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -655,6 +655,9 @@ QQuickWidget::~QQuickWidget()
delete d->root;
d->root = nullptr;
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
+
// NB! resetting graphics resources must be done from this destructor,
// *not* from the private class' destructor. This is due to how destruction
// works and due to the QWidget dtor (for toplevels) destroying the repaint
@@ -1021,8 +1024,19 @@ void QQuickWidgetPrivate::initializeWithRhi()
if (rhi)
return;
- if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager())
+ if (QWidgetRepaintManager *repaintManager = tlwd->maybeRepaintManager()) {
rhi = repaintManager->rhi();
+ if (rhi) {
+ // We don't own the RHI, so make sure we clean up if it goes away
+ rhi->addCleanupCallback(q, [this](QRhi *rhi) {
+ if (this->rhi == rhi) {
+ invalidateRenderControl();
+ deviceLost = true;
+ this->rhi = nullptr;
+ }
+ });
+ }
+ }
if (!rhi) {
// The widget (and its parent chain, if any) may not be shown at
@@ -1675,6 +1689,8 @@ bool QQuickWidget::event(QEvent *e)
}
case QEvent::WindowAboutToChangeInternal:
+ if (d->rhi)
+ d->rhi->removeCleanupCallback(this);
d->invalidateRenderControl();
d->deviceLost = true;
d->rhi = nullptr;
diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
index fde6945ede..428180b3b8 100644
--- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
+++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp
@@ -138,6 +138,7 @@ private slots:
#endif
void focusPreserved();
void accessibilityHandlesViewChange();
+ void cleanupRhi();
private:
QPointingDevice *device = QTest::createTouchDevice();
@@ -1027,6 +1028,27 @@ void tst_qquickwidget::accessibilityHandlesViewChange()
(void)iface->child(0);
}
+class CreateDestroyWidget : public QWidget
+{
+public:
+ using QWidget::create;
+ using QWidget::destroy;
+};
+
+void tst_qquickwidget::cleanupRhi()
+{
+#ifdef Q_OS_ANDROID
+ QSKIP("This test crashes on Android (QTBUG-121133)");
+#endif
+ CreateDestroyWidget topLevel;
+ QQuickWidget quickWidget(&topLevel);
+ quickWidget.setSource(testFileUrl("rectangle.qml"));
+ topLevel.show();
+ QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
+
+ topLevel.destroy();
+ topLevel.create();
+}
QTEST_MAIN(tst_qquickwidget)