summaryrefslogtreecommitdiff
path: root/dev-qt/qtwayland/files/qtwayland-5.12.4-fix-stuttering-when-gui-thread-busy.patch
blob: 794fb1275298c98a4de4f8e8000c8763172c3c46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
From ec9057081f1094fbfeb11449bc533997731e4079 Mon Sep 17 00:00:00 2001
From: Johan Klokkhammer Helsing <johan.helsing@qt.io>
Date: Wed, 19 Jun 2019 14:05:22 +0200
Subject: [PATCH] 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>
---
 src/client/qwaylandwindow.cpp | 59 +++++++++++++++++++++++--------------------
 src/client/qwaylandwindow_p.h |  2 +-
 2 files changed, 32 insertions(+), 29 deletions(-)

diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index cecdbda92..7c8ecadaa 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -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();
+    // Stop the timer and stop waiting immediately
+    int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1);
+    mWaitingForFrameCallback = false;
 
-    if (mFrameCallbackTimerId != -1) {
-        killTimer(mFrameCallbackTimerId);
-        mFrameCallbackTimerId = -1;
-    }
+    // The rest can wait until we can run it on the correct thread
+    auto doHandleExpose = [this, timerId]() {
+        if (timerId != -1)
+            killTimer(timerId);
 
-    mWaitingForFrameCallback = false;
-    mFrameCallbackTimedOut = false;
+        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 (!wasExposed && isExposed())
-        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
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index c47123dc9..e8c9d5684 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -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;
-- 
2.16.3