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()
{
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

View File

@ -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()

View File

@ -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();