backends/drm: don't commit buffers that are still being rendered to
This allows updating the commit until the next vblank happens, making the effect of the dropped frame less visible
This commit is contained in:
parent
a64e43e6b1
commit
bf17d6388c
9 changed files with 101 additions and 16 deletions
|
@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -60,6 +60,12 @@ void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptr<DrmFrameb
|
|||
m_buffers[plane] = buffer;
|
||||
}
|
||||
|
||||
void DrmAtomicCommit::setVrr(DrmCrtc *crtc, bool vrr)
|
||||
{
|
||||
addProperty(crtc->vrrEnabled, 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<DrmFramebuffer> &buffer)
|
||||
: DrmCommit(pipeline->gpu())
|
||||
, m_pipeline(pipeline)
|
||||
|
|
|
@ -58,6 +58,7 @@ public:
|
|||
}
|
||||
void addBlob(const DrmProperty &prop, const std::shared_ptr<DrmBlob> &blob);
|
||||
void addBuffer(DrmPlane *plane, const std::shared_ptr<DrmFramebuffer> &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<DrmPipeline *> m_pipelines;
|
||||
DrmUniquePtr<drmModeAtomicReq> m_req;
|
||||
QHash<const DrmProperty *, std::shared_ptr<DrmBlob>> m_blobs;
|
||||
std::unordered_map<DrmPlane *, std::shared_ptr<DrmFramebuffer>> m_buffers;
|
||||
bool m_vrr = false;
|
||||
};
|
||||
|
||||
class DrmLegacyCommit : public DrmCommit
|
||||
|
|
|
@ -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<DrmAtomicCommit> &&commit, std::chrono::nanoseconds targetPageflipTime)
|
||||
void DrmCommitThread::setCommit(std::unique_ptr<DrmAtomicCommit> &&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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DrmAtomicCommit> &&commit, std::chrono::nanoseconds targetPageflipTime);
|
||||
void setCommit(std::unique_ptr<DrmAtomicCommit> &&commit);
|
||||
bool replaceCommit(std::unique_ptr<DrmAtomicCommit> &&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<DrmAtomicCommit> m_commit;
|
||||
std::unique_ptr<QThread> 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<std::unique_ptr<DrmAtomicCommit>> m_droppedCommits;
|
||||
bool m_vrr = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -873,8 +873,8 @@ std::shared_ptr<DrmFramebuffer> 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<DrmFramebuffer> 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) {
|
||||
|
|
|
@ -175,7 +175,7 @@ DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline
|
|||
Q_ASSERT(pipelines.size() == 1);
|
||||
Q_ASSERT(unusedObjects.isEmpty());
|
||||
const auto pipeline = pipelines.front();
|
||||
pipeline->m_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);
|
||||
|
|
|
@ -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<FileDescriptor, 4> fd;
|
||||
std::array<uint32_t, 4> offset{0, 0, 0, 0};
|
||||
std::array<uint32_t, 4> pitch{0, 0, 0, 0};
|
||||
};
|
||||
|
||||
struct ShmAttributes
|
||||
|
|
Loading…
Reference in a new issue