From e7050409098fd5f670ab506fd86475b072ba5d13 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Sun, 26 May 2024 15:08:53 +0300 Subject: [PATCH] client: Cache exposed state and unify sending events Right now we are sending an expose event on every configure event because exposure may have changed. This causes an overhead. The mechanism for sending expose events is also scattered, there are a lot of properties that affect exposure, having everyone track the full state gets messy. This is cleanup work to then be able to land viewport clipping and synchronous event delivery. Change-Id: I192b908cdb6f6adda7d0e33f83961861e09504e8 Reviewed-by: Vlad Zahorodnii --- .../xdg-shell/qwaylandxdgshell.cpp | 5 +- .../platforms/wayland/qwaylandwindow.cpp | 63 +++++++++++-------- .../platforms/wayland/qwaylandwindow_p.h | 10 ++- 3 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 6ccda7d265f..da6a8b73a7c 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -406,8 +406,6 @@ bool QWaylandXdgSurface::handleExpose(const QRegion ®ion) void QWaylandXdgSurface::applyConfigure() { - bool wasExposed = isExposed(); - // It is a redundant ack_configure, so skipped. if (m_pendingConfigureSerial == m_appliedConfigureSerial) return; @@ -421,8 +419,7 @@ void QWaylandXdgSurface::applyConfigure() m_configured = true; ack_configure(m_appliedConfigureSerial); - if (!wasExposed && isExposed()) - m_window->sendRecursiveExposeEvent(); + window()->updateExposure(); } bool QWaylandXdgSurface::wantsDecorations() const diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 4e2163df785..3aea7bbb35d 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -326,7 +326,7 @@ void QWaylandWindow::reset() mCanResize = true; mResizeDirty = false; mScale = std::nullopt; - + mExposed = false; mOpaqueArea = QRegion(); mMask = QRegion(); @@ -544,11 +544,12 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons void QWaylandWindow::sendExposeEvent(const QRect &rect) { - if (!(mShellSurface && mShellSurface->handleExpose(rect))) + if (!(mShellSurface && mShellSurface->handleExpose(rect))) { QWindowSystemInterface::handleExposeEvent(window(), rect); + mLastExposeGeometry = rect; + } else qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending"; - mLastExposeGeometry = rect; } QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const @@ -572,6 +573,7 @@ void QWaylandWindow::setVisible(bool visible) initWindow(); setGeometry(windowGeometry()); + updateExposure(); // Don't flush the events here, or else the newly visible window may start drawing, but since // there was no frame before it will be stuck at the waitForFrameSync() in // QWaylandShmBackingStore::beginPaint(). @@ -579,6 +581,8 @@ void QWaylandWindow::setVisible(bool visible) if (mShellSurface) mShellSurface->requestActivateOnShow(); } else { + // make sure isExposed is false during the next event dispatch + mExposed = false; sendExposeEvent(QRect()); reset(); } @@ -654,6 +658,8 @@ void QWaylandWindow::doApplyConfigure() mShellSurface->applyConfigure(); mWaitingToApplyConfigure = false; + + sendExposeEvent(QRect(QPoint(), geometry().size())); } void QWaylandWindow::doApplyConfigureFromOtherThread() @@ -662,7 +668,6 @@ void QWaylandWindow::doApplyConfigureFromOtherThread() if (!mCanResize || !mWaitingToApplyConfigure) return; doApplyConfigure(); - sendRecursiveExposeEvent(); } void QWaylandWindow::setCanResize(bool canResize) @@ -678,13 +683,11 @@ void QWaylandWindow::setCanResize(bool canResize) bool inGuiThread = QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(); if (inGuiThread) { doApplyConfigure(); - sendRecursiveExposeEvent(); } else { QMetaObject::invokeMethod(this, &QWaylandWindow::doApplyConfigureFromOtherThread, Qt::QueuedConnection); } } else if (mResizeDirty) { mResizeDirty = false; - sendExposeEvent(QRect(QPoint(), geometry().size())); } } } @@ -697,23 +700,9 @@ void QWaylandWindow::applyConfigure() doApplyConfigure(); lock.unlock(); - sendRecursiveExposeEvent(); QWindowSystemInterface::flushWindowSystemEvents(); } -void QWaylandWindow::sendRecursiveExposeEvent() -{ - if (!isExposed()) - sendExposeEvent(QRect()); - else - sendExposeEvent(QRect(QPoint(), geometry().size())); - - for (QWaylandSubSurface *subSurface : std::as_const(mChildren)) { - auto subWindow = subSurface->window(); - subWindow->sendRecursiveExposeEvent(); - } -} - void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { QReadLocker locker(&mSurfaceLock); @@ -843,8 +832,8 @@ void QWaylandWindow::doHandleFrameCallback() mWaitingForUpdateDelivery.storeRelease(false); bool wasExposed = isExposed(); mFrameCallbackTimedOut = false; - if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? - sendExposeEvent(QRect(QPoint(), geometry().size())); + // Did setting mFrameCallbackTimedOut make the window exposed? + updateExposure(); if (wasExposed && hasPendingUpdateRequest()) deliverUpdateRequest(); @@ -861,7 +850,7 @@ bool QWaylandWindow::waitForFrameSync(int timeout) qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; - sendExposeEvent(QRect()); + updateExposure(); } return !mWaitingForFrameCallback; @@ -1517,7 +1506,7 @@ void QWaylandWindow::requestActivateWindow() mShellSurface->requestActivate(); } -bool QWaylandWindow::isExposed() const +bool QWaylandWindow::calculateExposure() const { if (!window()->isVisible()) return false; @@ -1534,6 +1523,30 @@ bool QWaylandWindow::isExposed() const return !(shouldCreateShellSurface() || shouldCreateSubSurface()); } +void QWaylandWindow::updateExposure() +{ + bool exposed = calculateExposure(); + if (exposed == mExposed) + return; + + mExposed = exposed; + + if (!exposed) + sendExposeEvent(QRect()); + else + sendExposeEvent(QRect(QPoint(), geometry().size())); + + for (QWaylandSubSurface *subSurface : std::as_const(mChildren)) { + auto subWindow = subSurface->window(); + subWindow->updateExposure(); + } +} + +bool QWaylandWindow::isExposed() const +{ + return mExposed; +} + bool QWaylandWindow::isActive() const { return mDisplay->isWindowActivated(this); @@ -1647,7 +1660,7 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; - sendExposeEvent(QRect()); + updateExposure(); } void QWaylandWindow::requestUpdate() diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 5eec5b5d136..38423f79b1a 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -232,7 +232,10 @@ public: void endFrame(); void closeChildPopups(); - void sendRecursiveExposeEvent(); + + // should be invoked whenever a property that potentially affects + // exposure changes + void updateExposure(); virtual void reinit(); void reset(); @@ -249,6 +252,9 @@ Q_SIGNALS: protected: virtual void doHandleFrameCallback(); virtual QRect defaultGeometry() const; + + // this should be called directly for buffer size changes only + // use updateExposure for anything affecting the on/off state void sendExposeEvent(const QRect &rect); QWaylandDisplay *mDisplay = nullptr; @@ -299,6 +305,7 @@ protected: // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer bool mWaitingForUpdate = false; + bool mExposed = false; QRecursiveMutex mResizeLock; bool mWaitingToApplyConfigure = false; @@ -349,6 +356,7 @@ private: bool isOpaque() const; void updateInputRegion(); void updateViewport(); + bool calculateExposure() const; void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleScreensChanged();