diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2019-08-21 17:29:39 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2019-08-22 12:10:13 +0200 |
commit | 16868bd6a247d419d7c15ae10ff2c667e6110b43 (patch) | |
tree | 8a832a754394dd42acc268e4753896590497e8e7 /src/widgets/kernel | |
parent | 36cf237ea1ca1522e37c3d4e66f08dc00bc3295d (diff) |
widgets: Clean up and reorder QWidgetRepaintManager implementation
Group functions by related areas and order them roughly by their
flow in a normal app repaint cycle.
Change-Id: I7a963f612134b3fdbaf748e0432606825b8db64e
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
Diffstat (limited to 'src/widgets/kernel')
-rw-r--r-- | src/widgets/kernel/qwidget.cpp | 36 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetrepaintmanager.cpp | 704 | ||||
-rw-r--r-- | src/widgets/kernel/qwidgetrepaintmanager_p.h | 158 |
3 files changed, 460 insertions, 438 deletions
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 049ddb0213..c2caf7a421 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1775,6 +1775,42 @@ void QWidgetPrivate::syncBackingStore(const QRegion ®ion) } } +void QWidgetPrivate::repaint_sys(const QRegion &rgn) +{ + if (data.in_destructor) + return; + + if (shouldDiscardSyncRequest()) + return; + + Q_Q(QWidget); + if (q->testAttribute(Qt::WA_StaticContents)) { + if (!extra) + createExtra(); + extra->staticContentsSize = data.crect.size(); + } + + QPaintEngine *engine = q->paintEngine(); + + // QGLWidget does not support partial updates if: + // 1) The context is double buffered + // 2) The context is single buffered and auto-fill background is enabled. + const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL + || engine->type() == QPaintEngine::OpenGL2)) + && (usesDoubleBufferedGLContext || q->autoFillBackground()); + QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); + + toBePainted &= clipRect(); + clipToEffectiveMask(toBePainted); + if (toBePainted.isEmpty()) + return; // Nothing to repaint. + + drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0); + + if (Q_UNLIKELY(q->paintingActive())) + qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); +} + void QWidgetPrivate::setUpdatesEnabled_helper(bool enable) { Q_Q(QWidget); diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp index 96f440cd0f..b70ce39159 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager.cpp +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -71,164 +71,89 @@ QT_BEGIN_NAMESPACE Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList) #endif -static bool hasPlatformWindow(QWidget *widget) -{ - return widget && widget->windowHandle() && widget->windowHandle()->handle(); -} +// --------------------------------------------------------------------------- -/* - Moves the whole rect by (dx, dy) in widget's coordinate system. - Doesn't generate any updates. -*/ -bool QWidgetRepaintManager::bltRect(const QRect &rect, int dx, int dy, QWidget *widget) +QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel) + : tlw(topLevel), + updateRequestSent(0), + textureListWatcher(0), + perfFrames(0) { - const QPoint pos(widget->mapTo(tlw, rect.topLeft())); - const QRect tlwRect(QRect(pos, rect.size())); - if (dirty.intersects(tlwRect)) - return false; // We don't want to scroll junk. - return store->scroll(tlwRect, dx, dy); -} + store = tlw->backingStore(); + Q_ASSERT(store); -/*! - Returns the region (in top-level coordinates) that needs repaint and/or flush. + // Ensure all existing subsurfaces and static widgets are added to their respective lists. + updateLists(topLevel); +} - If the widget is non-zero, only the dirty region for the widget is returned - and the region will be in widget coordinates. -*/ -QRegion QWidgetRepaintManager::dirtyRegion(QWidget *widget) const +void QWidgetRepaintManager::updateLists(QWidget *cur) { - const bool widgetDirty = widget && widget != tlw; - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { - if (widgetDirty) { - const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); - const QPoint offset(widget->mapTo(tlw, QPoint())); - const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset)); - return dirtyWidgetRect.translated(-offset); - } - return QRect(QPoint(), tlwRect.size()); - } + if (!cur) + return; - // Calculate the region that needs repaint. - QRegion r(dirty); - for (int i = 0; i < dirtyWidgets.size(); ++i) { - QWidget *w = dirtyWidgets.at(i); - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) + QList<QObject*> children = cur->children(); + for (int i = 0; i < children.size(); ++i) { + QWidget *child = qobject_cast<QWidget*>(children.at(i)); + if (!child || child->isWindow()) continue; - r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint())); - } - // Append the region that needs flush. - r += dirtyOnScreen; - - for (QWidget *w : dirtyOnScreenWidgets) { - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) - continue; - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - r += wd->needsFlush->translated(w->mapTo(tlw, QPoint())); + updateLists(child); } - if (widgetDirty) { - // Intersect with the widget geometry and translate to its coordinates. - const QPoint offset(widget->mapTo(tlw, QPoint())); - r &= widget->rect().translated(offset); - r.translate(-offset); - } - return r; + if (cur->testAttribute(Qt::WA_StaticContents)) + addStaticWidget(cur); +} + +QWidgetRepaintManager::~QWidgetRepaintManager() +{ + for (int c = 0; c < dirtyWidgets.size(); ++c) + resetWidget(dirtyWidgets.at(c)); + for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) + resetWidget(dirtyRenderToTextureWidgets.at(c)); } /*! - Returns the static content inside the \a parent if non-zero; otherwise the static content - for the entire backing store is returned. The content will be clipped to \a withinClipRect - if non-empty. + Invalidates the \a r (in widget's coordinates) of the backing store, i.e. + all widgets intersecting with the region will be repainted when the backing + store is synced. */ -QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const +template <class T> +void QWidgetPrivate::invalidateBackingStore(const T &r) { - if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { - const QSize surfaceGeometry(store->size()); - QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - if (!withinClipRect.isEmpty()) - surfaceRect &= withinClipRect; - return QRegion(surfaceRect); - } - - QRegion region; - if (parent && parent->d_func()->children.isEmpty()) - return region; - - const bool clipToRect = !withinClipRect.isEmpty(); - const int count = staticWidgets.count(); - for (int i = 0; i < count; ++i) { - QWidget *w = staticWidgets.at(i); - QWidgetPrivate *wd = w->d_func(); - if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty() - || !w->isVisible() || (parent && !parent->isAncestorOf(w))) { - continue; - } - - QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height()); - const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint()); - if (clipToRect) - rect &= withinClipRect.translated(-offset); - if (rect.isEmpty()) - continue; - - rect &= wd->clipRect(); - if (rect.isEmpty()) - continue; + if (r.isEmpty()) + return; - QRegion visible(rect); - wd->clipToEffectiveMask(visible); - if (visible.isEmpty()) - continue; - wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true); + if (QCoreApplication::closingDown()) + return; - visible.translate(offset); - region += visible; - } + Q_Q(QWidget); + if (!q->isVisible() || !q->updatesEnabled()) + return; - return region; -} + QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); + if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) + return; -void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime) -{ - if (!widget) + T clipped(r); + clipped &= clipRect(); + if (clipped.isEmpty()) return; -#ifndef QT_NO_OPENGL - // Having every repaint() leading to a sync/flush is bad as it causes - // compositing and waiting for vsync each and every time. Change to - // UpdateLater, except for approx. once per frame to prevent starvation in - // case the control does not get back to the event loop. - QWidget *w = widget->window(); - if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) { - int refresh = 60; - QScreen *ws = w->windowHandle()->screen(); - if (ws) - refresh = ws->refreshRate(); - QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle()); - if (wd->lastComposeTime.isValid()) { - const qint64 elapsed = wd->lastComposeTime.elapsed(); - if (elapsed <= qint64(1000.0f / refresh)) - updateTime = UpdateLater; - } - } -#endif + if (!graphicsEffect && extra && extra->hasMask) { + QRegion masked(extra->mask); + masked &= clipped; + if (masked.isEmpty()) + return; - switch (updateTime) { - case UpdateLater: - updateRequestSent = true; - QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); - break; - case UpdateNow: { - QEvent event(QEvent::UpdateRequest); - QCoreApplication::sendEvent(widget, &event); - break; - } + tlwExtra->repaintManager->markDirty(masked, q, + QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); + } else { + tlwExtra->repaintManager->markDirty(clipped, q, + QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); } } +// Needed by tst_QWidget +template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore<QRect>(const QRect &r); static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; } static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); } @@ -352,49 +277,19 @@ void QWidgetRepaintManager::markDirty(const T &r, QWidget *widget, UpdateTime up template void QWidgetRepaintManager::markDirty<QRect>(const QRect &, QWidget *, UpdateTime, BufferState); template void QWidgetRepaintManager::markDirty<QRegion>(const QRegion &, QWidget *, UpdateTime, BufferState); -/*! - Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from - the backing store to the \a widget's native parent next time flush() is called. - - Paint on screen widgets are ignored. -*/ -void QWidgetRepaintManager::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset) +void QWidgetRepaintManager::addDirtyWidget(QWidget *widget, const QRegion &rgn) { - if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) - return; - - // Top-level. - if (widget == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region; - return; - } - - // Alien widgets. - if (!hasPlatformWindow(widget) && !widget->isWindow()) { - QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). - if (nativeParent == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region.translated(topLevelOffset); - return; - } - - // Alien widgets with native parent != tlw. - QWidgetPrivate *nativeParentPrivate = nativeParent->d_func(); - if (!nativeParentPrivate->needsFlush) - nativeParentPrivate->needsFlush = new QRegion; - const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint()); - *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset); - appendDirtyOnScreenWidget(nativeParent); - return; + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); +#if QT_CONFIG(graphicseffect) + if (widgetPrivate->graphicsEffect) + widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); + else +#endif // QT_CONFIG(graphicseffect) + widgetPrivate->dirty = rgn; + dirtyWidgets.append(widget); + widgetPrivate->inDirtyList = true; } - - // Native child widgets. - QWidgetPrivate *widgetPrivate = widget->d_func(); - if (!widgetPrivate->needsFlush) - widgetPrivate->needsFlush = new QRegion; - *widgetPrivate->needsFlush += region; - appendDirtyOnScreenWidget(widget); } void QWidgetRepaintManager::removeDirtyWidget(QWidget *w) @@ -415,43 +310,69 @@ void QWidgetRepaintManager::removeDirtyWidget(QWidget *w) } } -void QWidgetRepaintManager::updateLists(QWidget *cur) +void QWidgetRepaintManager::resetWidget(QWidget *widget) { - if (!cur) - return; - - QList<QObject*> children = cur->children(); - for (int i = 0; i < children.size(); ++i) { - QWidget *child = qobject_cast<QWidget*>(children.at(i)); - if (!child || child->isWindow()) - continue; - - updateLists(child); + if (widget) { + widget->d_func()->inDirtyList = false; + widget->d_func()->isScrolled = false; + widget->d_func()->isMoved = false; + widget->d_func()->dirty = QRegion(); } +} - if (cur->testAttribute(Qt::WA_StaticContents)) - addStaticWidget(cur); +void QWidgetRepaintManager::addDirtyRenderToTextureWidget(QWidget *widget) +{ + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); + Q_ASSERT(widgetPrivate->renderToTexture); + dirtyRenderToTextureWidgets.append(widget); + widgetPrivate->inDirtyList = true; + } } -QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel) - : tlw(topLevel), - updateRequestSent(0), - textureListWatcher(0), - perfFrames(0) +void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime) { - store = tlw->backingStore(); - Q_ASSERT(store); + if (!widget) + return; - // Ensure all existing subsurfaces and static widgets are added to their respective lists. - updateLists(topLevel); +#ifndef QT_NO_OPENGL + // Having every repaint() leading to a sync/flush is bad as it causes + // compositing and waiting for vsync each and every time. Change to + // UpdateLater, except for approx. once per frame to prevent starvation in + // case the control does not get back to the event loop. + QWidget *w = widget->window(); + if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) { + int refresh = 60; + QScreen *ws = w->windowHandle()->screen(); + if (ws) + refresh = ws->refreshRate(); + QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle()); + if (wd->lastComposeTime.isValid()) { + const qint64 elapsed = wd->lastComposeTime.elapsed(); + if (elapsed <= qint64(1000.0f / refresh)) + updateTime = UpdateLater; + } + } +#endif + + switch (updateTime) { + case UpdateLater: + updateRequestSent = true; + QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); + break; + case UpdateNow: { + QEvent event(QEvent::UpdateRequest); + QCoreApplication::sendEvent(widget, &event); + break; + } + } } -QWidgetRepaintManager::~QWidgetRepaintManager() +// --------------------------------------------------------------------------- + +static bool hasPlatformWindow(QWidget *widget) { - for (int c = 0; c < dirtyWidgets.size(); ++c) - resetWidget(dirtyWidgets.at(c)); - for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) - resetWidget(dirtyRenderToTextureWidgets.at(c)); + return widget && widget->windowHandle() && widget->windowHandle()->handle(); } static QVector<QRect> getSortedRectsToScroll(const QRegion ®ion, int dx, int dy) @@ -653,6 +574,21 @@ void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) } } +/* + Moves the whole rect by (dx, dy) in widget's coordinate system. + Doesn't generate any updates. +*/ +bool QWidgetRepaintManager::bltRect(const QRect &rect, int dx, int dy, QWidget *widget) +{ + const QPoint pos(widget->mapTo(tlw, rect.topLeft())); + const QRect tlwRect(QRect(pos, rect.size())); + if (dirty.intersects(tlwRect)) + return false; // We don't want to scroll junk. + return store->scroll(tlwRect, dx, dy); +} + +// --------------------------------------------------------------------------- + #ifndef QT_NO_OPENGL static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector<QWidget *> *nativeChildren) { @@ -764,36 +700,7 @@ static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) #endif // QT_NO_OPENGL -bool QWidgetPrivate::shouldDiscardSyncRequest() const -{ - Q_Q(const QWidget); - return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible(); -} - -bool QWidgetRepaintManager::syncAllowed() -{ -#ifndef QT_NO_OPENGL - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (textureListWatcher && !textureListWatcher->isLocked()) { - textureListWatcher->deleteLater(); - textureListWatcher = 0; - } else if (!tlwExtra->widgetTextures.empty()) { - bool skipSync = false; - for (const auto &tl : tlwExtra->widgetTextures) { - if (tl->isLocked()) { - if (!textureListWatcher) - textureListWatcher = new QPlatformTextureListWatcher(this); - if (!textureListWatcher->isLocked()) - textureListWatcher->watch(tl.get()); - skipSync = true; - } - } - if (skipSync) // cannot compose due to widget textures being in use - return false; - } -#endif - return true; -} +// --------------------------------------------------------------------------- /*! Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store. @@ -855,6 +762,37 @@ void QWidgetRepaintManager::sync() paintAndFlush(); } +bool QWidgetPrivate::shouldDiscardSyncRequest() const +{ + Q_Q(const QWidget); + return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible(); +} + +bool QWidgetRepaintManager::syncAllowed() +{ +#ifndef QT_NO_OPENGL + QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); + if (textureListWatcher && !textureListWatcher->isLocked()) { + textureListWatcher->deleteLater(); + textureListWatcher = 0; + } else if (!tlwExtra->widgetTextures.empty()) { + bool skipSync = false; + for (const auto &tl : tlwExtra->widgetTextures) { + if (tl->isLocked()) { + if (!textureListWatcher) + textureListWatcher = new QPlatformTextureListWatcher(this); + if (!textureListWatcher->isLocked()) + textureListWatcher->watch(tl.get()); + skipSync = true; + } + } + if (skipSync) // cannot compose due to widget textures being in use + return false; + } +#endif + return true; +} + void QWidgetRepaintManager::paintAndFlush() { const bool updatesDisabled = !tlw->updatesEnabled(); @@ -1074,6 +1012,60 @@ void QWidgetRepaintManager::paintAndFlush() } /*! + Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from + the backing store to the \a widget's native parent next time flush() is called. + + Paint on screen widgets are ignored. +*/ +void QWidgetRepaintManager::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset) +{ + if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) + return; + + // Top-level. + if (widget == tlw) { + if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) + dirtyOnScreen += region; + return; + } + + // Alien widgets. + if (!hasPlatformWindow(widget) && !widget->isWindow()) { + QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). + if (nativeParent == tlw) { + if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) + dirtyOnScreen += region.translated(topLevelOffset); + return; + } + + // Alien widgets with native parent != tlw. + QWidgetPrivate *nativeParentPrivate = nativeParent->d_func(); + if (!nativeParentPrivate->needsFlush) + nativeParentPrivate->needsFlush = new QRegion; + const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint()); + *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset); + appendDirtyOnScreenWidget(nativeParent); + return; + } + + // Native child widgets. + QWidgetPrivate *widgetPrivate = widget->d_func(); + if (!widgetPrivate->needsFlush) + widgetPrivate->needsFlush = new QRegion; + *widgetPrivate->needsFlush += region; + appendDirtyOnScreenWidget(widget); +} + +void QWidgetRepaintManager::appendDirtyOnScreenWidget(QWidget *widget) +{ + if (!widget) + return; + + if (!dirtyOnScreenWidgets.contains(widget)) + dirtyOnScreenWidgets.append(widget); +} + +/*! Flushes the contents of the backing store into the top-level widget. If the \a widget is non-zero, the content is flushed to the \a widget. */ @@ -1191,6 +1183,174 @@ void QWidgetRepaintManager::flush(QWidget *widget, const QRegion ®ion, QPlatf store->flush(effectiveRegion, widget->windowHandle(), offset); } +// --------------------------------------------------------------------------- + +void QWidgetRepaintManager::addStaticWidget(QWidget *widget) +{ + if (!widget) + return; + + Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents)); + if (!staticWidgets.contains(widget)) + staticWidgets.append(widget); +} + +// Move the reparented widget and all its static children from this backing store +// to the new backing store if reparented into another top-level / backing store. +void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented) +{ + Q_ASSERT(reparented); + QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager(); + if (newPaintManager == this) + return; + + int i = 0; + while (i < staticWidgets.size()) { + QWidget *w = staticWidgets.at(i); + if (reparented == w || reparented->isAncestorOf(w)) { + staticWidgets.removeAt(i); + if (newPaintManager) + newPaintManager->addStaticWidget(w); + } else { + ++i; + } + } +} + +void QWidgetRepaintManager::removeStaticWidget(QWidget *widget) +{ + staticWidgets.removeAll(widget); +} + +bool QWidgetRepaintManager::hasStaticContents() const +{ +#if defined(Q_OS_WIN) + return !staticWidgets.isEmpty(); +#else + return !staticWidgets.isEmpty() && false; +#endif +} + +/*! + Returns the static content inside the \a parent if non-zero; otherwise the static content + for the entire backing store is returned. The content will be clipped to \a withinClipRect + if non-empty. +*/ +QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const +{ + if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { + const QSize surfaceGeometry(store->size()); + QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + if (!withinClipRect.isEmpty()) + surfaceRect &= withinClipRect; + return QRegion(surfaceRect); + } + + QRegion region; + if (parent && parent->d_func()->children.isEmpty()) + return region; + + const bool clipToRect = !withinClipRect.isEmpty(); + const int count = staticWidgets.count(); + for (int i = 0; i < count; ++i) { + QWidget *w = staticWidgets.at(i); + QWidgetPrivate *wd = w->d_func(); + if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty() + || !w->isVisible() || (parent && !parent->isAncestorOf(w))) { + continue; + } + + QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height()); + const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint()); + if (clipToRect) + rect &= withinClipRect.translated(-offset); + if (rect.isEmpty()) + continue; + + rect &= wd->clipRect(); + if (rect.isEmpty()) + continue; + + QRegion visible(rect); + wd->clipToEffectiveMask(visible); + if (visible.isEmpty()) + continue; + wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true); + + visible.translate(offset); + region += visible; + } + + return region; +} + +void QWidgetRepaintManager::updateStaticContentsSize() +{ + for (int i = 0; i < staticWidgets.size(); ++i) { + QWidgetPrivate *wd = staticWidgets.at(i)->d_func(); + if (!wd->extra) + wd->createExtra(); + wd->extra->staticContentsSize = wd->data.crect.size(); + } +} + +// --------------------------------------------------------------------------- + +bool QWidgetRepaintManager::isDirty() const +{ + return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty()); +} + +/*! + Returns the region (in top-level coordinates) that needs repaint and/or flush. + + If the widget is non-zero, only the dirty region for the widget is returned + and the region will be in widget coordinates. +*/ +QRegion QWidgetRepaintManager::dirtyRegion(QWidget *widget) const +{ + const bool widgetDirty = widget && widget != tlw; + const QRect tlwRect(topLevelRect()); + const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); + if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { + if (widgetDirty) { + const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); + const QPoint offset(widget->mapTo(tlw, QPoint())); + const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset)); + return dirtyWidgetRect.translated(-offset); + } + return QRect(QPoint(), tlwRect.size()); + } + + // Calculate the region that needs repaint. + QRegion r(dirty); + for (int i = 0; i < dirtyWidgets.size(); ++i) { + QWidget *w = dirtyWidgets.at(i); + if (widgetDirty && w != widget && !widget->isAncestorOf(w)) + continue; + r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint())); + } + + // Append the region that needs flush. + r += dirtyOnScreen; + + for (QWidget *w : dirtyOnScreenWidgets) { + if (widgetDirty && w != widget && !widget->isAncestorOf(w)) + continue; + QWidgetPrivate *wd = w->d_func(); + Q_ASSERT(wd->needsFlush); + r += wd->needsFlush->translated(w->mapTo(tlw, QPoint())); + } + + if (widgetDirty) { + // Intersect with the widget geometry and translate to its coordinates. + const QPoint offset(widget->mapTo(tlw, QPoint())); + r &= widget->rect().translated(offset); + r.translate(-offset); + } + return r; +} + /*! Invalidates the backing store when the widget is resized. Static areas are never invalidated unless absolutely needed. @@ -1283,86 +1443,6 @@ void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, c } } -/*! - Invalidates the \a r (in widget's coordinates) of the backing store, i.e. - all widgets intersecting with the region will be repainted when the backing - store is synced. -*/ -template <class T> -void QWidgetPrivate::invalidateBackingStore(const T &r) -{ - if (r.isEmpty()) - return; - - if (QCoreApplication::closingDown()) - return; - - Q_Q(QWidget); - if (!q->isVisible() || !q->updatesEnabled()) - return; - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) - return; - - T clipped(r); - clipped &= clipRect(); - if (clipped.isEmpty()) - return; - - if (!graphicsEffect && extra && extra->hasMask) { - QRegion masked(extra->mask); - masked &= clipped; - if (masked.isEmpty()) - return; - - tlwExtra->repaintManager->markDirty(masked, q, - QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); - } else { - tlwExtra->repaintManager->markDirty(clipped, q, - QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); - } -} -// Needed by tst_QWidget -template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore<QRect>(const QRect &r); - -void QWidgetPrivate::repaint_sys(const QRegion &rgn) -{ - if (data.in_destructor) - return; - - if (shouldDiscardSyncRequest()) - return; - - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_StaticContents)) { - if (!extra) - createExtra(); - extra->staticContentsSize = data.crect.size(); - } - - QPaintEngine *engine = q->paintEngine(); - - // QGLWidget does not support partial updates if: - // 1) The context is double buffered - // 2) The context is single buffered and auto-fill background is enabled. - const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL - || engine->type() == QPaintEngine::OpenGL2)) - && (usesDoubleBufferedGLContext || q->autoFillBackground()); - QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); - - toBePainted &= clipRect(); - clipToEffectiveMask(toBePainted); - if (toBePainted.isEmpty()) - return; // Nothing to repaint. - - drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0); - - if (Q_UNLIKELY(q->paintingActive())) - qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); -} - - QT_END_NAMESPACE #include "moc_qwidgetrepaintmanager_p.cpp" diff --git a/src/widgets/kernel/qwidgetrepaintmanager_p.h b/src/widgets/kernel/qwidgetrepaintmanager_p.h index d0368b2232..5686457dfb 100644 --- a/src/widgets/kernel/qwidgetrepaintmanager_p.h +++ b/src/widgets/kernel/qwidgetrepaintmanager_p.h @@ -98,9 +98,6 @@ public: QWidgetRepaintManager(QWidget *t); ~QWidgetRepaintManager(); - void sync(QWidget *exposedWidget, const QRegion &exposedRegion); - void sync(); - QBackingStore *backingStore() const { return store; } void setBackingStore(QBackingStore *backingStore) { store = backingStore; } @@ -110,49 +107,44 @@ public: void removeDirtyWidget(QWidget *w); - inline void addStaticWidget(QWidget *widget) - { - if (!widget) - return; - - Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents)); - if (!staticWidgets.contains(widget)) - staticWidgets.append(widget); - } - - // Move the reparented widget and all its static children from this backing store - // to the new backing store if reparented into another top-level / backing store. - inline void moveStaticWidgets(QWidget *reparented) - { - Q_ASSERT(reparented); - QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager(); - if (newPaintManager == this) - return; - - int i = 0; - while (i < staticWidgets.size()) { - QWidget *w = staticWidgets.at(i); - if (reparented == w || reparented->isAncestorOf(w)) { - staticWidgets.removeAt(i); - if (newPaintManager) - newPaintManager->addStaticWidget(w); - } else { - ++i; - } - } - } - - inline void removeStaticWidget(QWidget *widget) - { - staticWidgets.removeAll(widget); - } + void sync(QWidget *exposedWidget, const QRegion &exposedRegion); + void sync(); + void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); + + void addStaticWidget(QWidget *widget); + void moveStaticWidgets(QWidget *reparented); + void removeStaticWidget(QWidget *widget); QRegion staticContents(QWidget *widget = nullptr, const QRect &withinClipRect = QRect()) const; - void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); private: + void updateLists(QWidget *widget); + + void addDirtyWidget(QWidget *widget, const QRegion &rgn); + void resetWidget(QWidget *widget); + + void addDirtyRenderToTextureWidget(QWidget *widget); + + void sendUpdateRequest(QWidget *widget, UpdateTime updateTime); + + bool syncAllowed(); + void paintAndFlush(); + + void appendDirtyOnScreenWidget(QWidget *widget); + + void flush(QWidget *widget = nullptr); + void flush(QWidget *widget, const QRegion ®ion, QPlatformTextureList *widgetTextures); + + bool isDirty() const; + QRegion dirtyRegion(QWidget *widget = nullptr) const; + + bool hasStaticContents() const; + void updateStaticContentsSize(); + + QRect topLevelRect() const { return tlw->data->crect; } + QWidget *tlw; QRegion dirtyOnScreen; // needsFlush QRegion dirty; // needsRepaint @@ -168,92 +160,6 @@ private: QElapsedTimer perfTime; int perfFrames; - void sendUpdateRequest(QWidget *widget, UpdateTime updateTime); - - inline bool isDirty() const - { - return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty()); - } - - void paintAndFlush(); - - void flush(QWidget *widget = nullptr); - void flush(QWidget *widget, const QRegion ®ion, QPlatformTextureList *widgetTextures); - - QRegion dirtyRegion(QWidget *widget = nullptr) const; - - void updateLists(QWidget *widget); - - bool syncAllowed(); - - inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) - { - if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - QWidgetPrivate *widgetPrivate = widget->d_func(); -#if QT_CONFIG(graphicseffect) - if (widgetPrivate->graphicsEffect) - widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); - else -#endif // QT_CONFIG(graphicseffect) - widgetPrivate->dirty = rgn; - dirtyWidgets.append(widget); - widgetPrivate->inDirtyList = true; - } - } - - inline void addDirtyRenderToTextureWidget(QWidget *widget) - { - if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - QWidgetPrivate *widgetPrivate = widget->d_func(); - Q_ASSERT(widgetPrivate->renderToTexture); - dirtyRenderToTextureWidgets.append(widget); - widgetPrivate->inDirtyList = true; - } - } - - inline QRect topLevelRect() const - { - return tlw->data->crect; - } - - inline void appendDirtyOnScreenWidget(QWidget *widget) - { - if (!widget) - return; - - if (!dirtyOnScreenWidgets.contains(widget)) - dirtyOnScreenWidgets.append(widget); - } - - inline void resetWidget(QWidget *widget) - { - if (widget) { - widget->d_func()->inDirtyList = false; - widget->d_func()->isScrolled = false; - widget->d_func()->isMoved = false; - widget->d_func()->dirty = QRegion(); - } - } - - inline void updateStaticContentsSize() - { - for (int i = 0; i < staticWidgets.size(); ++i) { - QWidgetPrivate *wd = staticWidgets.at(i)->d_func(); - if (!wd->extra) - wd->createExtra(); - wd->extra->staticContentsSize = wd->data.crect.size(); - } - } - - inline bool hasStaticContents() const - { -#if defined(Q_OS_WIN) - return !staticWidgets.isEmpty(); -#else - return !staticWidgets.isEmpty() && false; -#endif - } - Q_DISABLE_COPY_MOVE(QWidgetRepaintManager) }; |