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>
This commit is contained in:
David Redondo 2023-03-01 08:47:04 +01:00
parent b538a53a9d
commit 141d626029
7 changed files with 61 additions and 6 deletions

View File

@ -17,6 +17,7 @@ QBackingStoreDefaultCompositor::~QBackingStoreDefaultCompositor()
void QBackingStoreDefaultCompositor::reset()
{
m_rhi = nullptr;
delete m_psNoBlend;
m_psNoBlend = nullptr;
delete m_psBlend;

View File

@ -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())

View File

@ -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();

View File

@ -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())

View File

@ -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
{

View File

@ -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 {

View File

@ -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: