summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Redondo <qt@david-redondo.de>2023-03-01 08:47:04 +0100
committerDavid Redondo <qt@david-redondo.de>2023-03-17 14:45:21 +0100
commit141d626029776a6b7a6f2fbc29a051221c0742e9 (patch)
treecfe36f293a22b3b22a1b4170737305b2ee68a356
parentb538a53a9d2a581c2fddf5dae0a504ef954c3641 (diff)
Handle device loss for texture widgets
Previously the widget stayed black and we printed "QBackingStoreDefaultCompositor: the QRhi has changed unexpectedly, this should not happen". To make it work the compositor is recreated in addition to the rhi and the widgets are informed with the internal events. Change-Id: I982d08bd3530478fe0f827080154c008a92a812e Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
-rw-r--r--src/gui/painting/qbackingstoredefaultcompositor.cpp1
-rw-r--r--src/gui/painting/qplatformbackingstore.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp6
-rw-r--r--src/widgets/kernel/qwidget.cpp8
-rw-r--r--src/widgets/kernel/qwidget_p.h1
-rw-r--r--src/widgets/kernel/qwidgetrepaintmanager.cpp4
-rw-r--r--tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp46
7 files changed, 61 insertions, 6 deletions
diff --git a/src/gui/painting/qbackingstoredefaultcompositor.cpp b/src/gui/painting/qbackingstoredefaultcompositor.cpp
index 1dd116ac81..383fb651dd 100644
--- a/src/gui/painting/qbackingstoredefaultcompositor.cpp
+++ b/src/gui/painting/qbackingstoredefaultcompositor.cpp
@@ -17,6 +17,7 @@ QBackingStoreDefaultCompositor::~QBackingStoreDefaultCompositor()
void QBackingStoreDefaultCompositor::reset()
{
+ m_rhi = nullptr;
delete m_psNoBlend;
m_psNoBlend = nullptr;
delete m_psBlend;
diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp
index 82e7778b86..f7c4df40c8 100644
--- a/src/gui/painting/qplatformbackingstore.cpp
+++ b/src/gui/painting/qplatformbackingstore.cpp
@@ -379,6 +379,7 @@ void QPlatformBackingStore::graphicsDeviceReportedLost()
return;
qWarning("Rhi backingstore: graphics device lost, attempting to reinitialize");
+ d_ptr->compositor.reset();
d_ptr->rhiSupport.reset();
d_ptr->rhiSupport.create();
if (!d_ptr->rhiSupport.rhi())
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
index f3905dff81..0659cf9fc4 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -877,8 +877,10 @@ QPlatformBackingStore::FlushResult QXcbBackingStore::rhiFlush(QWindow *window,
m_image->flushScrolledRegion(true);
- QPlatformBackingStore::rhiFlush(window, sourceDevicePixelRatio, region, offset, textures, translucentBackground);
-
+ auto result = QPlatformBackingStore::rhiFlush(window, sourceDevicePixelRatio, region, offset,
+ textures, translucentBackground);
+ if (result != FlushSuccess)
+ return result;
QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle());
if (platformWindow->needsSync()) {
platformWindow->updateSyncRequestCounter();
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index 38957e9b46..08acae00c7 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -10602,7 +10602,7 @@ void QWidget::setParent(QWidget *parent)
setParent((QWidget*)parent, windowFlags() & ~Qt::WindowType_Mask);
}
-static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type eventType)
+void qSendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type eventType)
{
QWidgetPrivate *d = QWidgetPrivate::get(widget);
if (d->renderToTexture) {
@@ -10613,7 +10613,7 @@ static void sendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent
for (int i = 0; i < d->children.size(); ++i) {
QWidget *w = qobject_cast<QWidget *>(d->children.at(i));
if (w && !w->isWindow() && QWidgetPrivate::get(w)->textureChildSeen)
- sendWindowChangeToTextureChildrenRecursively(w, eventType);
+ qSendWindowChangeToTextureChildrenRecursively(w, eventType);
}
}
@@ -10675,7 +10675,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
// texture-based widgets need a pre-notification when their associated top-level window changes
// This is not under the wasCreated/newParent conditions above in order to also play nice with QDockWidget.
if (d->textureChildSeen && ((!parent && parentWidget()) || (parent && parent->window() != oldtlw)))
- sendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal);
+ qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowAboutToChangeInternal);
// If we get parented into another window, children will be folded
// into the new parent's focus chain, so clear focus now.
@@ -10756,7 +10756,7 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f)
// texture-based widgets need another event when their top-level window
// changes (more precisely, has already changed at this point)
if (d->textureChildSeen && oldtlw != window())
- sendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal);
+ qSendWindowChangeToTextureChildrenRecursively(this, QEvent::WindowChangeInternal);
if (!wasCreated) {
if (isWindow() || parentWidget()->isVisible())
diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h
index d9d9825b6f..2e1c4030f8 100644
--- a/src/widgets/kernel/qwidget_p.h
+++ b/src/widgets/kernel/qwidget_p.h
@@ -65,6 +65,7 @@ class QUnifiedToolbarSurface;
// implemented in qshortcut.cpp
bool qWidgetShortcutContextMatcher(QObject *object, Qt::ShortcutContext context);
+void qSendWindowChangeToTextureChildrenRecursively(QWidget *widget, QEvent::Type eventType);
class QUpdateLaterEvent : public QEvent
{
diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp
index 16db49f95e..718f904f93 100644
--- a/src/widgets/kernel/qwidgetrepaintmanager.cpp
+++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp
@@ -1058,7 +1058,11 @@ void QWidgetRepaintManager::flush(QWidget *widget, const QRegion &region, QPlatf
translucentBackground);
widgetWindowPrivate->sendComposeStatus(widget->window(), true);
if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
+ qSendWindowChangeToTextureChildrenRecursively(widget->window(),
+ QEvent::WindowAboutToChangeInternal);
store->handle()->graphicsDeviceReportedLost();
+ qSendWindowChangeToTextureChildrenRecursively(widget->window(),
+ QEvent::WindowChangeInternal);
widget->update();
}
} else {
diff --git a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
index ca1deb6094..07cce4cdc3 100644
--- a/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
+++ b/tests/auto/widgets/widgets/qopenglwidget/tst_qopenglwidget.cpp
@@ -4,6 +4,7 @@
#include <QtOpenGLWidgets/QOpenGLWidget>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QPainter>
+#include <QtGui/QBackingStore>
#include <QtGui/QScreen>
#include <QtGui/QStaticText>
#include <QtWidgets/QGraphicsView>
@@ -21,7 +22,10 @@
#include <private/qopengltextureglyphcache_p.h>
#include <qpa/qplatformintegration.h>
#include <private/qguiapplication_p.h>
+#include <qpa/qplatformbackingstore.h>
#include <qpa/qplatformintegration.h>
+#include <private/qrhi_p.h>
+#include <private/qrhigles2_p.h>
class tst_QOpenGLWidget : public QObject
{
@@ -33,6 +37,9 @@ private slots:
void clearAndGrab();
void clearAndResizeAndGrab();
void createNonTopLevel();
+#if QT_CONFIG(egl)
+ void deviceLoss();
+#endif
void painter();
void reparentToAlreadyCreated();
void reparentToNotYetCreated();
@@ -189,6 +196,45 @@ void tst_QOpenGLWidget::createNonTopLevel()
QVERIFY(QOpenGLContext::currentContext() == glw->context() && glw->context());
}
+#if QT_CONFIG(egl)
+void tst_QOpenGLWidget::deviceLoss()
+{
+ QScopedPointer<QOpenGLWidget> w(new ClearWidget(0, 640, 480));
+
+ w->resize(640, 480);
+ w->show();
+
+ auto rhi = w->backingStore()->handle()->rhi();
+ QNativeInterface::QEGLContext *rhiContext = nullptr;
+ if (rhi->backend() == QRhi::OpenGLES2) {
+ auto rhiHandles = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
+ rhiContext = rhiHandles->context->nativeInterface<QNativeInterface::QEGLContext>();
+ }
+ if (!rhiContext)
+ QSKIP("deviceLoss needs EGL");
+
+ QVERIFY(QTest::qWaitForWindowExposed(w.data()));
+
+ QImage image = w->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), w->width());
+ QCOMPARE(image.height(), w->height());
+ QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+
+ rhiContext->invalidateContext();
+
+ w->resize(600, 600);
+ QSignalSpy frameSwappedSpy(w.get(), &QOpenGLWidget::resized);
+ QTRY_VERIFY(frameSwappedSpy.size() > 0);
+
+ image = w->grabFramebuffer();
+ QVERIFY(!image.isNull());
+ QCOMPARE(image.width(), w->width());
+ QCOMPARE(image.height(), w->height());
+ QVERIFY(image.pixel(30, 40) == qRgb(255, 0, 0));
+}
+#endif
+
class PainterWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public: