5#include "qplatformdefs.h"
9#include <QtCore/qglobal.h>
10#include <QtCore/qdebug.h>
11#include <QtCore/qvarlengtharray.h>
12#include <QtGui/qevent.h>
13#include <QtWidgets/qapplication.h>
14#include <QtGui/qpaintengine.h>
15#if QT_CONFIG(graphicsview)
16#include <QtWidgets/qgraphicsproxywidget.h>
19#include <private/qwidget_p.h>
20#include <private/qapplication_p.h>
21#include <private/qpaintengine_raster_p.h>
22#if QT_CONFIG(graphicseffect)
23#include <private/qgraphicseffect_p.h>
25#include <QtGui/private/qwindow_p.h>
26#include <QtGui/private/qhighdpiscaling_p.h>
28#include <qpa/qplatformbackingstore.h>
32Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
39class QPlatformTextureListWatcher :
public QObject
43 QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager)
44 : m_repaintManager(repaintManager) {}
46 void watch(QPlatformTextureList *textureList) {
47 connect(textureList, SIGNAL(locked(
bool)), SLOT(onLockStatusChanged(
bool)));
48 m_locked[textureList] = textureList->isLocked();
51 bool isLocked()
const {
52 for (
const auto &[_, v] : m_locked.asKeyValueRange()) {
60 void onLockStatusChanged(
bool locked) {
61 QPlatformTextureList *tl =
static_cast<QPlatformTextureList *>(sender());
62 m_locked[tl] = locked;
64 m_repaintManager->sync();
68 QHash<QPlatformTextureList *,
bool> m_locked;
69 QWidgetRepaintManager *m_repaintManager;
74QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel)
75 : tlw(topLevel), store(tlw->backingStore())
80 updateLists(topLevel);
83void QWidgetRepaintManager::updateLists(QWidget *cur)
88 QList<QObject*> children = cur->children();
89 for (
int i = 0; i < children.size(); ++i) {
90 QWidget *child = qobject_cast<QWidget*>(children.at(i));
91 if (!child || child->isWindow())
97 if (cur->testAttribute(Qt::WA_StaticContents))
101QWidgetRepaintManager::~QWidgetRepaintManager()
103 for (
int c = 0; c < dirtyWidgets.size(); ++c)
104 resetWidget(dirtyWidgets.at(c));
105 for (
int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
106 resetWidget(dirtyRenderToTextureWidgets.at(c));
110
111
112
113
114
116void QWidgetPrivate::invalidateBackingStore(
const T &r)
121 if (QCoreApplication::closingDown())
125 if (!q->isVisible() || !q->updatesEnabled())
128 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
129 if (!tlwExtra || !tlwExtra->backingStore || !tlwExtra->repaintManager)
133 clipped &= clipRect();
134 if (clipped.isEmpty())
137 if (!graphicsEffect && extra && extra->hasMask) {
138 QRegion masked(extra->mask);
140 if (masked.isEmpty())
143 tlwExtra->repaintManager->markDirty(masked, q,
144 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
146 tlwExtra->repaintManager->markDirty(clipped, q,
147 QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid);
151template Q_AUTOTEST_EXPORT
void QWidgetPrivate::invalidateBackingStore<QRect>(
const QRect &r);
157
158
159
160
161
162
163
164
165
166
167
169void QWidgetRepaintManager::markDirty(
const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState)
171 qCInfo(lcWidgetPainting) <<
"Marking" << r <<
"of" << widget <<
"dirty"
172 <<
"with" << updateTime;
174 Q_ASSERT(tlw->d_func()->extra);
175 Q_ASSERT(tlw->d_func()->extra->topextra);
176 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
177 Q_ASSERT(widget->window() == tlw);
178 Q_ASSERT(!r.isEmpty());
180#if QT_CONFIG(graphicseffect)
181 widget->d_func()->invalidateGraphicsEffectsRecursively();
184 QRect widgetRect = widgetRectFor(widget, r);
188 if (widget->d_func()->shouldPaintOnScreen()) {
189 if (widget->d_func()->dirty.isEmpty()) {
190 widget->d_func()->dirty = r;
191 sendUpdateRequest(widget, updateTime);
193 }
else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
194 if (updateTime == UpdateNow)
195 sendUpdateRequest(widget, updateTime);
199 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
200 widget->d_func()->dirty += r;
201 if (!eventAlreadyPosted || updateTime == UpdateNow)
202 sendUpdateRequest(widget, updateTime);
208 if (QWidgetPrivate::get(widget)->renderToTexture) {
209 if (!widget->d_func()->inDirtyList)
210 addDirtyRenderToTextureWidget(widget);
211 if (!updateRequestSent || updateTime == UpdateNow)
212 sendUpdateRequest(tlw, updateTime);
218 QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect);
219 const QPoint offset = widget->mapTo(tlw, QPoint());
220 QRect translatedRect = effectiveWidgetRect.translated(offset);
221#if QT_CONFIG(graphicseffect)
223 translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
225 if (qt_region_strictContains(dirty, translatedRect)) {
226 if (updateTime == UpdateNow)
227 sendUpdateRequest(tlw, updateTime);
233 if (bufferState == BufferInvalid) {
234 const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent;
235#if QT_CONFIG(graphicseffect)
236 if (widget->d_func()->graphicsEffect)
237 dirty += widget->d_func()->effectiveRectFor(r).translated(offset);
240 dirty += r.translated(offset);
242 if (!eventAlreadyPosted || updateTime == UpdateNow)
243 sendUpdateRequest(tlw, updateTime);
249 if (dirtyWidgets.isEmpty()) {
250 addDirtyWidget(widget, r);
251 sendUpdateRequest(tlw, updateTime);
257 if (widget->d_func()->inDirtyList) {
258 if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) {
259#if QT_CONFIG(graphicseffect)
260 if (widget->d_func()->graphicsEffect)
261 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r);
264 widget->d_func()->dirty += r;
267 addDirtyWidget(widget, r);
272 if (updateTime == UpdateNow)
273 sendUpdateRequest(tlw, updateTime);
275template void QWidgetRepaintManager::markDirty<QRect>(
const QRect &, QWidget *, UpdateTime, BufferState);
276template void QWidgetRepaintManager::markDirty<QRegion>(
const QRegion &, QWidget *, UpdateTime, BufferState);
278void QWidgetRepaintManager::addDirtyWidget(QWidget *widget,
const QRegion &rgn)
280 if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
281 QWidgetPrivate *widgetPrivate = widget->d_func();
282#if QT_CONFIG(graphicseffect)
283 if (widgetPrivate->graphicsEffect)
284 widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect());
287 widgetPrivate->dirty = rgn;
288 dirtyWidgets.append(widget);
289 widgetPrivate->inDirtyList =
true;
293void QWidgetRepaintManager::removeDirtyWidget(QWidget *w)
298 dirtyWidgets.removeAll(w);
299 dirtyRenderToTextureWidgets.removeAll(w);
302 needsFlushWidgets.removeAll(w);
304 QWidgetPrivate *wd = w->d_func();
305 const int n = wd->children.size();
306 for (
int i = 0; i < n; ++i) {
307 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
308 removeDirtyWidget(child);
312void QWidgetRepaintManager::resetWidget(QWidget *widget)
315 widget->d_func()->inDirtyList =
false;
316 widget->d_func()->isScrolled =
false;
317 widget->d_func()->isMoved =
false;
318 widget->d_func()->dirty = QRegion();
322void QWidgetRepaintManager::addDirtyRenderToTextureWidget(QWidget *widget)
324 if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) {
325 QWidgetPrivate *widgetPrivate = widget->d_func();
326 Q_ASSERT(widgetPrivate->renderToTexture);
327 dirtyRenderToTextureWidgets.append(widget);
328 widgetPrivate->inDirtyList =
true;
332void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
337 qCInfo(lcWidgetPainting) <<
"Sending update request to" << widget <<
"with" << updateTime;
343 if (updateTime == UpdateNow && QWidgetPrivate::get(widget)->textureChildSeen) {
345 QWidget *w = widget->window();
346 QScreen *ws = w->windowHandle()->screen();
348 refresh = ws->refreshRate();
349 QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle());
350 if (wd->lastComposeTime.isValid()) {
351 const qint64 elapsed = wd->lastComposeTime.elapsed();
352 if (elapsed <= qint64(1000.0f / refresh))
353 updateTime = UpdateLater;
357 switch (updateTime) {
362 if (!widget->d_func()->shouldPaintOnScreen())
363 updateRequestSent =
true;
364 QCoreApplication::postEvent(widget,
new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
367 QEvent event(QEvent::UpdateRequest);
368 QCoreApplication::sendEvent(widget, &event);
378 return widget && widget->windowHandle() && widget->windowHandle()->handle();
383void QWidgetPrivate::moveRect(
const QRect &rect,
int dx,
int dy)
386 if (!q->isVisible() || (dx == 0 && dy == 0))
389 QWidget *tlw = q->window();
391 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_MOVE") == 0;
393 QWidget *parentWidget = q->parentWidget();
394 QPoint toplevelOffset = parentWidget->mapTo(tlw, QPoint());
395 QWidgetPrivate *parentPrivate = parentWidget->d_func();
396 const QRect clipR(parentPrivate->clipRect());
397 const QRect newRect(rect.translated(dx, dy));
398 QRect destRect = rect.intersected(clipR);
399 if (destRect.isValid())
400 destRect = destRect.translated(dx, dy).intersected(clipR);
401 const QRect sourceRect(destRect.translated(-dx, -dy));
402 const QRect parentRect(rect & clipR);
403 const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q);
405 bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild && sourceRect.isValid()
406#if QT_CONFIG(graphicsview)
408 && !tlw->d_func()->extra->proxyWidget
410 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
412 if (!accelerateMove) {
413 QRegion parentRegion(effectiveRectFor(parentRect));
414 if (!extra || !extra->hasMask) {
415 parentRegion -= newRect;
418 parentRegion += newRect & clipR;
420 parentPrivate->invalidateBackingStore(parentRegion);
421 invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
423 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
424 Q_ASSERT(repaintManager);
425 QRegion childExpose(newRect & clipR);
427 if (repaintManager->bltRect(sourceRect, dx, dy, parentWidget))
428 childExpose -= destRect;
430 if (!parentWidget->updatesEnabled())
433 const bool childUpdatesEnabled = q->updatesEnabled();
435 if (childUpdatesEnabled && !childExpose.isEmpty()) {
436 childExpose.translate(-data.crect.topLeft());
437 repaintManager->markDirty(childExpose, q);
441 QRegion parentExpose(parentRect);
442 parentExpose -= newRect;
443 if (extra && extra->hasMask)
444 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
446 if (!parentExpose.isEmpty()) {
447 repaintManager->markDirty(parentExpose, parentWidget);
448 parentPrivate->isMoved =
true;
451 if (childUpdatesEnabled) {
452 QRegion needsFlush(sourceRect);
453 needsFlush += destRect;
454 repaintManager->markNeedsFlush(parentWidget, needsFlush, toplevelOffset);
460void QWidgetPrivate::scrollRect(
const QRect &rect,
int dx,
int dy)
463 QWidget *tlw = q->window();
465 QWidgetRepaintManager *repaintManager = QWidgetPrivate::get(tlw)->maybeRepaintManager();
469 static const bool accelEnv = qEnvironmentVariableIntValue(
"QT_NO_FAST_SCROLL") == 0;
471 const QRect scrollRect = rect & clipRect();
472 bool overlapped =
false;
473 bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent)
474 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
476 if (!accelerateScroll) {
478 QRegion region(scrollRect);
479 subtractOpaqueSiblings(region);
480 invalidateBackingStore(region);
482 invalidateBackingStore(scrollRect);
485 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
486 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
487 const QRect sourceRect = destRect.translated(-dx, -dy);
489 QRegion childExpose(scrollRect);
490 if (sourceRect.isValid()) {
491 if (repaintManager->bltRect(sourceRect, dx, dy, q))
492 childExpose -= destRect;
496 if (rect == q->rect()) {
497 dirty.translate(dx, dy);
499 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
500 if (!dirtyScrollRegion.isEmpty()) {
501 dirty -= dirtyScrollRegion;
502 dirtyScrollRegion.translate(dx, dy);
503 dirty += dirtyScrollRegion;
508 if (!q->updatesEnabled())
511 if (!childExpose.isEmpty()) {
512 repaintManager->markDirty(childExpose, q);
519 repaintManager->markNeedsFlush(q, destRect, toplevelOffset);
524
525
526
527bool QWidgetRepaintManager::bltRect(
const QRect &rect,
int dx,
int dy, QWidget *widget)
529 const QPoint pos(widget->mapTo(tlw, rect.topLeft()));
530 const QRect tlwRect(QRect(pos, rect.size()));
531 if (dirty.intersects(tlwRect))
533 return store->scroll(tlwRect, dx, dy);
539 QPlatformTextureList *widgetTextures,
540 QList<QWidget *> *nativeChildren)
542 QWidgetPrivate *wd = QWidgetPrivate::get(widget);
543 if (wd->renderToTexture) {
544 QPlatformTextureList::Flags flags = wd->textureListFlags();
545 const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
546 QWidgetPrivate::TextureData data = wd->texture();
547 widgetTextures->appendTexture(widget, data.textureLeft, data.textureRight, rect, wd->clipRect(), flags);
550 for (
int i = 0; i < wd->children.size(); ++i) {
551 QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
553 if (w && !w->isWindow() && hasPlatformWindow(w))
554 nativeChildren->append(w);
555 if (w && !w->isWindow() && !hasPlatformWindow(w) && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
556 findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren);
563 if (QWidgetPrivate::get(widget)->textureChildSeen) {
564 QList<QWidget *> nativeChildren;
565 auto tl = std::make_unique<QPlatformTextureList>();
568 findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren);
571 QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl));
573 for (QWidget *ncw : std::as_const(nativeChildren)) {
574 if (QWidgetPrivate::get(ncw)->textureChildSeen)
575 findAllTextureWidgetsRecursively(tlw, ncw);
582 for (
const auto &tl : QWidgetPrivate::get(tlw)->topData()->widgetTextures) {
583 Q_ASSERT(!tl->isEmpty());
584 for (
int i = 0; i < tl->count(); ++i) {
585 QWidget *w =
static_cast<QWidget *>(tl->source(i));
586 if ((hasPlatformWindow(w) && w == widget) || (!hasPlatformWindow(w) && w->nativeParentWidget() == widget))
591 if (QWidgetPrivate::get(widget)->textureChildSeen)
592 return qt_dummy_platformTextureList();
600
601
602
603
604
605
606void QWidgetRepaintManager::sync(QWidget *exposedWidget,
const QRegion &exposedRegion)
608 qCInfo(lcWidgetPainting) <<
"Syncing" << exposedRegion <<
"of" << exposedWidget;
610 if (!tlw->isVisible())
613 if (!exposedWidget || !hasPlatformWindow(exposedWidget)
614 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
615 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
620 if (!isDirty() && store->size().isValid()) {
621 QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, exposedWidget);
622 flush(exposedWidget, widgetTextures ? QRegion() : exposedRegion, widgetTextures);
629 QPoint offset = exposedWidget != tlw ? exposedWidget->mapTo(tlw, QPoint()) : QPoint();
630 markNeedsFlush(exposedWidget, exposedRegion, offset);
637
638
639void QWidgetRepaintManager::sync()
641 qCInfo(lcWidgetPainting) <<
"Syncing dirty widgets";
643 updateRequestSent =
false;
644 if (qt_widget_private(tlw)->shouldDiscardSyncRequest()) {
650 if (!tlw->isVisible()) {
652 for (
int i = 0; i < dirtyWidgets.size(); ++i)
653 resetWidget(dirtyWidgets.at(i));
654 dirtyWidgets.clear();
663bool QWidgetPrivate::shouldDiscardSyncRequest()
const
666 return !maybeTopData() || !q->testAttribute(Qt::WA_Mapped) || !q->isVisible();
669bool QWidgetRepaintManager::syncAllowed()
671 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
672 if (textureListWatcher && !textureListWatcher->isLocked()) {
673 textureListWatcher->deleteLater();
674 textureListWatcher =
nullptr;
675 }
else if (!tlwExtra->widgetTextures.empty()) {
676 bool skipSync =
false;
677 for (
const auto &tl : tlwExtra->widgetTextures) {
678 if (tl->isLocked()) {
679 if (!textureListWatcher)
680 textureListWatcher =
new QPlatformTextureListWatcher(
this);
681 if (!textureListWatcher->isLocked())
682 textureListWatcher->watch(tl.get());
694#if QT_CONFIG(graphicseffect)
696 if (w->graphicsEffect())
698 w = w->parentWidget();
704void QWidgetRepaintManager::paintAndFlush()
706 qCInfo(lcWidgetPainting) <<
"Painting and flushing dirty"
707 <<
"top level" << dirty <<
"and dirty widgets" << dirtyWidgets;
709 const bool updatesDisabled = !tlw->updatesEnabled();
710 bool repaintAllWidgets =
false;
712 const QRect tlwRect = tlw->data->crect;
713 if (!updatesDisabled && store->size() != tlwRect.size()) {
714 QPlatformIntegration *integration = QGuiApplicationPrivate::platformIntegration();
715 if (hasStaticContents() && !store->size().isEmpty()
716 && integration->hasCapability(QPlatformIntegration::BackingStoreStaticContents)) {
718 const QRect clipRect(QPoint(0, 0), store->size());
719 const QRegion staticRegion(staticContents(
nullptr, clipRect));
720 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
721 newVisible -= staticRegion;
723 store->setStaticContents(staticRegion);
726 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
727 for (
int i = 0; i < dirtyWidgets.size(); ++i)
728 resetWidget(dirtyWidgets.at(i));
729 dirtyWidgets.clear();
730 repaintAllWidgets =
true;
734 if (store->size() != tlwRect.size())
735 store->resize(tlwRect.size());
741 QRegion toClean(dirty);
747 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
748 for (
int i = 0; i < dirtyWidgets.size(); ++i) {
749 QWidget *w = dirtyWidgets.at(i);
750 QWidgetPrivate *wd = w->d_func();
751 if (wd->data.in_destructor)
755 wd->dirty &= wd->clipRect();
756 wd->clipToEffectiveMask(wd->dirty);
759 bool hasDirtySiblingsAbove =
false;
762 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
768 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
771 if (!wd->isScrolled && !wd->isMoved)
772 wd->subtractOpaqueChildren(wd->dirty, w->rect());
774 if (wd->dirty.isEmpty() && wd->textureChildSeen)
775 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
777 if (wd->dirty.isEmpty()) {
782 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
784 toClean += widgetDirty;
786#if QT_CONFIG(graphicsview)
787 if (tlw->d_func()->extra->proxyWidget) {
793 if (!isDrawnInEffect(w) && !hasDirtySiblingsAbove && wd->isOpaque
794 && !dirty.intersects(widgetDirty.boundingRect())) {
795 opaqueNonOverlappedWidgets.append(w);
798 dirty += widgetDirty;
801 dirtyWidgets.clear();
806 QTLWExtra *tlwExtra = tlw->d_func()->topData();
807 tlwExtra->widgetTextures.clear();
808 findAllTextureWidgetsRecursively(tlw, tlw);
810 if (toClean.isEmpty()) {
816 QVarLengthArray<QWidget *, 16> paintPending;
817 const int numPaintPending = dirtyRenderToTextureWidgets.size();
818 paintPending.reserve(numPaintPending);
819 for (
int i = 0; i < numPaintPending; ++i) {
820 QWidget *w = dirtyRenderToTextureWidgets.at(i);
824 dirtyRenderToTextureWidgets.clear();
825 for (
int i = 0; i < numPaintPending; ++i) {
826 QWidget *w = paintPending[i];
827 w->d_func()->sendPaintEvent(w->rect());
829 QWidget *npw = w->nativeParentWidget();
830 if (hasPlatformWindow(w) || (npw && npw != tlw)) {
831 if (!hasPlatformWindow(w))
846 for (
const auto &tl : tlwExtra->widgetTextures) {
847 for (
int i = 0; i < tl->count(); ++i) {
848 QWidget *w =
static_cast<QWidget *>(tl->source(i));
849 if (dirtyRenderToTextureWidgets.contains(w)) {
850 const QRect rect = tl->geometry(i);
853 w->d_func()->renderToTextureReallyDirty = 1;
859 for (
int i = 0; i < dirtyRenderToTextureWidgets.size(); ++i)
860 resetWidget(dirtyRenderToTextureWidgets.at(i));
861 dirtyRenderToTextureWidgets.clear();
863#if QT_CONFIG(graphicsview)
864 if (tlw->d_func()->extra->proxyWidget) {
865 updateStaticContentsSize();
867 updateRequestSent =
false;
868 for (
const QRect &rect : toClean)
869 tlw->d_func()->extra->proxyWidget->update(rect);
874 store->beginPaint(toClean);
878 updateStaticContentsSize();
879 const QRegion dirtyCopy(dirty);
881 updateRequestSent =
false;
884 for (
int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
885 QWidget *w = opaqueNonOverlappedWidgets[i];
886 QWidgetPrivate *wd = w->d_func();
888 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawRecursive;
890 if (!wd->isScrolled && !wd->isMoved)
891 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
893 flags |= QWidgetPrivate::DrawAsRoot;
895 QRegion toBePainted(wd->dirty);
900 offset += w->mapTo(tlw, QPoint());
901 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags,
nullptr,
this);
905 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
906 QWidgetPrivate::DrawWidgetFlags flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive
907 | QWidgetPrivate::UseEffectRegionBounds;
908 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags,
nullptr,
this);
917
918
919
920
921
922void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion,
const QPoint &topLevelOffset)
924 if (!widget || widget->d_func()->shouldPaintOnScreen() || region.isEmpty())
929 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of top level"
930 << widget <<
"as needing flush";
931 topLevelNeedsFlush += region;
932 }
else if (!hasPlatformWindow(widget) && !widget->isWindow()) {
933 QWidget *nativeParent = widget->nativeParentWidget();
934 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of"
935 << widget <<
"as needing flush in" << nativeParent
936 <<
"at offset" << topLevelOffset;
937 if (nativeParent == tlw) {
939 topLevelNeedsFlush += region.translated(topLevelOffset);
942 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
943 markNeedsFlush(nativeParent, region.translated(nativeParentOffset));
947 qCInfo(lcWidgetPainting) <<
"Marking" << region
948 <<
"of native child" << widget <<
"as needing flush";
949 markNeedsFlush(widget, region);
953void QWidgetRepaintManager::markNeedsFlush(QWidget *widget,
const QRegion ®ion)
958 auto *widgetPrivate = qt_widget_private(widget);
959 if (!widgetPrivate->needsFlush)
960 widgetPrivate->needsFlush =
new QRegion;
962 *widgetPrivate->needsFlush += region;
964 if (!needsFlushWidgets.contains(widget))
965 needsFlushWidgets.append(widget);
969
970
971void QWidgetRepaintManager::flush()
973 qCInfo(lcWidgetPainting) <<
"Flushing top level"
974 << topLevelNeedsFlush <<
"and children" << needsFlushWidgets;
976 const bool hasNeedsFlushWidgets = !needsFlushWidgets.isEmpty();
977 bool flushed =
false;
980 if (!topLevelNeedsFlush.isEmpty()) {
981 flush(tlw, topLevelNeedsFlush, widgetTexturesFor(tlw, tlw));
982 topLevelNeedsFlush = QRegion();
987 if (!flushed && !hasNeedsFlushWidgets) {
988 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
989 if (QPlatformTextureList *widgetTextures = widgetTexturesFor(tlw, tlw))
990 flush(tlw, QRegion(), widgetTextures);
994 if (!hasNeedsFlushWidgets)
997 for (QWidget *w : std::exchange(needsFlushWidgets, {})) {
998 QWidgetPrivate *wd = w->d_func();
999 Q_ASSERT(wd->needsFlush);
1000 QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) :
nullptr;
1001 flush(w, *wd->needsFlush, widgetTexturesForNative);
1002 *wd->needsFlush = QRegion();
1007
1008
1009
1010
1011void QWidgetRepaintManager::flush(QWidget *widget,
const QRegion ®ion, QPlatformTextureList *widgetTextures)
1013 Q_ASSERT(!region.isEmpty() || widgetTextures);
1017 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
1020 QWindow *window = widget->windowHandle();
1025 if (window->type() == Qt::ForeignWindow)
1028 static bool fpsDebug = qEnvironmentVariableIntValue(
"QT_DEBUG_FPS");
1032 if (perfTime.elapsed() > 5000) {
1033 double fps =
double(perfFrames * 1000) / perfTime.restart();
1034 qDebug(
"FPS: %.1f\n", fps);
1041 offset += widget->mapTo(tlw, QPoint());
1046 const bool flushWithRhi = widget->d_func()->usesRhiFlush;
1048 qCDebug(lcWidgetPainting) <<
"Flushing" << region <<
"of" << widget
1049 <<
"to" << window << (flushWithRhi ?
"using RHI" :
"");
1055 if (!widgetTextures)
1056 widgetTextures = qt_dummy_platformTextureList;
1060 auto *widgetPrivate = QWidgetPrivate::get(widget);
1061 widgetPrivate->sendComposeStatus(widget,
false);
1066 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
1068 QPlatformBackingStore::FlushResult flushResult;
1069 flushResult = store->handle()->rhiFlush(window,
1070 widget->devicePixelRatio(),
1074 translucentBackground);
1076 widgetPrivate->sendComposeStatus(widget,
true);
1078 if (flushResult == QPlatformBackingStore::FlushFailedDueToLostDevice) {
1079 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1080 QEvent::WindowAboutToChangeInternal);
1081 store->handle()->graphicsDeviceReportedLost(window);
1082 qSendWindowChangeToTextureChildrenRecursively(widget->window(),
1083 QEvent::WindowChangeInternal);
1087 store->flush(region, window, offset);
1093void QWidgetRepaintManager::addStaticWidget(QWidget *widget)
1098 Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents));
1099 if (!staticWidgets.contains(widget))
1100 staticWidgets.append(widget);
1105void QWidgetRepaintManager::moveStaticWidgets(QWidget *reparented)
1107 Q_ASSERT(reparented);
1108 QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager();
1109 if (newPaintManager ==
this)
1113 while (i < staticWidgets.size()) {
1114 QWidget *w = staticWidgets.at(i);
1115 if (reparented == w || reparented->isAncestorOf(w)) {
1116 staticWidgets.removeAt(i);
1117 if (newPaintManager)
1118 newPaintManager->addStaticWidget(w);
1125void QWidgetRepaintManager::removeStaticWidget(QWidget *widget)
1127 staticWidgets.removeAll(widget);
1130bool QWidgetRepaintManager::hasStaticContents()
const
1132 return !staticWidgets.isEmpty();
1136
1137
1138
1139
1140QRegion QWidgetRepaintManager::staticContents(QWidget *parent,
const QRect &withinClipRect)
const
1142 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
1143 QRect backingstoreRect(QPoint(0, 0), store->size());
1144 if (!withinClipRect.isEmpty())
1145 backingstoreRect &= withinClipRect;
1146 return QRegion(backingstoreRect);
1150 if (parent && parent->d_func()->children.isEmpty())
1153 const bool clipToRect = !withinClipRect.isEmpty();
1154 const int count = staticWidgets.size();
1155 for (
int i = 0; i < count; ++i) {
1156 QWidget *w = staticWidgets.at(i);
1157 QWidgetPrivate *wd = w->d_func();
1158 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
1159 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
1163 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
1164 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
1166 rect &= withinClipRect.translated(-offset);
1170 rect &= wd->clipRect();
1174 QRegion visible(rect);
1175 wd->clipToEffectiveMask(visible);
1176 if (visible.isEmpty())
1178 wd->subtractOpaqueSiblings(visible,
nullptr,
true);
1180 visible.translate(offset);
1187void QWidgetRepaintManager::updateStaticContentsSize()
1189 for (
int i = 0; i < staticWidgets.size(); ++i) {
1190 QWidgetPrivate *wd = staticWidgets.at(i)->d_func();
1193 wd->extra->staticContentsSize = wd->data.crect.size();
1199bool QWidgetRepaintManager::isDirty()
const
1201 return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty());
1205
1206
1207
1208void QWidgetPrivate::invalidateBackingStore_resizeHelper(
const QPoint &oldPos,
const QSize &oldSize)
1211 Q_ASSERT(!q->isWindow());
1212 Q_ASSERT(q->parentWidget());
1214 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1215 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1216 || (data.crect.height() < oldSize.height());
1218 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1219 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1220 const QRect newWidgetRect(q->rect());
1221 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1223 if (!staticContents || graphicsEffect) {
1224 QRegion staticChildren;
1225 QWidgetRepaintManager *bs =
nullptr;
1226 if (offset.isNull() && (bs = maybeRepaintManager()))
1227 staticChildren = bs->staticContents(q, oldWidgetRect);
1228 const bool hasStaticChildren = !staticChildren.isEmpty();
1230 if (hasStaticChildren) {
1231 QRegion dirty(newWidgetRect);
1232 dirty -= staticChildren;
1233 invalidateBackingStore(dirty);
1236 invalidateBackingStore(newWidgetRect);
1239 if (!parentAreaExposed)
1243 if (!graphicsEffect && extra && extra->hasMask) {
1244 QRegion parentExpose(extra->mask.translated(oldPos));
1245 parentExpose &= QRect(oldPos, oldSize);
1246 if (hasStaticChildren)
1247 parentExpose -= data.crect;
1248 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1250 if (hasStaticChildren && !graphicsEffect) {
1251 QRegion parentExpose(QRect(oldPos, oldSize));
1252 parentExpose -= data.crect;
1253 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1255 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
1262 if (!offset.isNull()) {
1263 if (sizeDecreased) {
1264 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1265 qMin(oldSize.height(), data.crect.height()));
1266 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1268 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1273 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1274 QRegion newVisible(newWidgetRect);
1275 newVisible -= oldWidgetRect;
1276 invalidateBackingStore(newVisible);
1279 if (!parentAreaExposed)
1283 const QRect oldRect(oldPos, oldSize);
1284 if (extra && extra->hasMask) {
1285 QRegion parentExpose(oldRect);
1286 parentExpose &= extra->mask.translated(oldPos);
1287 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1288 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1290 QRegion parentExpose(oldRect);
1291 parentExpose -= data.crect;
1292 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1298#include "qwidgetrepaintmanager.moc"
1299#include "moc_qwidgetrepaintmanager_p.cpp"