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>
44 : m_repaintManager(repaintManager) {}
48 m_locked[textureList] = textureList->
isLocked();
52 foreach (
bool v, m_locked) {
60 void onLockStatusChanged(
bool locked) {
62 m_locked[tl] = locked;
64 m_repaintManager->sync();
75 : tlw(topLevel), store(tlw->backingStore())
80 updateLists(topLevel);
83void QWidgetRepaintManager::updateLists(
QWidget *cur)
89 for (
int i = 0;
i < children.
size(); ++
i) {
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));
125 if (!
q->isVisible() || !
q->updatesEnabled())
134 if (clipped.isEmpty())
171 qCInfo(lcWidgetPainting) <<
"Marking" <<
r <<
"of" <<
widget <<
"dirty"
172 <<
"with" << updateTime;
175 Q_ASSERT(tlw->d_func()->extra->topextra);
180#if QT_CONFIG(graphicseffect)
181 widget->d_func()->invalidateGraphicsEffectsRecursively();
188 if (
widget->d_func()->shouldPaintOnScreen()) {
189 if (
widget->d_func()->dirty.isEmpty()) {
191 sendUpdateRequest(
widget, updateTime);
193 }
else if (qt_region_strictContains(
widget->d_func()->dirty, widgetRect)) {
195 sendUpdateRequest(
widget, updateTime);
199 const bool eventAlreadyPosted = !
widget->d_func()->dirty.isEmpty();
201 if (!eventAlreadyPosted || updateTime ==
UpdateNow)
202 sendUpdateRequest(
widget, updateTime);
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);
221#if QT_CONFIG(graphicseffect)
225 if (qt_region_strictContains(dirty, translatedRect)) {
227 sendUpdateRequest(tlw, updateTime);
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);
242 if (!eventAlreadyPosted || updateTime ==
UpdateNow)
243 sendUpdateRequest(tlw, updateTime);
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);
273 sendUpdateRequest(tlw, updateTime);
282#if QT_CONFIG(graphicseffect)
287 widgetPrivate->
dirty = rgn;
306 for (
int i = 0;
i <
n; ++
i) {
315 widget->d_func()->inDirtyList =
false;
316 widget->d_func()->isScrolled =
false;
317 widget->d_func()->isMoved =
false;
322void QWidgetRepaintManager::addDirtyRenderToTextureWidget(
QWidget *
widget)
332void QWidgetRepaintManager::sendUpdateRequest(
QWidget *
widget, UpdateTime updateTime)
337 qCInfo(lcWidgetPainting) <<
"Sending update request to" <<
widget <<
"with" << updateTime;
348 refresh =
ws->refreshRate();
357 switch (updateTime) {
359 updateRequestSent =
true;
382 if (!
q->isVisible() || (dx == 0 && dy == 0))
393 const QRect newRect(
rect.translated(dx, dy));
394 QRect destRect =
rect.intersected(clipR);
401 bool accelerateMove = accelEnv &&
isOpaque && !nativeWithTextureChild && sourceRect.
isValid()
402#if QT_CONFIG(graphicsview)
404 && !tlw->d_func()->extra->proxyWidget
408 if (!accelerateMove) {
411 parentRegion -= newRect;
414 parentRegion += newRect & clipR;
421 QRegion childExpose(newRect & clipR);
423 if (repaintManager->
bltRect(sourceRect, dx, dy, parentWidget))
424 childExpose -= destRect;
429 const bool childUpdatesEnabled =
q->updatesEnabled();
431 if (childUpdatesEnabled && !childExpose.
isEmpty()) {
437 QRegion parentExpose(parentRect);
438 parentExpose -= newRect;
440 parentExpose +=
QRegion(newRect) -
extra->mask.translated(
data.crect.topLeft());
443 repaintManager->
markDirty(parentExpose, parentWidget);
447 if (childUpdatesEnabled) {
468 bool overlapped =
false;
472 if (!accelerateScroll) {
487 if (repaintManager->
bltRect(sourceRect, dx, dy,
q))
488 childExpose -= destRect;
492 if (
rect ==
q->rect()) {
496 if (!dirtyScrollRegion.
isEmpty()) {
497 dirty -= dirtyScrollRegion;
499 dirty += dirtyScrollRegion;
504 if (!
q->updatesEnabled())
529 return store->
scroll(tlwRect, dx, dy);
561 auto tl = std::make_unique<QPlatformTextureList>();
569 for (
QWidget *ncw : std::as_const(nativeChildren)) {
580 for (
int i = 0;
i < tl->
count(); ++
i) {
588 return qt_dummy_platformTextureList();
604 qCInfo(lcWidgetPainting) <<
"Syncing" << exposedRegion <<
"of" << exposedWidget;
618 flush(exposedWidget, widgetTextures ?
QRegion() : exposedRegion, widgetTextures);
637 qCInfo(lcWidgetPainting) <<
"Syncing dirty widgets";
639 updateRequestSent =
false;
648 for (
int i = 0;
i < dirtyWidgets.
size(); ++
i)
649 resetWidget(dirtyWidgets.
at(
i));
650 dirtyWidgets.
clear();
665bool QWidgetRepaintManager::syncAllowed()
667 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
668 if (textureListWatcher && !textureListWatcher->
isLocked()) {
670 textureListWatcher =
nullptr;
672 bool skipSync =
false;
673 for (
const auto &tl : tlwExtra->widgetTextures) {
675 if (!textureListWatcher)
677 if (!textureListWatcher->
isLocked())
678 textureListWatcher->
watch(tl.get());
690#if QT_CONFIG(graphicseffect)
692 if (
w->graphicsEffect())
694 w =
w->parentWidget();
700void QWidgetRepaintManager::paintAndFlush()
702 qCInfo(lcWidgetPainting) <<
"Painting and flushing dirty"
703 <<
"top level" << dirty <<
"and dirty widgets" << dirtyWidgets;
706 bool repaintAllWidgets =
false;
708 const QRect tlwRect = tlw->data->crect;
709 if (!updatesDisabled && store->
size() != tlwRect.
size()) {
710 if (hasStaticContents() && !store->
size().
isEmpty() ) {
715 newVisible -= staticRegion;
721 for (
int i = 0;
i < dirtyWidgets.size(); ++
i)
722 resetWidget(dirtyWidgets.at(
i));
723 dirtyWidgets.clear();
724 repaintAllWidgets =
true;
728 if (store->
size() != tlwRect.
size())
742 for (
int i = 0;
i < dirtyWidgets.size(); ++
i) {
753 bool hasDirtySiblingsAbove =
false;
762 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->
dirty;
769 wd->
dirty = dirtyBeforeSubtractedOpaqueChildren;
778 toClean += widgetDirty;
780#if QT_CONFIG(graphicsview)
781 if (tlw->d_func()->extra->proxyWidget) {
788 && !dirty.
intersects(widgetDirty.boundingRect())) {
789 opaqueNonOverlappedWidgets.
append(
w);
792 dirty += widgetDirty;
795 dirtyWidgets.clear();
800 QTLWExtra *tlwExtra = tlw->d_func()->topData();
805 if (toClean.isEmpty()) {
812 const int numPaintPending = dirtyRenderToTextureWidgets.
size();
813 paintPending.
reserve(numPaintPending);
814 for (
int i = 0;
i < numPaintPending; ++
i) {
819 dirtyRenderToTextureWidgets.
clear();
820 for (
int i = 0;
i < numPaintPending; ++
i) {
822 w->d_func()->sendPaintEvent(
w->rect());
841 for (
const auto &tl : tlwExtra->widgetTextures) {
842 for (
int i = 0;
i < tl->
count(); ++
i) {
844 if (dirtyRenderToTextureWidgets.
contains(
w)) {
848 w->d_func()->renderToTextureReallyDirty = 1;
854 for (
int i = 0;
i < dirtyRenderToTextureWidgets.
size(); ++
i)
855 resetWidget(dirtyRenderToTextureWidgets.
at(
i));
856 dirtyRenderToTextureWidgets.
clear();
858#if QT_CONFIG(graphicsview)
859 if (tlw->d_func()->extra->proxyWidget) {
860 updateStaticContentsSize();
862 updateRequestSent =
false;
864 tlw->d_func()->extra->proxyWidget->update(
rect);
873 updateStaticContentsSize();
874 const QRegion dirtyCopy(dirty);
876 updateRequestSent =
false;
879 for (
int i = 0;
i < opaqueNonOverlappedWidgets.
size(); ++
i) {
880 QWidget *
w = opaqueNonOverlappedWidgets[
i];
900 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
924 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of top level"
925 <<
widget <<
"as needing flush";
926 topLevelNeedsFlush += region;
929 qCInfo(lcWidgetPainting) <<
"Marking" << region <<
"of"
930 <<
widget <<
"as needing flush in" << nativeParent
931 <<
"at offset" << topLevelOffset;
932 if (nativeParent == tlw) {
934 topLevelNeedsFlush += region.
translated(topLevelOffset);
942 qCInfo(lcWidgetPainting) <<
"Marking" << region
943 <<
"of native child" <<
widget <<
"as needing flush";
966void QWidgetRepaintManager::flush()
968 qCInfo(lcWidgetPainting) <<
"Flushing top level"
969 << topLevelNeedsFlush <<
"and children" << needsFlushWidgets;
971 const bool hasNeedsFlushWidgets = !needsFlushWidgets.
isEmpty();
972 bool flushed =
false;
975 if (!topLevelNeedsFlush.
isEmpty()) {
977 topLevelNeedsFlush =
QRegion();
982 if (!flushed && !hasNeedsFlushWidgets) {
983 if (!tlw->d_func()->topData()->widgetTextures.empty()) {
985 flush(tlw,
QRegion(), widgetTextures);
989 if (!hasNeedsFlushWidgets)
992 for (
QWidget *
w :
std::exchange(needsFlushWidgets, {})) {
996 flush(
w, *wd->
needsFlush, widgetTexturesForNative);
1025 if (perfTime.
elapsed() > 5000) {
1026 double fps = double(perfFrames * 1000) / perfTime.
restart();
1027 qDebug(
"FPS: %.1f\n", fps);
1067 qCDebug(lcWidgetPainting) <<
"Flushing" << region <<
"of" <<
widget
1068 <<
"with QRhi" <<
rhi
1070 if (!widgetTextures)
1071 widgetTextures = qt_dummy_platformTextureList;
1087 translucentBackground);
1098 qCInfo(lcWidgetPainting) <<
"Flushing" << region <<
"of" <<
widget;
1121 if (newPaintManager ==
this)
1125 while (
i < staticWidgets.
size()) {
1129 if (newPaintManager)
1142bool QWidgetRepaintManager::hasStaticContents()
const
1144#if defined(Q_OS_WIN)
1145 return !staticWidgets.
isEmpty();
1147 return !staticWidgets.
isEmpty() &&
false;
1160 if (!withinClipRect.
isEmpty())
1161 backingstoreRect &= withinClipRect;
1162 return QRegion(backingstoreRect);
1169 const bool clipToRect = !withinClipRect.
isEmpty();
1179 QRect rect(0, 0, wd->
extra->staticContentsSize.width(), wd->
extra->staticContentsSize.height());
1203void QWidgetRepaintManager::updateStaticContentsSize()
1205 for (
int i = 0;
i < staticWidgets.
size(); ++
i) {
1217 return !(dirtyWidgets.isEmpty() && dirty.
isEmpty() && dirtyRenderToTextureWidgets.
isEmpty());
1231 const bool sizeDecreased = (
data.crect.width() < oldSize.
width())
1235 const bool parentAreaExposed = !
offset.isNull() || sizeDecreased;
1236 const QRect newWidgetRect(
q->rect());
1244 const bool hasStaticChildren = !staticChildren.
isEmpty();
1246 if (hasStaticChildren) {
1248 dirty -= staticChildren;
1255 if (!parentAreaExposed)
1261 parentExpose &=
QRect(oldPos, oldSize);
1262 if (hasStaticChildren)
1263 parentExpose -=
data.crect;
1264 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1268 parentExpose -=
data.crect;
1269 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1279 if (sizeDecreased) {
1289 if (!sizeDecreased || !oldWidgetRect.
contains(newWidgetRect)) {
1290 QRegion newVisible(newWidgetRect);
1291 newVisible -= oldWidgetRect;
1295 if (!parentAreaExposed)
1299 const QRect oldRect(oldPos, oldSize);
1301 QRegion parentExpose(oldRect);
1302 parentExpose &=
extra->mask.translated(oldPos);
1303 parentExpose -= (
extra->mask.translated(
data.crect.topLeft()) &
data.crect);
1304 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1306 QRegion parentExpose(oldRect);
1307 parentExpose -=
data.crect;
1308 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1319#include "qwidgetrepaintmanager.moc"
1320#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
Starts this timer.
bool isValid() const noexcept
Returns false if the timer has never been started or invalidated by a call to invalidate().
@ WindowAboutToChangeInternal
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 isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
constexpr int height() const noexcept
Returns the height of the rectangle.
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
QRect intersected(const QRect &other) const noexcept
bool contains(const QRect &r, bool proper=false) const noexcept
This is an overloaded member function, provided for convenience. It differs from the above function o...
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 int width() const noexcept
Returns the width 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
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.
constexpr size_type size() const noexcept
void reserve(qsizetype sz)
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
Q_GUI_EXPORT QWindowPrivate * qt_window_private(QWindow *window)
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
bool contains(const AT &t) const noexcept
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent