diff --git a/src/backends/drm/drm_commit.cpp b/src/backends/drm/drm_commit.cpp index 7a0f260c41..ea34d49c54 100644 --- a/src/backends/drm/drm_commit.cpp +++ b/src/backends/drm/drm_commit.cpp @@ -71,8 +71,8 @@ void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptrfbId, buffer ? buffer->framebufferId() : 0); m_buffers[plane] = buffer; m_frames[plane] = frame; - // atomic commits with IN_FENCE_FD fail with NVidia - if (plane->inFenceFd.isValid() && !plane->gpu()->isNVidia()) { + // atomic commits with IN_FENCE_FD fail with NVidia and (as of kernel 6.9) with tearing + if (plane->inFenceFd.isValid() && !plane->gpu()->isNVidia() && !isTearing()) { addProperty(plane->inFenceFd, buffer ? buffer->syncFd().get() : -1); } m_planes.emplace(plane); @@ -98,7 +98,11 @@ void DrmAtomicCommit::setPresentationMode(PresentationMode mode) bool DrmAtomicCommit::test() { - return doCommit(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK); + uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK; + if (isTearing()) { + flags |= DRM_MODE_PAGE_FLIP_ASYNC; + } + return doCommit(flags); } bool DrmAtomicCommit::testAllowModeset() @@ -108,7 +112,11 @@ bool DrmAtomicCommit::testAllowModeset() bool DrmAtomicCommit::commit() { - return doCommit(DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT); + uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; + if (isTearing()) { + flags |= DRM_MODE_PAGE_FLIP_ASYNC; + } + return doCommit(flags); } bool DrmAtomicCommit::commitModeset() @@ -244,6 +252,11 @@ bool DrmAtomicCommit::isReadyFor(std::chrono::steady_clock::time_point pageflipT return (!m_targetPageflipTime || pageflipTarget + s_pageflipSlop >= *m_targetPageflipTime) && areBuffersReadable(); } +bool DrmAtomicCommit::isTearing() const +{ + return m_mode == PresentationMode::Async || m_mode == PresentationMode::AdaptiveAsync; +} + DrmLegacyCommit::DrmLegacyCommit(DrmPipeline *pipeline, const std::shared_ptr &buffer, const std::shared_ptr &frame) : DrmCommit(pipeline->gpu()) , m_pipeline(pipeline) diff --git a/src/backends/drm/drm_commit.h b/src/backends/drm/drm_commit.h index ed9a3399e6..8877b27782 100644 --- a/src/backends/drm/drm_commit.h +++ b/src/backends/drm/drm_commit.h @@ -85,6 +85,7 @@ public: std::optional targetPageflipTime() const; bool isReadyFor(std::chrono::steady_clock::time_point pageflipTarget) const; + bool isTearing() const; private: bool doCommit(uint32_t flags); diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp index b765dd7284..9d30433c23 100644 --- a/src/backends/drm/drm_commit_thread.cpp +++ b/src/backends/drm/drm_commit_thread.cpp @@ -63,7 +63,7 @@ DrmCommitThread::DrmCommitThread(DrmGpu *gpu, const QString &name) optimizeCommits(m_targetPageflipTime); if (!m_commits.front()->isReadyFor(m_targetPageflipTime)) { // no commit is ready yet, reschedule - if (m_vrr) { + if (m_vrr || m_tearing) { m_targetPageflipTime += 50us; } else { m_targetPageflipTime += m_minVblankInterval; @@ -108,6 +108,7 @@ void DrmCommitThread::submit() const bool success = commit->commit(); if (success) { m_vrr = vrr.value_or(m_vrr); + m_tearing = commit->isTearing(); m_committed = std::move(commit); m_commits.erase(m_commits.begin()); } else { @@ -267,7 +268,9 @@ void DrmCommitThread::addCommit(std::unique_ptr &&commit) std::unique_lock lock(m_mutex); m_commits.push_back(std::move(commit)); const auto now = std::chrono::steady_clock::now(); - if (m_vrr && now >= m_lastPageflip + m_minVblankInterval) { + if (m_tearing) { + m_targetPageflipTime = now; + } else if (m_vrr && now >= m_lastPageflip + m_minVblankInterval) { m_targetPageflipTime = now; } else { m_targetPageflipTime = estimateNextVblank(now); diff --git a/src/backends/drm/drm_commit_thread.h b/src/backends/drm/drm_commit_thread.h index 159628f068..7ada259d63 100644 --- a/src/backends/drm/drm_commit_thread.h +++ b/src/backends/drm/drm_commit_thread.h @@ -59,6 +59,7 @@ private: std::chrono::nanoseconds m_minVblankInterval; std::vector> m_commitsToDelete; bool m_vrr = false; + bool m_tearing = false; std::chrono::nanoseconds m_safetyMargin{0}; }; diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index 46d49e0e31..c45c63a9e5 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -40,6 +40,9 @@ #ifndef DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT #define DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT 6 #endif +#ifndef DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP +#define DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP 0x15 +#endif namespace KWin { @@ -89,8 +92,9 @@ DrmGpu::DrmGpu(DrmBackend *backend, int fd, std::unique_ptr &&device) initDrmResources(); if (m_atomicModeSetting == false) { - // only supported with legacy m_asyncPageflipSupported = drmGetCap(fd, DRM_CAP_ASYNC_PAGE_FLIP, &capability) == 0 && capability == 1; + } else { + m_asyncPageflipSupported = drmGetCap(fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &capability) == 0 && capability == 1; } } diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 6fcfee79ae..80b971cf90 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -117,6 +117,12 @@ DrmLease *DrmOutput::lease() const bool DrmOutput::updateCursorLayer() { + const bool tearingDesired = m_desiredPresentationMode == PresentationMode::Async || m_desiredPresentationMode == PresentationMode::AdaptiveAsync; + if (m_pipeline->gpu()->atomicModeSetting() && tearingDesired && m_pipeline->cursorLayer() && m_pipeline->cursorLayer()->isEnabled()) { + // The kernel rejects async commits that change anything but the primary plane FB_ID + // This disables the hardware cursor, so it doesn't interfere with that + return false; + } return m_pipeline->updateCursor(); } @@ -287,6 +293,7 @@ void DrmOutput::updateDpmsMode(DpmsMode dpmsMode) bool DrmOutput::present(const std::shared_ptr &frame) { + m_desiredPresentationMode = frame->presentationMode(); const bool needsModeset = m_gpu->needsModeset(); bool success; if (needsModeset) { diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index ff4837d896..8c010d4606 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -94,6 +94,7 @@ private: QVector3D m_channelFactors = {1, 1, 1}; bool m_channelFactorsNeedShaderFallback = false; ColorDescription m_scanoutColorDescription = ColorDescription::sRGB; + PresentationMode m_desiredPresentationMode = PresentationMode::VSync; }; }