From 1bef0aa9b8d4d935bb0bd1c44654b99a1326ea77 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Tue, 21 Apr 2020 10:11:41 +0200 Subject: [PATCH 1/4] Don't force gamma-correction off The freetype backend can now do selective gamma-correcting, but need to have a useful gamma-value. Fixes: QTBUG-83636 Change-Id: I89e99d0a63b75d15d70763ee0c2459caa761beee Reviewed-by: David Edmundson Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/plugins/platforms/wayland/qwaylandintegration.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 85c1990b612..2e0d508f974 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -233,13 +233,6 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const if (hint == ShowIsFullScreen && mDisplay->windowManagerIntegration()) return mDisplay->windowManagerIntegration()->showIsFullScreen(); - switch (hint) { - case QPlatformIntegration::FontSmoothingGamma: - return qreal(1.0); - default: - break; - } - return QPlatformIntegration::styleHint(hint); } From 85f4f2827bf07217fdb35ec9afe002c0d1d241c8 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Wed, 8 Apr 2020 18:19:00 +0100 Subject: [PATCH 2/4] Client: Reset wl_egl_window before wl_surface Semantically we want things in this order as wl_egl_window_create takes a reference to the wl_surface. Teardown typically is the opposite of construction. This brings us in line with GTK which also tears down in this order. This fixes a repeatable crash when using Nvidia's EGLStreams and closing a window. Change-Id: I356dfbba9a7315717c8ecdc7023ca7b047622697 Reviewed-by: Frederik Gladhorn Reviewed-by: Johan Helsing --- src/plugins/platforms/wayland/qwaylandwindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 5830c1368c2..9fa2f15f5f7 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -244,6 +244,8 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mShellSurface = nullptr; delete mSubSurfaceWindow; mSubSurfaceWindow = nullptr; + + invalidateSurface(); if (mSurface) { emit wlSurfaceDestroyed(); QWriteLocker lock(&mSurfaceLock); From be03178c4fc0fcc9cdcb142d57e57f71be1e357a Mon Sep 17 00:00:00 2001 From: Janne Koskinen Date: Tue, 24 Mar 2020 14:22:35 +0200 Subject: [PATCH 3/4] Fix leaking of callback timers Use two timers to create timeout trigger for framecallback. Removes multiple timers approach that is subject to race conditions. This was mistakenly pushed to 5.12 branch which will not be automatically merged upstream, so it has to be cherry-picked. Fixes: QTBUG-82914 Reviewed-by: Eskil Abrahamsen Blomfeldt (cherry picked from commit d18c29931b0bc889fff66bdbde89133544ba0529) Change-Id: I258c0c08f0ac5803192fc0024e40ba72e72c37a8 Reviewed-by: Janne Koskinen --- .../platforms/wayland/qwaylandwindow.cpp | 46 ++++++++----------- .../platforms/wayland/qwaylandwindow_p.h | 4 +- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 9fa2f15f5f7..884a9793acd 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -257,10 +257,7 @@ void QWaylandWindow::reset(bool sendDestroyEvent) mFrameCallback = nullptr; } - int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); - if (timerId != -1) { - killTimer(timerId); - } + mFrameCallbackElapsedTimer.invalidate(); mWaitingForFrameCallback = false; mFrameCallbackTimedOut = false; @@ -602,15 +599,11 @@ const wl_callback_listener QWaylandWindow::callbackListener = { void QWaylandWindow::handleFrameCallback() { - // Stop the timer and stop waiting immediately - int timerId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); // The rest can wait until we can run it on the correct thread - auto doHandleExpose = [this, timerId]() { - if (timerId != -1) - killTimer(timerId); - + auto doHandleExpose = [this]() { bool wasExposed = isExposed(); mFrameCallbackTimedOut = false; if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? @@ -645,13 +638,6 @@ bool QWaylandWindow::waitForFrameSync(int timeout) sendExposeEvent(QRect()); } - // Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread - // 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, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); - return !mWaitingForFrameCallback; } @@ -1107,8 +1093,16 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa void QWaylandWindow::timerEvent(QTimerEvent *event) { - if (mFrameCallbackTimerId.testAndSetOrdered(event->timerId(), -1)) { - killTimer(event->timerId()); + if (event->timerId() != mFrameCallbackCheckIntervalTimerId) + return; + + bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(100); + if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { + killTimer(mFrameCallbackCheckIntervalTimerId); + mFrameCallbackCheckIntervalTimerId = -1; + } + if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) { + mFrameCallbackElapsedTimer.invalidate(); qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; mFrameCallbackTimedOut = true; mWaitingForUpdate = false; @@ -1162,15 +1156,13 @@ void QWaylandWindow::handleUpdate() mWaitingForFrameCallback = true; mWaitingForUpdate = false; - // Stop current frame timer if any, can't use killTimer directly, see comment above. - int fcbId = mFrameCallbackTimerId.fetchAndStoreOrdered(-1); - if (fcbId != -1) - QMetaObject::invokeMethod(this, [this, fcbId] { killTimer(fcbId); }, Qt::QueuedConnection); - // Start a timer for handling the case when the compositor stops sending frame callbacks. - QMetaObject::invokeMethod(this, [this] { // Again; can't do it directly - if (mWaitingForFrameCallback) - mFrameCallbackTimerId = startTimer(100); + QMetaObject::invokeMethod(this, [this] { + if (mWaitingForFrameCallback) { + if (mFrameCallbackCheckIntervalTimerId < 0) + mFrameCallbackCheckIntervalTimerId = startTimer(100); + mFrameCallbackElapsedTimer.start(); + } }, Qt::QueuedConnection); } diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 352df89daf6..ef03fd1f459 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -58,6 +58,7 @@ #include #include #include +#include #include @@ -225,7 +226,8 @@ protected: WId mWindowId; bool mWaitingForFrameCallback = false; bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out - QAtomicInt mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback + int mFrameCallbackCheckIntervalTimerId = -1; + QElapsedTimer mFrameCallbackElapsedTimer; struct ::wl_callback *mFrameCallback = nullptr; struct ::wl_event_queue *mFrameQueue = nullptr; QWaitCondition mFrameSyncWait; From 29fa4de2f482a7ae55d2555c280ebdf93a6e17df Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 6 Apr 2020 09:52:33 +0200 Subject: [PATCH 4/4] Client: Make frame back callback timeout configurable To avoid rendering when windows are minimized, we try to detect this case by checking if 100 ms passes after an update before any frame callback arrives. Wayland expects to drive the rendering through frame callbacks, while Qt expects to be informed when a window is no longer visible and will run a tight, vsynced render loop until it gets the appropriate event. This mismatch causes issues, and the timeout is an attempt to avoid actively rendering to a hidden surface by detecting the missing callbacks. It causes problems on embedded, though, when a device is so busy that the timeout happens even when the window is visible. So we introduce a way to configure the timeout. Either increase its duration if you can guarantee a certain minimum time between events even with high load, or set it to 0 to disable it completely. (at the expense of drawing to invisible windows). Note that this is required for fixing a critical memory leak that happens when using Wayland on embedded. This was mistakenly pushed to 5.12 branch, so it will not be automatically merged upstream. Therefore we have to cherry-pick it. [ChangeLog][Client] Added support for QT_WAYLAND_FRAME_CALLBACK_TIMEOUT environment variable, which can be used to disable or change the internal frame callback timeout. If you see windows that stop rendering or minimize on heavy load, then try setting the variable to 0. Task-number: QTBUG-82914 Reviewed-by: David Edmundson (cherry picked from commit 0a0e0eb8dca699858435dec5194b0b8b6ebd3cf8) Change-Id: I2aeeecb0fab8f7be9b838e477c88eae22b322d75 Reviewed-by: Janne Koskinen --- .../platforms/wayland/qwaylandwindow.cpp | 25 +++++++++++++------ .../platforms/wayland/qwaylandwindow_p.h | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 884a9793acd..0545c80f4c7 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -79,6 +79,13 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) , mFrameQueue(mDisplay->createEventQueue()) , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) { + { + bool ok; + int frameCallbackTimeout = qEnvironmentVariableIntValue("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", &ok); + if (ok) + mFrameCallbackTimeout = frameCallbackTimeout; + } + static WId id = 1; mWindowId = id++; initializeWlSurface(); @@ -1096,7 +1103,7 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) if (event->timerId() != mFrameCallbackCheckIntervalTimerId) return; - bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(100); + bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { killTimer(mFrameCallbackCheckIntervalTimerId); mFrameCallbackCheckIntervalTimerId = -1; @@ -1157,13 +1164,15 @@ void QWaylandWindow::handleUpdate() mWaitingForUpdate = false; // Start a timer for handling the case when the compositor stops sending frame callbacks. - QMetaObject::invokeMethod(this, [this] { - if (mWaitingForFrameCallback) { - if (mFrameCallbackCheckIntervalTimerId < 0) - mFrameCallbackCheckIntervalTimerId = startTimer(100); - mFrameCallbackElapsedTimer.start(); - } - }, Qt::QueuedConnection); + if (mFrameCallbackTimeout > 0) { + QMetaObject::invokeMethod(this, [this] { + if (mWaitingForFrameCallback) { + if (mFrameCallbackCheckIntervalTimerId < 0) + mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout); + mFrameCallbackElapsedTimer.start(); + } + }, Qt::QueuedConnection); + } } void QWaylandWindow::deliverUpdateRequest() diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index ef03fd1f459..89d6272d81d 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -240,6 +240,7 @@ protected: bool mCanResize = true; bool mResizeDirty = false; bool mResizeAfterSwap; + int mFrameCallbackTimeout = 100; QVariantMap m_properties; bool mSentInitialResize = false;