diff --git a/src/backends/drm/drm_buffer.cpp b/src/backends/drm/drm_buffer.cpp index e52c4762b3..0f0b8b71f4 100644 --- a/src/backends/drm/drm_buffer.cpp +++ b/src/backends/drm/drm_buffer.cpp @@ -52,4 +52,16 @@ void DrmFramebuffer::releaseBuffer() { m_bufferRef = nullptr; } + +bool DrmFramebuffer::isReadable() +{ + if (m_readable) { + return true; + } else { + const auto &fds = m_bufferRef->dmabufAttributes()->fd; + return m_readable = std::all_of(fds.begin(), fds.end(), [](const auto &fd) { + return !fd.isValid() || fd.isReadable(); + }); + } +} } diff --git a/src/backends/drm/drm_buffer.h b/src/backends/drm/drm_buffer.h index 182fe3e5b6..4d5145c60d 100644 --- a/src/backends/drm/drm_buffer.h +++ b/src/backends/drm/drm_buffer.h @@ -30,11 +30,13 @@ public: GraphicsBuffer *buffer() const; void releaseBuffer(); + bool isReadable(); protected: const uint32_t m_framebufferId; DrmGpu *const m_gpu; GraphicsBufferRef m_bufferRef; + bool m_readable = false; }; } diff --git a/src/backends/drm/drm_commit.cpp b/src/backends/drm/drm_commit.cpp index df2053ca94..b166020106 100644 --- a/src/backends/drm/drm_commit.cpp +++ b/src/backends/drm/drm_commit.cpp @@ -60,6 +60,12 @@ void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptrvrrEnabled, vrr ? 1 : 0); + m_vrr = vrr; +} + bool DrmAtomicCommit::test() { return drmModeAtomicCommit(gpu()->fd(), m_req.get(), DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK, this) == 0; @@ -96,6 +102,19 @@ void DrmAtomicCommit::pageFlipped(std::chrono::nanoseconds timestamp) const } } +bool DrmAtomicCommit::areBuffersReadable() const +{ + return std::all_of(m_buffers.begin(), m_buffers.end(), [](const auto &pair) { + const auto &[plane, buffer] = pair; + return !buffer || buffer->isReadable(); + }); +} + +bool DrmAtomicCommit::isVrr() const +{ + return m_vrr; +} + DrmLegacyCommit::DrmLegacyCommit(DrmPipeline *pipeline, const std::shared_ptr &buffer) : DrmCommit(pipeline->gpu()) , m_pipeline(pipeline) diff --git a/src/backends/drm/drm_commit.h b/src/backends/drm/drm_commit.h index e3817a7822..1106bed386 100644 --- a/src/backends/drm/drm_commit.h +++ b/src/backends/drm/drm_commit.h @@ -58,6 +58,7 @@ public: } void addBlob(const DrmProperty &prop, const std::shared_ptr &blob); void addBuffer(DrmPlane *plane, const std::shared_ptr &buffer); + void setVrr(DrmCrtc *crtc, bool vrr); bool test(); bool testAllowModeset(); @@ -68,11 +69,15 @@ public: drmModeAtomicReq *req() const; + bool areBuffersReadable() const; + bool isVrr() const; + private: const QVector m_pipelines; DrmUniquePtr m_req; QHash> m_blobs; std::unordered_map> m_buffers; + bool m_vrr = false; }; class DrmLegacyCommit : public DrmCommit diff --git a/src/backends/drm/drm_commit_thread.cpp b/src/backends/drm/drm_commit_thread.cpp index ebe0aa8703..65601e1870 100644 --- a/src/backends/drm/drm_commit_thread.cpp +++ b/src/backends/drm/drm_commit_thread.cpp @@ -12,12 +12,14 @@ #include "logging_p.h" #include "utils/realtime.h" +using namespace std::chrono_literals; + namespace KWin { // This value was chosen experimentally and should be adjusted if needed // committing takes about 800µs, the rest is accounting for sleep not being accurate enough -static constexpr std::chrono::microseconds s_safetyMargin(1800); +static constexpr auto s_safetyMargin = 1800us; DrmCommitThread::DrmCommitThread() { @@ -32,15 +34,26 @@ DrmCommitThread::DrmCommitThread() m_commitPending.wait(lock); } if (m_commit) { - const auto now = std::chrono::steady_clock::now().time_since_epoch(); + const auto now = std::chrono::steady_clock::now(); if (m_targetPageflipTime > now + s_safetyMargin) { lock.unlock(); - std::this_thread::sleep_for(m_targetPageflipTime - s_safetyMargin - now); + std::this_thread::sleep_until(m_targetPageflipTime - s_safetyMargin); lock.lock(); } // the other thread may replace the commit, but not erase it Q_ASSERT(m_commit); + if (!m_commit->areBuffersReadable()) { + // reschedule, this commit would not hit the pageflip deadline anyways + if (m_vrr) { + m_targetPageflipTime += 50us; + } else { + m_targetPageflipTime += m_minVblankInterval; + } + continue; + } + const bool vrr = m_commit->isVrr(); if (m_commit->commit()) { + m_vrr = vrr; // the atomic commit takes ownership of the object m_commit.release(); } else { @@ -62,11 +75,16 @@ DrmCommitThread::~DrmCommitThread() m_thread->wait(); } -void DrmCommitThread::setCommit(std::unique_ptr &&commit, std::chrono::nanoseconds targetPageflipTime) +void DrmCommitThread::setCommit(std::unique_ptr &&commit) { std::unique_lock lock(m_mutex); m_commit = std::move(commit); - m_targetPageflipTime = targetPageflipTime; + const auto now = std::chrono::steady_clock::now(); + if (m_vrr && now >= m_lastPageflip + m_minVblankInterval) { + m_targetPageflipTime = now; + } else { + m_targetPageflipTime = estimateNextVblank(now); + } m_commitPending.notify_all(); } @@ -87,4 +105,22 @@ void DrmCommitThread::clearDroppedCommits() std::unique_lock lock(m_mutex); m_droppedCommits.clear(); } + +void DrmCommitThread::setRefreshRate(uint32_t maximum) +{ + std::unique_lock lock(m_mutex); + m_minVblankInterval = std::chrono::nanoseconds(1'000'000'000'000ull / maximum); +} + +void DrmCommitThread::pageFlipped(std::chrono::nanoseconds timestamp) +{ + std::unique_lock lock(m_mutex); + m_lastPageflip = TimePoint(timestamp); +} + +TimePoint DrmCommitThread::estimateNextVblank(TimePoint now) const +{ + const uint64_t pageflipsSince = (now - m_lastPageflip) / m_minVblankInterval; + return m_lastPageflip + m_minVblankInterval * (pageflipsSince + 1); +} } diff --git a/src/backends/drm/drm_commit_thread.h b/src/backends/drm/drm_commit_thread.h index 156317c4ed..2333e89b1a 100644 --- a/src/backends/drm/drm_commit_thread.h +++ b/src/backends/drm/drm_commit_thread.h @@ -20,6 +20,8 @@ namespace KWin class DrmGpu; class DrmAtomicCommit; +using TimePoint = std::chrono::steady_clock::time_point; + class DrmCommitThread : public QObject { Q_OBJECT @@ -27,21 +29,28 @@ public: explicit DrmCommitThread(); ~DrmCommitThread(); - void setCommit(std::unique_ptr &&commit, std::chrono::nanoseconds targetPageflipTime); + void setCommit(std::unique_ptr &&commit); bool replaceCommit(std::unique_ptr &&commit); + void setRefreshRate(uint32_t maximum); + void pageFlipped(std::chrono::nanoseconds timestamp); + Q_SIGNALS: void commitFailed(); private: void clearDroppedCommits(); + TimePoint estimateNextVblank(TimePoint now) const; std::unique_ptr m_commit; std::unique_ptr m_thread; std::mutex m_mutex; std::condition_variable m_commitPending; - std::chrono::nanoseconds m_targetPageflipTime; + TimePoint m_lastPageflip; + TimePoint m_targetPageflipTime; + std::chrono::nanoseconds m_minVblankInterval; std::vector> m_droppedCommits; + bool m_vrr = false; }; } diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index 1e5c92006b..528ef15c60 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -873,8 +873,8 @@ std::shared_ptr DrmGpu::importBuffer(GraphicsBuffer *buffer) attributes->height, attributes->format, handles, - attributes->pitch, - attributes->offset, + attributes->pitch.data(), + attributes->offset.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS); @@ -884,8 +884,8 @@ std::shared_ptr DrmGpu::importBuffer(GraphicsBuffer *buffer) attributes->height, attributes->format, handles, - attributes->pitch, - attributes->offset, + attributes->pitch.data(), + attributes->offset.data(), &framebufferId, 0); if (ret == EOPNOTSUPP && attributes->planeCount == 1) { diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 884616b3e0..c76b4c4d35 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -175,7 +175,7 @@ DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVectorm_commitThread->setCommit(std::move(commit), pipeline->m_output->renderLoop()->nextPresentationTimestamp()); + pipeline->m_commitThread->setCommit(std::move(commit)); pipeline->atomicCommitSuccessful(); return Error::None; } @@ -218,7 +218,7 @@ bool DrmPipeline::prepareAtomicPresentation(DrmAtomicCommit *commit) } if (m_pending.crtc->vrrEnabled.isValid()) { - commit->addProperty(m_pending.crtc->vrrEnabled, m_pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive || m_pending.syncMode == RenderLoopPrivate::SyncMode::AdaptiveAsync); + commit->setVrr(m_pending.crtc, m_pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive || m_pending.syncMode == RenderLoopPrivate::SyncMode::AdaptiveAsync); } if (m_pending.crtc->gammaLut.isValid()) { commit->addBlob(m_pending.crtc->gammaLut, m_pending.gamma ? m_pending.gamma->blob() : nullptr); @@ -405,6 +405,7 @@ bool DrmPipeline::moveCursor() void DrmPipeline::applyPendingChanges() { m_next = m_pending; + m_commitThread->setRefreshRate(m_pending.mode->refreshRate()); } DrmConnector *DrmPipeline::connector() const @@ -419,6 +420,7 @@ DrmGpu *DrmPipeline::gpu() const void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp) { + m_commitThread->pageFlipped(timestamp); m_pageflipPending = false; if (m_output && activePending()) { m_output->pageFlipped(timestamp); diff --git a/src/core/graphicsbuffer.h b/src/core/graphicsbuffer.h index ac97ee2c71..4772aa35ac 100644 --- a/src/core/graphicsbuffer.h +++ b/src/core/graphicsbuffer.h @@ -24,9 +24,9 @@ struct DmaBufAttributes uint32_t format = 0; uint64_t modifier = 0; - FileDescriptor fd[4]; - uint32_t offset[4] = {0, 0, 0, 0}; - uint32_t pitch[4] = {0, 0, 0, 0}; + std::array fd; + std::array offset{0, 0, 0, 0}; + std::array pitch{0, 0, 0, 0}; }; struct ShmAttributes