4#include "qplatformdefs.h"
8#include <QtCore/qglobal.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/qvarlengtharray.h>
11#include <QtGui/qevent.h>
12#include <QtWidgets/qapplication.h>
13#include <QtGui/qpaintengine.h>
14#if QT_CONFIG(graphicsview)
15#include <QtWidgets/qgraphicsproxywidget.h>
18#include <private/qwidget_p.h>
19#include <private/qapplication_p.h>
20#include <private/qpaintengine_raster_p.h>
21#if QT_CONFIG(graphicseffect)
22#include <private/qgraphicseffect_p.h>
24#include <QtGui/private/qwindow_p.h>
25#include <QtGui/private/qhighdpiscaling_p.h>
27#include <qpa/qplatformbackingstore.h>
43 : m_repaintManager(repaintManager) {}
47 m_locked[textureList] = textureList->isLocked();
51 for (
const auto &[_,
v] : m_locked.asKeyValueRange()) {
59 void onLockStatusChanged(
bool locked) {
61 m_locked[tl] = locked;
63 m_repaintManager->sync();
67 QHash<QPlatformTextureList *, bool> m_locked;
74 : tlw(topLevel), store(tlw->backingStore())
79 updateLists(topLevel);
82void QWidgetRepaintManager::updateLists(
QWidget *cur)
87 QList<QObject*> children = cur->children();
88 for (
int i = 0;
i < children.size(); ++
i) {
102 for (
int c = 0;
c < dirtyWidgets.
size(); ++
c)
103 resetWidget(dirtyWidgets.
at(
c));
104 for (
int c = 0;
c < dirtyRenderToTextureWidgets.
size(); ++
c)
105 resetWidget(dirtyRenderToTextureWidgets.
at(
c));
124 if (!
q->isVisible() || !
q->updatesEnabled())
128 if (!tlwExtra || !tlwExtra->backingStore || !tlwExtra->repaintManager)
133 if (clipped.isEmpty())
139 if (masked.isEmpty())
142 tlwExtra->repaintManager->markDirty(masked,
q,
145 tlwExtra->repaintManager->markDirty(clipped,
q,
170 qCInfo(lcWidgetPainting) <<
"Marking" <<
r <<
"of" <<
widget <<
"dirty"
171 <<
"with" << updateTime;
174 Q_ASSERT(tlw->d_func()->extra->topextra);
179#if QT_CONFIG(graphicseffect)
180 widget->d_func()->invalidateGraphicsEffectsRecursively();
187 if (
widget->d_func()->shouldPaintOnScreen()) {
188 if (
widget->d_func()->dirty.isEmpty()) {
190 sendUpdateRequest(
widget, updateTime);
192 }
else if (qt_region_strictContains(
widget->d_func()->dirty, widgetRect)) {
194 sendUpdateRequest(
widget, updateTime);
198 const bool eventAlreadyPosted = !
widget->d_func()->dirty.isEmpty();
200 if (!eventAlreadyPosted || updateTime ==
UpdateNow)
201 sendUpdateRequest(
widget, updateTime);
208 if (!
widget->d_func()->inDirtyList)
209 addDirtyRenderToTextureWidget(
widget);
210 if (!updateRequestSent || updateTime ==
UpdateNow)
211 sendUpdateRequest(tlw, updateTime);
217 QRect effectiveWidgetRect =
widget->d_func()->effectiveRectFor(widgetRect);
220#if QT_CONFIG(graphicseffect)
222 translatedRect = translatedRect.intersected(
QRect(
QPoint(), tlw->
size()));
224 if (qt_region_strictContains(dirty, translatedRect)) {
226 sendUpdateRequest(tlw, updateTime);
233 const bool eventAlreadyPosted = !dirty.
isEmpty() || updateRequestSent;
234#if QT_CONFIG(graphicseffect)
235 if (
widget->d_func()->graphicsEffect)
236 dirty +=
widget->d_func()->effectiveRectFor(
r).translated(
offset);
241 if (!eventAlreadyPosted || updateTime ==
UpdateNow)
242 sendUpdateRequest(tlw, updateTime);
250 sendUpdateRequest(tlw, updateTime);
256 if (
widget->d_func()->inDirtyList) {
257 if (!qt_region_strictContains(
widget->d_func()->dirty, effectiveWidgetRect)) {
258#if QT_CONFIG(graphicseffect)
259 if (
widget->d_func()->graphicsEffect)
260 widget->d_func()->dirty +=
widget->d_func()->effectiveRectFor(
r);
272 sendUpdateRequest(tlw, updateTime);
281#if QT_CONFIG(graphicseffect)
282 if (widgetPrivate->graphicsEffect)
283 widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.
boundingRect());
286 widgetPrivate->dirty = rgn;
288 widgetPrivate->inDirtyList =
true;
305 for (
int i = 0;
i <
n; ++
i) {
314 widget->d_func()->inDirtyList =
false;
315 widget->d_func()->isScrolled =
false;
316 widget->d_func()->isMoved =
false;
321void QWidgetRepaintManager::addDirtyRenderToTextureWidget(
QWidget *
widget)
325 Q_ASSERT(widgetPrivate->renderToTexture);
327 widgetPrivate->inDirtyList =
true;
331void QWidgetRepaintManager::sendUpdateRequest(
QWidget *
widget, UpdateTime updateTime)
336 qCInfo(lcWidgetPainting) <<
"Sending update request to" <<
widget <<
"with" << updateTime;
347 refresh =
ws->refreshRate();
356 switch (updateTime) {
361 if (!
widget->d_func()->shouldPaintOnScreen())
362 updateRequestSent =
true;
385 if (!
q->isVisible() || (dx == 0 && dy == 0))
395 const QRect clipR(parentPrivate->clipRect());
396 const QRect newRect(
rect.translated(dx, dy));
398 if (destRect.isValid())
399 destRect = destRect.
translated(dx, dy).intersected(clipR);
400 const QRect sourceRect(destRect.translated(-dx, -dy));
404 bool accelerateMove = accelEnv &&
isOpaque && !nativeWithTextureChild && sourceRect.
isValid()
405#if QT_CONFIG(graphicsview)
407 && !tlw->d_func()->extra->proxyWidget
411 if (!accelerateMove) {
414 parentRegion -= newRect;
417 parentRegion += newRect & clipR;
419 parentPrivate->invalidateBackingStore(parentRegion);
424 QRegion childExpose(newRect & clipR);
426 if (repaintManager->
bltRect(sourceRect, dx, dy, parentWidget))
427 childExpose -= destRect;
432 const bool childUpdatesEnabled =
q->updatesEnabled();
434 if (childUpdatesEnabled && !childExpose.isEmpty()) {
435 childExpose.translate(-
data.crect.topLeft());
440 QRegion parentExpose(parentRect);
441 parentExpose -= newRect;
445 if (!parentExpose.isEmpty()) {
446 repaintManager->
markDirty(parentExpose, parentWidget);
447 parentPrivate->isMoved =
true;
450 if (childUpdatesEnabled) {
471 bool overlapped =
false;
475 if (!accelerateScroll) {
490 if (repaintManager->
bltRect(sourceRect, dx, dy,
q))
491 childExpose -= destRect;
495 if (
rect ==
q->rect()) {
499 if (!dirtyScrollRegion.isEmpty()) {
500 dirty -= dirtyScrollRegion;
502 dirty += dirtyScrollRegion;
507 if (!
q->updatesEnabled())
510 if (!childExpose.isEmpty()) {
532 return store->
scroll(tlwRect, dx, dy);
539 QList<QWidget *> *nativeChildren)
553 nativeChildren->append(
w);
563 QList<QWidget *> nativeChildren;
564 auto tl = std::make_unique<QPlatformTextureList>();
572 for (
QWidget *ncw : std::as_const(nativeChildren)) {
583 for (
int i = 0;
i < tl->
count(); ++
i) {
591 return qt_dummy_platformTextureList();
607 qCInfo(lcWidgetPainting) <<
"Syncing" << exposedRegion <<
"of" << exposedWidget;
613 || !exposedWidget->isVisible() || !exposedWidget->testAttribute(
Qt::WA_Mapped)
614 || !exposedWidget->updatesEnabled() || exposedRegion.
isEmpty()) {
621 flush(exposedWidget, widgetTextures ?
QRegion() : exposedRegion, widgetTextures);
640 qCInfo(lcWidgetPainting) <<
"Syncing dirty widgets";
642 updateRequestSent =
false;
651 for (
int i = 0;
i < dirtyWidgets.
size(); ++
i)
652 resetWidget(dirtyWidgets.
at(
i));
653 dirtyWidgets.
clear();
668bool QWidgetRepaintManager::syncAllowed()
670 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
671 if (textureListWatcher && !textureListWatcher->
isLocked()) {
673 textureListWatcher =
nullptr;
674 }
else if (!tlwExtra->widgetTextures.empty()) {
675 bool skipSync =
false;
676 for (
const auto &tl : tlwExtra->widgetTextures) {
678 if (!textureListWatcher)
680 if (!textureListWatcher->
isLocked())
681 textureListWatcher->
watch(tl.get());
693#if QT_CONFIG(graphicseffect)
695 if (
w->graphicsEffect())
697 w =
w->parentWidget();
703void QWidgetRepaintManager::paintAndFlush()
705 qCInfo(lcWidgetPainting) <<
"Painting and flushing dirty"
706 <<
"top level" << dirty <<
"and dirty widgets" << dirtyWidgets;
709 bool repaintAllWidgets =
false;
711 const QRect tlwRect = tlw->data->crect;
712 if (!updatesDisabled && store->
size() != tlwRect.size()) {
714 if (hasStaticContents() && !store->
size().
isEmpty()
719 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
720 newVisible -= staticRegion;
725 dirty =
QRegion(0, 0, tlwRect.width(), tlwRect.height());
726 for (
int i = 0;
i < dirtyWidgets.size(); ++
i)
727 resetWidget(dirtyWidgets.at(
i));
728 dirtyWidgets.clear();
729 repaintAllWidgets =
true;
733 if (store->
size() != tlwRect.size())
734 store->
resize(tlwRect.size());
746 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
747 for (
int i = 0;
i < dirtyWidgets.size(); ++
i) {
758 bool hasDirtySiblingsAbove =
false;
767 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->
dirty;
774 wd->
dirty = dirtyBeforeSubtractedOpaqueChildren;
783 toClean += widgetDirty;
785#if QT_CONFIG(graphicsview)
786 if (tlw->d_func()->extra->proxyWidget) {
793 && !dirty.
intersects(widgetDirty.boundingRect())) {
794 opaqueNonOverlappedWidgets.append(
w);
797 dirty += widgetDirty;
800 dirtyWidgets.clear();
805 QTLWExtra *tlwExtra = tlw->d_func()->topData();
806 tlwExtra->widgetTextures.clear();
809 if (toClean.isEmpty()) {
815 QVarLengthArray<QWidget *, 16> paintPending;
816 const int numPaintPending = dirtyRenderToTextureWidgets.
size();
817 paintPending.reserve(numPaintPending);
818 for (
int i = 0;
i < numPaintPending; ++
i) {
823 dirtyRenderToTextureWidgets.
clear();
824 for (
int i = 0;
i < numPaintPending; ++
i) {
826 w->d_func()->sendPaintEvent(
w->rect());
845 for (
const auto &tl : tlwExtra->widgetTextures) {
846 for (
int i = 0;
i < tl->
count(); ++
i) {
848 if (dirtyRenderToTextureWidgets.
contains(
w)) {
852 w->d_func()->renderToTextureReallyDirty = 1;
858 for (
int i = 0;
i < dirtyRenderToTextureWidgets.
size(); ++
i)
859 resetWidget(dirtyRenderToTextureWidgets.
at(
i));
860 dirtyRenderToTextureWidgets.
clear();
862#if QT_CONFIG(graphicsview)
863 if (tlw->d_func()->extra->proxyWidget) {
864 updateStaticContentsSize();
866 updateRequestSent =
false;
868 tlw->d_func()->extra->proxyWidget->update(
rect);
877 updateStaticContentsSize();
878 const QRegion dirtyCopy(dirty);
880 updateRequestSent =
false;
883 for (
int i = 0;
i < opaqueNonOverlappedWidgets.size(); ++
i) {
884 QWidget *
w = opaqueNonOverlappedWidgets[
i];
904 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
928 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of top level"
929 <<
widget <<
"as needing flush";
930 topLevelNeedsFlush += region;
933 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of"
934 <<
widget <<
"as needing flush in" << nativeParent
935 <<
"at offset" << topLevelOffset;
936 if (nativeParent == tlw) {
938 topLevelNeedsFlush += region.
translated(topLevelOffset);
946 qCInfo(lcWidgetPainting) <<
"Marking" << region
947 <<
"of native child" <<
widget <<
"as needing flush";
958 if (!widgetPrivate->needsFlush)
959 widgetPrivate->needsFlush =
new QRegion;
961 *widgetPrivate->needsFlush += region;
970void QWidgetRepaintManager::flush()
972 qCInfo(lcWidgetPainting) <<
"Flushing top level"
973 << topLevelNeedsFlush <<
"and children" << needsFlushWidgets;
975 const bool hasNeedsFlushWidgets = !needsFlushWidgets.
isEmpty();
976 bool flushed =
false;
979 if (!topLevelNeedsFlush.
isEmpty()) {
981 topLevelNeedsFlush =
QRegion();
986 if (!flushed && !hasNeedsFlushWidgets) {
987 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
989 flush(tlw,
QRegion(), widgetTextures);
993 if (!hasNeedsFlushWidgets)
996 for (
QWidget *
w :
std::exchange(needsFlushWidgets, {})) {
1000 flush(
w, *wd->
needsFlush, widgetTexturesForNative);
1029 if (perfTime.
elapsed() > 5000) {
1030 double fps = double(perfFrames * 1000) / perfTime.
restart();
1031 qDebug(
"FPS: %.1f\n", fps);
1071 qCDebug(lcWidgetPainting) <<
"Flushing" << region <<
"of" <<
widget
1072 <<
"with QRhi" <<
rhi
1074 if (!widgetTextures)
1075 widgetTextures = qt_dummy_platformTextureList;
1078 widgetWindowPrivate->sendComposeStatus(
widget->
window(),
false);
1090 translucentBackground);
1091 widgetWindowPrivate->sendComposeStatus(
widget->
window(),
true);
1101 qCInfo(lcWidgetPainting) <<
"Flushing" << region <<
"of" <<
widget;
1124 if (newPaintManager ==
this)
1128 while (
i < staticWidgets.
size()) {
1130 if (reparented ==
w || reparented->isAncestorOf(
w)) {
1132 if (newPaintManager)
1133 newPaintManager->addStaticWidget(
w);
1145bool QWidgetRepaintManager::hasStaticContents()
const
1147 return !staticWidgets.
isEmpty();
1159 if (!withinClipRect.isEmpty())
1160 backingstoreRect &= withinClipRect;
1161 return QRegion(backingstoreRect);
1168 const bool clipToRect = !withinClipRect.
isEmpty();
1178 QRect rect(0, 0, wd->
extra->staticContentsSize.width(), wd->
extra->staticContentsSize.height());
1202void QWidgetRepaintManager::updateStaticContentsSize()
1204 for (
int i = 0;
i < staticWidgets.
size(); ++
i) {
1216 return !(dirtyWidgets.isEmpty() && dirty.
isEmpty() && dirtyRenderToTextureWidgets.
isEmpty());
1230 const bool sizeDecreased = (
data.crect.width() < oldSize.
width())
1234 const bool parentAreaExposed = !
offset.isNull() || sizeDecreased;
1235 const QRect newWidgetRect(
q->rect());
1243 const bool hasStaticChildren = !staticChildren.
isEmpty();
1245 if (hasStaticChildren) {
1247 dirty -= staticChildren;
1254 if (!parentAreaExposed)
1260 parentExpose &=
QRect(oldPos, oldSize);
1261 if (hasStaticChildren)
1262 parentExpose -=
data.crect;
1263 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1267 parentExpose -=
data.crect;
1268 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1278 if (sizeDecreased) {
1288 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1289 QRegion newVisible(newWidgetRect);
1290 newVisible -= oldWidgetRect;
1294 if (!parentAreaExposed)
1298 const QRect oldRect(oldPos, oldSize);
1300 QRegion parentExpose(oldRect);
1301 parentExpose &=
extra->mask.translated(oldPos);
1302 parentExpose -= (
extra->mask.translated(
data.crect.topLeft()) &
data.crect);
1303 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1305 QRegion parentExpose(oldRect);
1306 parentExpose -=
data.crect;
1307 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1318#include "qwidgetrepaintmanager.moc"
1319#include "moc_qwidgetrepaintmanager_p.cpp"
QPlatformBackingStore * handle() const
Returns a pointer to the QPlatformBackingStore implementation.
QPaintDevice * paintDevice()
Returns the paint device for this surface.
void beginPaint(const QRegion &)
Begins painting on the backing store surface in the given region.
void flush(const QRegion ®ion, QWindow *window=nullptr, const QPoint &offset=QPoint())
Flushes the given region from the specified window onto the screen.
void setStaticContents(const QRegion ®ion)
Set region as the static contents of this window.
void resize(const QSize &size)
Sets the size of the window surface to size.
QSize size() const
Returns the current size of the window surface.
bool scroll(const QRegion &area, int dx, int dy)
Scrolls the given area dx pixels to the right and dy downward; both dx and dy may be negative.
void endPaint()
Ends painting.
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
static bool closingDown()
Returns true if the application objects are being destroyed; otherwise returns false.
static void postEvent(QObject *receiver, QEvent *event, int priority=Qt::NormalEventPriority)
qint64 elapsed() const noexcept
Returns the number of milliseconds since this QElapsedTimer was last started.
qint64 restart() noexcept
Restarts the timer and returns the number of milliseconds elapsed since the previous start.
void start() noexcept
\typealias QElapsedTimer::Duration Synonym for std::chrono::nanoseconds.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
@ WindowAboutToChangeInternal
static QPlatformIntegration * platformIntegration()
qsizetype size() const noexcept
bool isEmpty() const noexcept
void removeAt(qsizetype i)
const_reference at(qsizetype i) const noexcept
qsizetype removeAll(const AT &t)
void append(parameter_type t)
const QObjectList & children() const
Returns a list of child objects.
void deleteLater()
\threadsafe
qreal devicePixelRatio() const
QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager)
void watch(QPlatformTextureList *textureList)
QRect geometry(int index) const
void appendTexture(void *source, QRhiTexture *texture, const QRect &geometry, const QRect &clipRect=QRect(), Flags flags={ })
\inmodule QtCore\reentrant
constexpr int x() const noexcept
Returns the x coordinate of this point.
constexpr int y() const noexcept
Returns the y coordinate of this point.
\inmodule QtCore\reentrant
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
QRect intersected(const QRect &other) const noexcept
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
constexpr QSize size() const noexcept
Returns the size of the rectangle.
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
The QRegion class specifies a clip region for a painter.
QRect boundingRect() const noexcept
Returns the bounding rectangle of this region.
void translate(int dx, int dy)
Translates (moves) the region dx along the X axis and dy along the Y axis.
bool intersects(const QRegion &r) const
bool isEmpty() const
Returns true if the region is empty; otherwise returns false.
QRegion intersected(const QRegion &r) const
QRegion translated(int dx, int dy) const
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
The QScreen class is used to query screen properties. \inmodule QtGui.
constexpr int height() const noexcept
Returns the height.
constexpr int width() const noexcept
Returns the width.
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
static QWindowPrivate * get(QWindow *window)
QElapsedTimer lastComposeTime
SurfaceType surfaceType() const override
Returns the surface type of the window.
Combined button and popup list for selecting options.
@ WA_TranslucentBackground
QTextStream & ws(QTextStream &stream)
Calls \l {QTextStream::}{skipWhiteSpace()} on stream and returns stream.
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qCInfo(category,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
GLsizei const GLfloat * v
[13]
GLfloat GLfloat GLfloat w
[0]
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLdouble GLdouble GLdouble GLdouble q
static double elapsed(qint64 after, qint64 before)
#define Q_AUTOTEST_EXPORT
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
bool contains(const AT &t) const noexcept