Client: Fix stuttering when the GUI thread is busy

When we did invokeMethod for handling the frame callbacks, we had to wait for
the GUI thread to finish whatever it's doing before we would stop blocking.

Fix it by clearing the frame callback timer and stop blocking immediately,
while delaying the rest of the work until it can be run on the other thread.

Fixes: QTBUG-76397
Change-Id: I343e4feac4838926b4fa2ccac2948988bc6c3bb7
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2019-06-19 14:05:22 +02:00
parent a571e4cebd
commit 26c5d5e62b
2 changed files with 33 additions and 30 deletions

View File

@ -610,29 +610,34 @@ const wl_callback_listener QWaylandWindow::callbackListener = {
Q_UNUSED(callback);
Q_UNUSED(time);
auto *window = static_cast<QWaylandWindow*>(data);
if (window->thread() != QThread::currentThread())
QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection);
else
window->handleFrameCallback();
window->handleFrameCallback();
}
};
void QWaylandWindow::handleFrameCallback()
{
bool wasExposed = isExposed();
if (mFrameCallbackTimerId != -1) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
}
// Stop the timer and stop waiting immediately
int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1);
mWaitingForFrameCallback = false;
mFrameCallbackTimedOut = false;
if (!wasExposed && isExposed())
sendExposeEvent(QRect(QPoint(), geometry().size()));
if (wasExposed && hasPendingUpdateRequest())
deliverUpdateRequest();
// The rest can wait until we can run it on the correct thread
auto doHandleExpose = [this, timerId]() {
if (timerId != -1)
killTimer(timerId);
bool wasExposed = isExposed();
mFrameCallbackTimedOut = false;
if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
sendExposeEvent(QRect(QPoint(), geometry().size()));
if (wasExposed && hasPendingUpdateRequest())
deliverUpdateRequest();
};
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, doHandleExpose);
} else {
doHandleExpose();
}
}
QMutex QWaylandWindow::mFrameSyncMutex;
@ -654,11 +659,11 @@ bool QWaylandWindow::waitForFrameSync(int timeout)
}
// Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
// Ordered semantics is needed to avoid stopping the timer twice and not miss it when it's
// started by other writes
int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1);
if (fcbId != -1)
QMetaObject::invokeMethod(this, [=] { killTimer(fcbId); }, Qt::QueuedConnection);
return !mWaitingForFrameCallback;
}
@ -1090,9 +1095,9 @@ void QWaylandWindow::timerEvent(QTimerEvent *event)
}
}
if (event->timerId() == mFrameCallbackTimerId) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
if (mFrameCallbackTimerId.testAndSetOrdered(event->timerId(), -1)) {
killTimer(event->timerId());
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true;
mWaitingForUpdate = false;
@ -1154,11 +1159,9 @@ void QWaylandWindow::handleUpdate()
mWaitingForUpdate = false;
// Stop current frame timer if any, can't use killTimer directly, see comment above.
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1);
if (fcbId != -1)
QMetaObject::invokeMethod(this, [=] { killTimer(fcbId); }, Qt::QueuedConnection);
// Start a timer for handling the case when the compositor stops sending frame callbacks.
QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly

View File

@ -216,7 +216,7 @@ protected:
WId mWindowId;
bool mWaitingForFrameCallback = false;
bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback
QAtomicInt mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback
struct ::wl_callback *mFrameCallback = nullptr;
struct ::wl_event_queue *mFrameQueue = nullptr;
QWaitCondition mFrameSyncWait;