backends/drm: split commits for the cursor and primary plane
And reorder and merge atomic commits where possible to ensure the cursor always stays smooth BUG: 472663
This commit is contained in:
parent
2e42d7193d
commit
4c397a9526
6 changed files with 184 additions and 52 deletions
|
@ -57,6 +57,7 @@ void DrmAtomicCommit::addBuffer(DrmPlane *plane, const std::shared_ptr<DrmFrameb
|
|||
{
|
||||
addProperty(plane->fbId, buffer ? buffer->framebufferId() : 0);
|
||||
m_buffers[plane] = buffer;
|
||||
m_planes.emplace(plane);
|
||||
}
|
||||
|
||||
void DrmAtomicCommit::setVrr(DrmCrtc *crtc, bool vrr)
|
||||
|
@ -139,9 +140,35 @@ bool DrmAtomicCommit::areBuffersReadable() const
|
|||
});
|
||||
}
|
||||
|
||||
bool DrmAtomicCommit::isVrr() const
|
||||
std::optional<bool> DrmAtomicCommit::isVrr() const
|
||||
{
|
||||
return m_vrr;
|
||||
return m_vrr.value_or(false);
|
||||
}
|
||||
|
||||
const std::unordered_set<DrmPlane *> &DrmAtomicCommit::modifiedPlanes() const
|
||||
{
|
||||
return m_planes;
|
||||
}
|
||||
|
||||
void DrmAtomicCommit::merge(DrmAtomicCommit *onTop)
|
||||
{
|
||||
for (const auto &[obj, properties] : onTop->m_properties) {
|
||||
auto &ownProperties = m_properties[obj];
|
||||
for (const auto &[prop, value] : properties) {
|
||||
ownProperties[prop] = value;
|
||||
}
|
||||
}
|
||||
for (const auto &[plane, buffer] : onTop->m_buffers) {
|
||||
m_buffers[plane] = buffer;
|
||||
m_planes.emplace(plane);
|
||||
}
|
||||
for (const auto &[prop, blob] : onTop->m_blobs) {
|
||||
m_blobs[prop] = blob;
|
||||
}
|
||||
if (onTop->m_vrr) {
|
||||
m_vrr = onTop->m_vrr;
|
||||
}
|
||||
m_cursorOnly &= onTop->isCursorOnly();
|
||||
}
|
||||
|
||||
void DrmAtomicCommit::setCursorOnly(bool cursor)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QHash>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "drm_pointer.h"
|
||||
#include "drm_property.h"
|
||||
|
@ -49,6 +50,7 @@ class DrmAtomicCommit : public DrmCommit
|
|||
{
|
||||
public:
|
||||
DrmAtomicCommit(const QVector<DrmPipeline *> &pipelines);
|
||||
DrmAtomicCommit(const DrmAtomicCommit ©) = default;
|
||||
|
||||
void addProperty(const DrmProperty &prop, uint64_t value);
|
||||
template<typename T>
|
||||
|
@ -68,7 +70,10 @@ public:
|
|||
void pageFlipped(std::chrono::nanoseconds timestamp) const override;
|
||||
|
||||
bool areBuffersReadable() const;
|
||||
bool isVrr() const;
|
||||
std::optional<bool> isVrr() const;
|
||||
const std::unordered_set<DrmPlane *> &modifiedPlanes() const;
|
||||
|
||||
void merge(DrmAtomicCommit *onTop);
|
||||
|
||||
void setCursorOnly(bool cursor);
|
||||
bool isCursorOnly() const;
|
||||
|
@ -77,9 +82,10 @@ private:
|
|||
bool doCommit(uint32_t flags);
|
||||
|
||||
const QVector<DrmPipeline *> m_pipelines;
|
||||
QHash<const DrmProperty *, std::shared_ptr<DrmBlob>> m_blobs;
|
||||
std::unordered_map<const DrmProperty *, std::shared_ptr<DrmBlob>> m_blobs;
|
||||
std::unordered_map<DrmPlane *, std::shared_ptr<DrmFramebuffer>> m_buffers;
|
||||
bool m_vrr = false;
|
||||
std::unordered_set<DrmPlane *> m_planes;
|
||||
std::optional<bool> m_vrr;
|
||||
std::unordered_map<uint32_t /* object */, std::unordered_map<uint32_t /* property */, uint64_t /* value */>> m_properties;
|
||||
bool m_cursorOnly = false;
|
||||
};
|
||||
|
|
|
@ -44,11 +44,10 @@ DrmCommitThread::DrmCommitThread()
|
|||
std::this_thread::sleep_until(m_targetPageflipTime - s_safetyMargin);
|
||||
lock.lock();
|
||||
}
|
||||
const auto it = std::find_if(m_commits.rbegin(), m_commits.rend(), [](const auto &commit) {
|
||||
return commit->areBuffersReadable();
|
||||
});
|
||||
if (it == m_commits.rend()) {
|
||||
// reschedule, this commit would not hit the pageflip deadline anyways
|
||||
optimizeCommits();
|
||||
auto &commit = m_commits.front();
|
||||
if (!commit->areBuffersReadable()) {
|
||||
// no commit is ready yet, reschedule
|
||||
if (m_vrr) {
|
||||
m_targetPageflipTime += 50us;
|
||||
} else {
|
||||
|
@ -56,21 +55,14 @@ DrmCommitThread::DrmCommitThread()
|
|||
}
|
||||
continue;
|
||||
}
|
||||
auto &commit = *it;
|
||||
commit->setCursorOnly(std::all_of(m_commits.begin(), it.base(), [](const auto &commit) {
|
||||
return commit->isCursorOnly();
|
||||
}));
|
||||
const bool vrr = commit->isVrr();
|
||||
const auto vrr = commit->isVrr();
|
||||
const bool success = commit->commit();
|
||||
if (success) {
|
||||
m_pageflipPending = true;
|
||||
m_vrr = vrr;
|
||||
m_vrr = vrr.value_or(m_vrr);
|
||||
// the atomic commit takes ownership of the object
|
||||
commit.release();
|
||||
// commits that were pushed earlier are outdated and can be dropped
|
||||
std::move(m_commits.begin(), it.base(), std::back_inserter(m_droppedCommits));
|
||||
m_commits.erase(m_commits.begin(), it.base());
|
||||
// commits that came after will be rescheduled once the pageflip is done
|
||||
m_commits.erase(m_commits.begin());
|
||||
} else {
|
||||
for (auto &commit : m_commits) {
|
||||
m_droppedCommits.push_back(std::move(commit));
|
||||
|
@ -86,6 +78,98 @@ DrmCommitThread::DrmCommitThread()
|
|||
m_thread->start();
|
||||
}
|
||||
|
||||
void DrmCommitThread::optimizeCommits()
|
||||
{
|
||||
if (m_commits.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
// merge commits in the front that are already ready (regardless of which planes they modify)
|
||||
if (m_commits.front()->areBuffersReadable()) {
|
||||
auto it = m_commits.begin() + 1;
|
||||
while (it != m_commits.end() && (*it)->areBuffersReadable()) {
|
||||
m_commits.front()->merge(it->get());
|
||||
m_droppedCommits.push_back(std::move(*it));
|
||||
it = m_commits.erase(it);
|
||||
}
|
||||
}
|
||||
// merge commits that are ready and modify the same drm planes
|
||||
for (auto it = m_commits.begin(); it != m_commits.end();) {
|
||||
DrmAtomicCommit *const commit = it->get();
|
||||
it++;
|
||||
while (it != m_commits.end() && commit->modifiedPlanes() == (*it)->modifiedPlanes() && (*it)->areBuffersReadable()) {
|
||||
commit->merge(it->get());
|
||||
m_droppedCommits.push_back(std::move(*it));
|
||||
it = m_commits.erase(it);
|
||||
}
|
||||
}
|
||||
if (m_commits.size() == 1) {
|
||||
// already done
|
||||
return;
|
||||
}
|
||||
std::unique_ptr<DrmAtomicCommit> front;
|
||||
if (m_commits.front()->areBuffersReadable()) {
|
||||
front = std::move(m_commits.front());
|
||||
m_commits.erase(m_commits.begin());
|
||||
}
|
||||
// try to move commits that are ready to the front
|
||||
for (auto it = m_commits.begin() + 1; it != m_commits.end();) {
|
||||
auto &commit = *it;
|
||||
// commits that target the same plane(s) need to stay in the same order
|
||||
const auto &planes = commit->modifiedPlanes();
|
||||
const bool skipping = std::any_of(m_commits.begin(), it, [&planes](const auto &other) {
|
||||
return std::any_of(planes.begin(), planes.end(), [&other](DrmPlane *plane) {
|
||||
return other->modifiedPlanes().contains(plane);
|
||||
});
|
||||
});
|
||||
if (skipping || !commit->areBuffersReadable()) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
// find out if the modified commit order will actually work
|
||||
std::unique_ptr<DrmAtomicCommit> duplicate;
|
||||
if (front) {
|
||||
duplicate = std::make_unique<DrmAtomicCommit>(*front);
|
||||
duplicate->merge(commit.get());
|
||||
if (!duplicate->test()) {
|
||||
m_droppedCommits.push_back(std::move(duplicate));
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (!commit->test()) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
duplicate = std::make_unique<DrmAtomicCommit>(*commit);
|
||||
}
|
||||
bool success = true;
|
||||
for (const auto &otherCommit : m_commits) {
|
||||
if (otherCommit != commit) {
|
||||
duplicate->merge(otherCommit.get());
|
||||
if (!duplicate->test()) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_droppedCommits.push_back(std::move(duplicate));
|
||||
if (success) {
|
||||
if (front) {
|
||||
front->merge(commit.get());
|
||||
m_droppedCommits.push_back(std::move(commit));
|
||||
} else {
|
||||
front = std::move(commit);
|
||||
}
|
||||
it = m_commits.erase(it);
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
if (front) {
|
||||
m_commits.insert(m_commits.begin(), std::move(front));
|
||||
}
|
||||
}
|
||||
|
||||
DrmCommitThread::~DrmCommitThread()
|
||||
{
|
||||
m_thread->requestInterruption();
|
||||
|
|
|
@ -41,6 +41,7 @@ Q_SIGNALS:
|
|||
private:
|
||||
void clearDroppedCommits();
|
||||
TimePoint estimateNextVblank(TimePoint now) const;
|
||||
void optimizeCommits();
|
||||
|
||||
std::vector<std::unique_ptr<DrmAtomicCommit>> m_commits;
|
||||
std::unique_ptr<QThread> m_thread;
|
||||
|
|
|
@ -78,7 +78,24 @@ DrmPipeline::Error DrmPipeline::present()
|
|||
{
|
||||
Q_ASSERT(m_pending.crtc);
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
return commitPipelines({this}, CommitMode::Commit);
|
||||
// test the full state, to take pending commits into account
|
||||
auto fullState = std::make_unique<DrmAtomicCommit>(QVector<DrmPipeline *>{this});
|
||||
if (!prepareAtomicPresentation(fullState.get())) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
if (m_pending.crtc->cursorPlane()) {
|
||||
prepareAtomicCursor(fullState.get());
|
||||
}
|
||||
if (!fullState->test()) {
|
||||
return errnoToError();
|
||||
}
|
||||
// only give the actual state update to the commit thread, so that it can potentially reorder the commits
|
||||
auto primaryPlaneUpdate = std::make_unique<DrmAtomicCommit>(QVector<DrmPipeline *>{this});
|
||||
if (!prepareAtomicPresentation(primaryPlaneUpdate.get())) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
m_commitThread->addCommit(std::move(primaryPlaneUpdate));
|
||||
return Error::None;
|
||||
} else {
|
||||
if (m_primaryLayer->hasDirectScanoutBuffer()) {
|
||||
// already presented
|
||||
|
@ -126,6 +143,9 @@ DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline
|
|||
if (!pipeline->prepareAtomicPresentation(commit.get())) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
if (pipeline->m_pending.crtc->cursorPlane()) {
|
||||
pipeline->prepareAtomicCursor(commit.get());
|
||||
}
|
||||
if (mode == CommitMode::TestAllowModeset || mode == CommitMode::CommitModeset) {
|
||||
if (!pipeline->prepareAtomicModeset(commit.get())) {
|
||||
return Error::InvalidArguments;
|
||||
|
@ -170,24 +190,6 @@ DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline
|
|||
}
|
||||
return Error::None;
|
||||
}
|
||||
case CommitMode::Commit: {
|
||||
Q_ASSERT(pipelines.size() == 1);
|
||||
Q_ASSERT(unusedObjects.isEmpty());
|
||||
const auto pipeline = pipelines.front();
|
||||
pipeline->m_commitThread->addCommit(std::move(commit));
|
||||
pipeline->atomicCommitSuccessful();
|
||||
return Error::None;
|
||||
}
|
||||
case CommitMode::CommitCursor: {
|
||||
commit->setCursorOnly(true);
|
||||
if (!commit->test()) {
|
||||
return errnoToError();
|
||||
}
|
||||
const auto pipeline = pipelines.front();
|
||||
pipeline->m_commitThread->addCommit(std::move(commit));
|
||||
pipeline->atomicCommitSuccessful();
|
||||
return Error::None;
|
||||
};
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
@ -231,16 +233,18 @@ bool DrmPipeline::prepareAtomicPresentation(DrmAtomicCommit *commit)
|
|||
const auto fb = m_primaryLayer->currentBuffer();
|
||||
m_pending.crtc->primaryPlane()->set(commit, QPoint(0, 0), fb->buffer()->size(), centerBuffer(fb->buffer()->size(), m_pending.mode->size()));
|
||||
commit->addBuffer(m_pending.crtc->primaryPlane(), fb);
|
||||
|
||||
if (auto plane = m_pending.crtc->cursorPlane()) {
|
||||
const auto layer = cursorLayer();
|
||||
plane->set(commit, QPoint(0, 0), gpu()->cursorSize(), QRect(layer->position().toPoint(), gpu()->cursorSize()));
|
||||
commit->addProperty(plane->crtcId, layer->isEnabled() ? m_pending.crtc->id() : 0);
|
||||
commit->addBuffer(plane, layer->isEnabled() ? layer->currentBuffer() : nullptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmPipeline::prepareAtomicCursor(DrmAtomicCommit *commit)
|
||||
{
|
||||
auto plane = m_pending.crtc->cursorPlane();
|
||||
const auto layer = cursorLayer();
|
||||
plane->set(commit, QPoint(0, 0), gpu()->cursorSize(), QRect(layer->position().toPoint(), gpu()->cursorSize()));
|
||||
commit->addProperty(plane->crtcId, layer->isEnabled() ? m_pending.crtc->id() : 0);
|
||||
commit->addBuffer(plane, layer->isEnabled() ? layer->currentBuffer() : nullptr);
|
||||
}
|
||||
|
||||
void DrmPipeline::prepareAtomicDisable(DrmAtomicCommit *commit)
|
||||
{
|
||||
m_connector->disable(commit);
|
||||
|
@ -318,6 +322,7 @@ bool DrmPipeline::prepareAtomicModeset(DrmAtomicCommit *commit)
|
|||
if (cursor->pixelBlendMode.isValid()) {
|
||||
commit->addEnum(cursor->pixelBlendMode, DrmPlane::PixelBlendMode::PreMultiplied);
|
||||
}
|
||||
prepareAtomicCursor(commit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -362,17 +367,27 @@ bool DrmPipeline::updateCursor()
|
|||
if (!m_pending.crtc) {
|
||||
return false;
|
||||
}
|
||||
bool result;
|
||||
// explicitly check for the cursor plane and not for AMS, as we might not always have one
|
||||
if (m_pending.crtc->cursorPlane()) {
|
||||
if (needsModeset() || !m_current.active) {
|
||||
return false;
|
||||
}
|
||||
result = commitPipelines({this}, CommitMode::CommitCursor) == Error::None;
|
||||
// test the full state, to take pending commits into account
|
||||
auto fullState = std::make_unique<DrmAtomicCommit>(QVector<DrmPipeline *>{this});
|
||||
prepareAtomicPresentation(fullState.get());
|
||||
prepareAtomicCursor(fullState.get());
|
||||
if (!fullState->test()) {
|
||||
return false;
|
||||
}
|
||||
// only give the actual state update to the commit thread, so that it can potentially reorder the commits
|
||||
auto cursorOnly = std::make_unique<DrmAtomicCommit>(QVector<DrmPipeline *>{this});
|
||||
prepareAtomicCursor(cursorOnly.get());
|
||||
cursorOnly->setCursorOnly(true);
|
||||
m_commitThread->addCommit(std::move(cursorOnly));
|
||||
return true;
|
||||
} else {
|
||||
result = setCursorLegacy();
|
||||
return setCursorLegacy();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void DrmPipeline::applyPendingChanges()
|
||||
|
|
|
@ -136,8 +136,6 @@ public:
|
|||
enum class CommitMode {
|
||||
Test,
|
||||
TestAllowModeset,
|
||||
Commit,
|
||||
CommitCursor,
|
||||
CommitModeset
|
||||
};
|
||||
Q_ENUM(CommitMode)
|
||||
|
@ -161,6 +159,7 @@ private:
|
|||
void atomicCommitSuccessful();
|
||||
bool prepareAtomicModeset(DrmAtomicCommit *commit);
|
||||
bool prepareAtomicPresentation(DrmAtomicCommit *commit);
|
||||
void prepareAtomicCursor(DrmAtomicCommit *commit);
|
||||
void prepareAtomicDisable(DrmAtomicCommit *commit);
|
||||
static Error commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects);
|
||||
|
||||
|
|
Loading…
Reference in a new issue