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 <vlad.zahorodnii@kde.org>
This commit is contained in:
David Edmundson 2024-05-26 15:08:53 +03:00
parent 18355d9d37
commit e705040909
3 changed files with 48 additions and 30 deletions

View File

@ -406,8 +406,6 @@ bool QWaylandXdgSurface::handleExpose(const QRegion &region)
void QWaylandXdgSurface::applyConfigure() void QWaylandXdgSurface::applyConfigure()
{ {
bool wasExposed = isExposed();
// It is a redundant ack_configure, so skipped. // It is a redundant ack_configure, so skipped.
if (m_pendingConfigureSerial == m_appliedConfigureSerial) if (m_pendingConfigureSerial == m_appliedConfigureSerial)
return; return;
@ -421,8 +419,7 @@ void QWaylandXdgSurface::applyConfigure()
m_configured = true; m_configured = true;
ack_configure(m_appliedConfigureSerial); ack_configure(m_appliedConfigureSerial);
if (!wasExposed && isExposed()) window()->updateExposure();
m_window->sendRecursiveExposeEvent();
} }
bool QWaylandXdgSurface::wantsDecorations() const bool QWaylandXdgSurface::wantsDecorations() const

View File

@ -326,7 +326,7 @@ void QWaylandWindow::reset()
mCanResize = true; mCanResize = true;
mResizeDirty = false; mResizeDirty = false;
mScale = std::nullopt; mScale = std::nullopt;
mExposed = false;
mOpaqueArea = QRegion(); mOpaqueArea = QRegion();
mMask = QRegion(); mMask = QRegion();
@ -544,11 +544,12 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons
void QWaylandWindow::sendExposeEvent(const QRect &rect) void QWaylandWindow::sendExposeEvent(const QRect &rect)
{ {
if (!(mShellSurface && mShellSurface->handleExpose(rect))) if (!(mShellSurface && mShellSurface->handleExpose(rect))) {
QWindowSystemInterface::handleExposeEvent(window(), rect); QWindowSystemInterface::handleExposeEvent(window(), rect);
mLastExposeGeometry = rect;
}
else else
qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending"; qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
mLastExposeGeometry = rect;
} }
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
@ -572,6 +573,7 @@ void QWaylandWindow::setVisible(bool visible)
initWindow(); initWindow();
setGeometry(windowGeometry()); setGeometry(windowGeometry());
updateExposure();
// Don't flush the events here, or else the newly visible window may start drawing, but since // 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 // there was no frame before it will be stuck at the waitForFrameSync() in
// QWaylandShmBackingStore::beginPaint(). // QWaylandShmBackingStore::beginPaint().
@ -579,6 +581,8 @@ void QWaylandWindow::setVisible(bool visible)
if (mShellSurface) if (mShellSurface)
mShellSurface->requestActivateOnShow(); mShellSurface->requestActivateOnShow();
} else { } else {
// make sure isExposed is false during the next event dispatch
mExposed = false;
sendExposeEvent(QRect()); sendExposeEvent(QRect());
reset(); reset();
} }
@ -654,6 +658,8 @@ void QWaylandWindow::doApplyConfigure()
mShellSurface->applyConfigure(); mShellSurface->applyConfigure();
mWaitingToApplyConfigure = false; mWaitingToApplyConfigure = false;
sendExposeEvent(QRect(QPoint(), geometry().size()));
} }
void QWaylandWindow::doApplyConfigureFromOtherThread() void QWaylandWindow::doApplyConfigureFromOtherThread()
@ -662,7 +668,6 @@ void QWaylandWindow::doApplyConfigureFromOtherThread()
if (!mCanResize || !mWaitingToApplyConfigure) if (!mCanResize || !mWaitingToApplyConfigure)
return; return;
doApplyConfigure(); doApplyConfigure();
sendRecursiveExposeEvent();
} }
void QWaylandWindow::setCanResize(bool canResize) void QWaylandWindow::setCanResize(bool canResize)
@ -678,13 +683,11 @@ void QWaylandWindow::setCanResize(bool canResize)
bool inGuiThread = QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed(); bool inGuiThread = QThread::currentThreadId() == QThreadData::get2(thread())->threadId.loadRelaxed();
if (inGuiThread) { if (inGuiThread) {
doApplyConfigure(); doApplyConfigure();
sendRecursiveExposeEvent();
} else { } else {
QMetaObject::invokeMethod(this, &QWaylandWindow::doApplyConfigureFromOtherThread, Qt::QueuedConnection); QMetaObject::invokeMethod(this, &QWaylandWindow::doApplyConfigureFromOtherThread, Qt::QueuedConnection);
} }
} else if (mResizeDirty) { } else if (mResizeDirty) {
mResizeDirty = false; mResizeDirty = false;
sendExposeEvent(QRect(QPoint(), geometry().size()));
} }
} }
} }
@ -697,23 +700,9 @@ void QWaylandWindow::applyConfigure()
doApplyConfigure(); doApplyConfigure();
lock.unlock(); lock.unlock();
sendRecursiveExposeEvent();
QWindowSystemInterface::flushWindowSystemEvents(); 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) void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{ {
QReadLocker locker(&mSurfaceLock); QReadLocker locker(&mSurfaceLock);
@ -843,8 +832,8 @@ void QWaylandWindow::doHandleFrameCallback()
mWaitingForUpdateDelivery.storeRelease(false); mWaitingForUpdateDelivery.storeRelease(false);
bool wasExposed = isExposed(); bool wasExposed = isExposed();
mFrameCallbackTimedOut = false; mFrameCallbackTimedOut = false;
if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? // Did setting mFrameCallbackTimedOut make the window exposed?
sendExposeEvent(QRect(QPoint(), geometry().size())); updateExposure();
if (wasExposed && hasPendingUpdateRequest()) if (wasExposed && hasPendingUpdateRequest())
deliverUpdateRequest(); deliverUpdateRequest();
@ -861,7 +850,7 @@ bool QWaylandWindow::waitForFrameSync(int timeout)
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true; mFrameCallbackTimedOut = true;
mWaitingForUpdate = false; mWaitingForUpdate = false;
sendExposeEvent(QRect()); updateExposure();
} }
return !mWaitingForFrameCallback; return !mWaitingForFrameCallback;
@ -1517,7 +1506,7 @@ void QWaylandWindow::requestActivateWindow()
mShellSurface->requestActivate(); mShellSurface->requestActivate();
} }
bool QWaylandWindow::isExposed() const bool QWaylandWindow::calculateExposure() const
{ {
if (!window()->isVisible()) if (!window()->isVisible())
return false; return false;
@ -1534,6 +1523,30 @@ bool QWaylandWindow::isExposed() const
return !(shouldCreateShellSurface() || shouldCreateSubSurface()); 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 bool QWaylandWindow::isActive() const
{ {
return mDisplay->isWindowActivated(this); 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"; qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true; mFrameCallbackTimedOut = true;
mWaitingForUpdate = false; mWaitingForUpdate = false;
sendExposeEvent(QRect()); updateExposure();
} }
void QWaylandWindow::requestUpdate() void QWaylandWindow::requestUpdate()

View File

@ -232,7 +232,10 @@ public:
void endFrame(); void endFrame();
void closeChildPopups(); void closeChildPopups();
void sendRecursiveExposeEvent();
// should be invoked whenever a property that potentially affects
// exposure changes
void updateExposure();
virtual void reinit(); virtual void reinit();
void reset(); void reset();
@ -249,6 +252,9 @@ Q_SIGNALS:
protected: protected:
virtual void doHandleFrameCallback(); virtual void doHandleFrameCallback();
virtual QRect defaultGeometry() const; 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); void sendExposeEvent(const QRect &rect);
QWaylandDisplay *mDisplay = nullptr; QWaylandDisplay *mDisplay = nullptr;
@ -299,6 +305,7 @@ protected:
// True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
bool mWaitingForUpdate = false; bool mWaitingForUpdate = false;
bool mExposed = false;
QRecursiveMutex mResizeLock; QRecursiveMutex mResizeLock;
bool mWaitingToApplyConfigure = false; bool mWaitingToApplyConfigure = false;
@ -349,6 +356,7 @@ private:
bool isOpaque() const; bool isOpaque() const;
void updateInputRegion(); void updateInputRegion();
void updateViewport(); void updateViewport();
bool calculateExposure() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e); void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreensChanged(); void handleScreensChanged();