From 039fd39e34591ce360d83c792aea7e6efd663a17 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 20 Feb 2024 21:19:18 +0100 Subject: [PATCH] backends/drm: delay cursor updates with adaptive sync While the primary plane delivers a high enough refresh rate for the cursor to be at least usable, delay cursor updates to be in sync with the primary plane. This avoids stutter of the content being shown on the primary plane. Because amdgpu doesn't handle this correctly at the moment, this feature is guarded behind the KWIN_DRM_DELAY_VRR_CURSOR_UPDATES environment variable. See https://gitlab.freedesktop.org/drm/amd/-/issues/2186 for more details on that. --- src/backends/drm/drm_commit_thread.cpp | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp index 1b2d4710b1..0b8e8de7e6 100644 --- a/src/backends/drm/drm_commit_thread.cpp +++ b/src/backends/drm/drm_commit_thread.cpp @@ -22,6 +22,10 @@ namespace KWin */ static constexpr auto s_pageflipTimeout = 5s; +// amdgpu doesn't handle this correctly, so it's off by default +// https://gitlab.freedesktop.org/drm/amd/-/issues/2186 +static const bool s_delayVrrCursorUpdates = qEnvironmentVariableIntValue("KWIN_DRM_DELAY_VRR_CURSOR_UPDATES") == 1; + DrmCommitThread::DrmCommitThread(DrmGpu *gpu, const QString &name) { if (!gpu->atomicModeSetting()) { @@ -73,6 +77,30 @@ DrmCommitThread::DrmCommitThread(DrmGpu *gpu, const QString &name) } continue; } + if (m_commits.front()->isCursorOnly() && m_vrr && s_delayVrrCursorUpdates) { + // wait for a primary plane commit to be in, while still enforcing + // a minimum cursor refresh rate of 30Hz + const auto cursorTarget = m_lastPageflip + std::chrono::duration_cast(1s) / 30; + const bool cursorOnly = std::all_of(m_commits.begin(), m_commits.end(), [](const auto &commit) { + return commit->isCursorOnly(); + }); + if (cursorOnly) { + // no primary plane commit, just wait until a new one gets added or the cursorTarget time is reached + if (m_commitPending.wait_until(lock, cursorTarget) == std::cv_status::no_timeout) { + continue; + } + } else { + bool timeout = true; + while (std::chrono::steady_clock::now() < cursorTarget && timeout && m_commits.front()->isCursorOnly()) { + timeout = m_commitPending.wait_for(lock, 50us) == std::cv_status::timeout; + optimizeCommits(); + } + if (!timeout) { + // some new commit was added, process that + continue; + } + } + } submit(); } }));