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>
This commit is contained in:
Tor Arne Vestbø 2019-08-21 17:29:39 +02:00
parent 36cf237ea1
commit 16868bd6a2
3 changed files with 477 additions and 455 deletions

View File

@ -1775,6 +1775,42 @@ void QWidgetPrivate::syncBackingStore(const QRegion &region)
} }
} }
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) void QWidgetPrivate::setUpdatesEnabled_helper(bool enable)
{ {
Q_Q(QWidget); Q_Q(QWidget);

View File

@ -71,164 +71,89 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList) Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
#endif #endif
static bool hasPlatformWindow(QWidget *widget) // ---------------------------------------------------------------------------
QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel)
: tlw(topLevel),
updateRequestSent(0),
textureListWatcher(0),
perfFrames(0)
{ {
return widget && widget->windowHandle() && widget->windowHandle()->handle(); store = tlw->backingStore();
Q_ASSERT(store);
// Ensure all existing subsurfaces and static widgets are added to their respective lists.
updateLists(topLevel);
} }
/* void QWidgetRepaintManager::updateLists(QWidget *cur)
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())); if (!cur)
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);
}
/*!
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;
}
/*!
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::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
{
if (!widget)
return; return;
#ifndef QT_NO_OPENGL QList<QObject*> children = cur->children();
// Having every repaint() leading to a sync/flush is bad as it causes for (int i = 0; i < children.size(); ++i) {
// compositing and waiting for vsync each and every time. Change to QWidget *child = qobject_cast<QWidget*>(children.at(i));
// UpdateLater, except for approx. once per frame to prevent starvation in if (!child || child->isWindow())
// case the control does not get back to the event loop. continue;
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) { updateLists(child);
case UpdateLater: }
updateRequestSent = true;
QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); if (cur->testAttribute(Qt::WA_StaticContents))
break; addStaticWidget(cur);
case UpdateNow: { }
QEvent event(QEvent::UpdateRequest);
QCoreApplication::sendEvent(widget, &event); QWidgetRepaintManager::~QWidgetRepaintManager()
break; {
} for (int c = 0; c < dirtyWidgets.size(); ++c)
resetWidget(dirtyWidgets.at(c));
for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
resetWidget(dirtyRenderToTextureWidgets.at(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);
static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; } static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; }
static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); } 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<QRect>(const QRect &, QWidget *, UpdateTime, BufferState);
template void QWidgetRepaintManager::markDirty<QRegion>(const QRegion &, QWidget *, UpdateTime, BufferState); template void QWidgetRepaintManager::markDirty<QRegion>(const QRegion &, QWidget *, UpdateTime, BufferState);
/*! void QWidgetRepaintManager::addDirtyWidget(QWidget *widget, const QRegion &rgn)
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 &region, QWidget *widget, const QPoint &topLevelOffset)
{ {
if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
return; QWidgetPrivate *widgetPrivate = widget->d_func();
#if QT_CONFIG(graphicseffect)
// Top-level. if (widgetPrivate->graphicsEffect)
if (widget == tlw) { widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect());
if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) else
dirtyOnScreen += region; #endif // QT_CONFIG(graphicseffect)
return; widgetPrivate->dirty = rgn;
dirtyWidgets.append(widget);
widgetPrivate->inDirtyList = true;
} }
// 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::removeDirtyWidget(QWidget *w) 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) if (widget) {
widget->d_func()->inDirtyList = false;
widget->d_func()->isScrolled = false;
widget->d_func()->isMoved = false;
widget->d_func()->dirty = QRegion();
}
}
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;
}
}
void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
{
if (!widget)
return; return;
QList<QObject*> children = cur->children(); #ifndef QT_NO_OPENGL
for (int i = 0; i < children.size(); ++i) { // Having every repaint() leading to a sync/flush is bad as it causes
QWidget *child = qobject_cast<QWidget*>(children.at(i)); // compositing and waiting for vsync each and every time. Change to
if (!child || child->isWindow()) // UpdateLater, except for approx. once per frame to prevent starvation in
continue; // case the control does not get back to the event loop.
QWidget *w = widget->window();
updateLists(child); 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 (cur->testAttribute(Qt::WA_StaticContents)) switch (updateTime) {
addStaticWidget(cur); 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(QWidget *topLevel) // ---------------------------------------------------------------------------
: tlw(topLevel),
updateRequestSent(0),
textureListWatcher(0),
perfFrames(0)
{
store = tlw->backingStore();
Q_ASSERT(store);
// Ensure all existing subsurfaces and static widgets are added to their respective lists. static bool hasPlatformWindow(QWidget *widget)
updateLists(topLevel);
}
QWidgetRepaintManager::~QWidgetRepaintManager()
{ {
for (int c = 0; c < dirtyWidgets.size(); ++c) return widget && widget->windowHandle() && widget->windowHandle()->handle();
resetWidget(dirtyWidgets.at(c));
for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
resetWidget(dirtyRenderToTextureWidgets.at(c));
} }
static QVector<QRect> getSortedRectsToScroll(const QRegion &region, int dx, int dy) static QVector<QRect> getSortedRectsToScroll(const QRegion &region, 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 #ifndef QT_NO_OPENGL
static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector<QWidget *> *nativeChildren) 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 #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. Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
@ -855,6 +762,37 @@ void QWidgetRepaintManager::sync()
paintAndFlush(); 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() void QWidgetRepaintManager::paintAndFlush()
{ {
const bool updatesDisabled = !tlw->updatesEnabled(); const bool updatesDisabled = !tlw->updatesEnabled();
@ -1073,6 +1011,60 @@ void QWidgetRepaintManager::paintAndFlush()
flush(); flush();
} }
/*!
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 &region, 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. 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. 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 &region, QPlatf
store->flush(effectiveRegion, widget->windowHandle(), offset); 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. Invalidates the backing store when the widget is resized.
Static areas are never invalidated unless absolutely needed. 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 QT_END_NAMESPACE
#include "moc_qwidgetrepaintmanager_p.cpp" #include "moc_qwidgetrepaintmanager_p.cpp"

View File

@ -98,9 +98,6 @@ public:
QWidgetRepaintManager(QWidget *t); QWidgetRepaintManager(QWidget *t);
~QWidgetRepaintManager(); ~QWidgetRepaintManager();
void sync(QWidget *exposedWidget, const QRegion &exposedRegion);
void sync();
QBackingStore *backingStore() const { return store; } QBackingStore *backingStore() const { return store; }
void setBackingStore(QBackingStore *backingStore) { store = backingStore; } void setBackingStore(QBackingStore *backingStore) { store = backingStore; }
@ -110,49 +107,44 @@ public:
void removeDirtyWidget(QWidget *w); void removeDirtyWidget(QWidget *w);
inline void addStaticWidget(QWidget *widget) void sync(QWidget *exposedWidget, const QRegion &exposedRegion);
{ void sync();
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);
}
QRegion staticContents(QWidget *widget = nullptr, const QRect &withinClipRect = QRect()) const;
void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); 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;
bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget);
private: 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 &region, 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; QWidget *tlw;
QRegion dirtyOnScreen; // needsFlush QRegion dirtyOnScreen; // needsFlush
QRegion dirty; // needsRepaint QRegion dirty; // needsRepaint
@ -168,92 +160,6 @@ private:
QElapsedTimer perfTime; QElapsedTimer perfTime;
int perfFrames; 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 &region, 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) Q_DISABLE_COPY_MOVE(QWidgetRepaintManager)
}; };