Decouple rate-limiting of paint-on-screen widgets from top level widget
As part of eacd58d4e78e7238ba5fcca90ba960aaf3ebd263, a mechanism was added to prevent posting redundant UpdateRequest events to the top level widget, managed by QWidgetRepaintManager. The mechanism relied on a boolean that was set when posting an update request event, and reset when processing the event for the top level widget, as part of QWidgetRepaintManager::sync(). However, for paint-on-screen widgets, we don't post an update request to the top level, we post it to the paint-on-screen widget directly. And when processing that event, we don't paint the widget though the normal QWidgetRepaintManager machinery, but instead call the paint event via QWidgetPrivate::paintOnScreen(). As a result, an update() on a paint-on-screen widget would result in never receiving updates for non-paint-on-screen widgets, as we assumed there was no reason to send further update requests. We could fix this by clearing the updateRequestSent flag as part of the paintOnScreen() code path, but that's incorrect as the flag represents whether the top level QWidgetRepaintManager needs an update request event or not, and would lead to missed updates to normal widgets until the paint-on-screen widget finishes its update. Instead, we only set updateRequestSent if we're posting update requests for non-paint-on-screen widgets, which in practice means the top level widget. The paint on screen logic in QWidgetRepaintManager::markDirty still takes care of rate-limiting the update requests to the paint-on-screen widget, by comparing the dirty area of the widget. There is definite room for improvement here, especially in the direction of handling update requests via QWindow::requestUpdate instead of manually posted events, but for now this will have to do. Fixes: QTBUG-80167 Pick-to: 6.6 6.5 6.2 5.15 Change-Id: Ib5685de7ca2fd7cd7883a25bb7bc0255ea242d30 Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io> (cherry picked from commit 697e1b0397259959e3f555296f87a0d9d923e4b5) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
007507b576
commit
c198f7124c
@ -355,7 +355,11 @@ void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime update
|
||||
|
||||
switch (updateTime) {
|
||||
case UpdateLater:
|
||||
updateRequestSent = true;
|
||||
// Prevent redundant update request events, unless it's a
|
||||
// paint on screen widget, as these don't go through the
|
||||
// normal backingstore sync machinery.
|
||||
if (!widget->d_func()->shouldPaintOnScreen())
|
||||
updateRequestSent = true;
|
||||
QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
|
||||
break;
|
||||
case UpdateNow: {
|
||||
|
@ -77,8 +77,11 @@ public:
|
||||
const auto type = event->type();
|
||||
if (type == QEvent::WindowActivate || type == QEvent::WindowDeactivate)
|
||||
return true;
|
||||
if (type == QEvent::UpdateRequest)
|
||||
++updateRequests;
|
||||
return QWidget::event(event);
|
||||
}
|
||||
int updateRequests = 0;
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override
|
||||
@ -256,6 +259,8 @@ private slots:
|
||||
void opaqueChildren();
|
||||
void staticContents();
|
||||
void scroll();
|
||||
void paintOnScreenUpdates();
|
||||
|
||||
#if defined(QT_BUILD_INTERNAL)
|
||||
void scrollWithOverlap();
|
||||
void overlappedRegion();
|
||||
@ -491,6 +496,90 @@ void tst_QWidgetRepaintManager::scroll()
|
||||
QCOMPARE(widget.takePaintedRegions(), QRegion());
|
||||
}
|
||||
|
||||
class PaintOnScreenWidget : public TestWidget
|
||||
{
|
||||
public:
|
||||
using TestWidget::TestWidget;
|
||||
|
||||
// Explicit override to prevent noPaintOnScreen on Windows
|
||||
QPaintEngine *paintEngine() const override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QWidgetRepaintManager::paintOnScreenUpdates()
|
||||
{
|
||||
{
|
||||
TestWidget topLevel;
|
||||
topLevel.setObjectName("TopLevel");
|
||||
topLevel.resize(500, 500);
|
||||
TestWidget renderToTextureWidget(&topLevel);
|
||||
renderToTextureWidget.setObjectName("RenderToTexture");
|
||||
renderToTextureWidget.setGeometry(0, 0, 200, 200);
|
||||
QWidgetPrivate::get(&renderToTextureWidget)->setRenderToTexture();
|
||||
|
||||
PaintOnScreenWidget paintOnScreenWidget(&topLevel);
|
||||
paintOnScreenWidget.setObjectName("PaintOnScreen");
|
||||
paintOnScreenWidget.setGeometry(200, 200, 300, 300);
|
||||
|
||||
topLevel.initialShow();
|
||||
|
||||
// Updating before toggling WA_PaintOnScreen should work fine
|
||||
paintOnScreenWidget.update();
|
||||
paintOnScreenWidget.waitForPainted();
|
||||
QVERIFY(paintOnScreenWidget.waitForPainted());
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QEXPECT_FAIL("", "This test fails on Android", Abort);
|
||||
#endif
|
||||
QCOMPARE(paintOnScreenWidget.takePaintedRegions(), paintOnScreenWidget.rect());
|
||||
|
||||
renderToTextureWidget.update();
|
||||
QVERIFY(renderToTextureWidget.waitForPainted());
|
||||
QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
|
||||
|
||||
// Then toggle WA_PaintOnScreen
|
||||
paintOnScreenWidget.setAttribute(Qt::WA_PaintOnScreen);
|
||||
|
||||
// The render-to-texture widget updates fine
|
||||
renderToTextureWidget.update();
|
||||
QVERIFY(renderToTextureWidget.waitForPainted());
|
||||
QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
|
||||
|
||||
// Updating the paint-on-screen texture widget will not result
|
||||
// in a paint event, but should result in an update request.
|
||||
paintOnScreenWidget.updateRequests = 0;
|
||||
paintOnScreenWidget.update();
|
||||
QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenWidget.updateRequests > 0; }));
|
||||
|
||||
// And should not prevent the render-to-texture widget from receiving updates
|
||||
renderToTextureWidget.update();
|
||||
QVERIFY(renderToTextureWidget.waitForPainted());
|
||||
QCOMPARE(renderToTextureWidget.takePaintedRegions(), renderToTextureWidget.rect());
|
||||
}
|
||||
|
||||
{
|
||||
TestWidget paintOnScreenTopLevel;
|
||||
paintOnScreenTopLevel.setObjectName("PaintOnScreenTopLevel");
|
||||
paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen);
|
||||
|
||||
paintOnScreenTopLevel.initialShow();
|
||||
|
||||
paintOnScreenTopLevel.updateRequests = 0;
|
||||
paintOnScreenTopLevel.update();
|
||||
QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 0; }));
|
||||
|
||||
// Turn off paint on screen and make it a render-to-texture widget.
|
||||
// This will lead us into a QWidgetRepaintManager::markDirty() code
|
||||
// path that checks updateRequestSent, which is still set from the
|
||||
// update above since paint-on-screen handling doesn't reset it.
|
||||
paintOnScreenTopLevel.setAttribute(Qt::WA_PaintOnScreen, false);
|
||||
QWidgetPrivate::get(&paintOnScreenTopLevel)->setRenderToTexture();
|
||||
paintOnScreenTopLevel.update();
|
||||
QVERIFY(QTest::qWaitFor([&]{ return paintOnScreenTopLevel.updateRequests > 1; }));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(QT_BUILD_INTERNAL)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user