platforms/drm: disable unused resources on modesets

When we switch CRTCs it can happen that a CRTC would stay enabled yet has
no connectors anymore. In this case the kernel may reject our atomic commit,
which would cause the modeset to fail. To counteract that, properly disable
unused drm objects
This commit is contained in:
Xaver Hugl 2021-10-08 11:22:02 +02:00
parent 102fb3d5f2
commit 7283c98f27
11 changed files with 101 additions and 14 deletions

View file

@ -177,6 +177,7 @@ void DrmGpu::initDrmResources()
DrmPlane *p = new DrmPlane(this, kplane->plane_id);
if (p->init()) {
m_planes << p;
m_allObjects << p;
} else {
delete p;
}
@ -199,13 +200,19 @@ void DrmGpu::initDrmResources()
}
auto planes = m_planes;
for (int i = 0; i < resources->count_crtcs; ++i) {
uint32_t crtcId = resources->crtcs[i];
DrmPlane *primary = nullptr;
DrmPlane *cursor = nullptr;
for (const auto &plane : qAsConst(planes)) {
if (plane->type() == DrmPlane::TypeIndex::Primary
&& plane->isCrtcSupported(i)) {
primary = plane;
if (plane->getProp(DrmPlane::PropertyIndex::CrtcId)->current() == resources->crtcs[i]) {
break;
if (plane->isCrtcSupported(i)) {
if (plane->type() == DrmPlane::TypeIndex::Primary) {
if (!primary || primary->getProp(DrmPlane::PropertyIndex::CrtcId)->pending() == crtcId) {
primary = plane;
}
} else if (plane->type() == DrmPlane::TypeIndex::Cursor) {
if (!cursor || cursor->getProp(DrmPlane::PropertyIndex::CrtcId)->pending() == crtcId) {
cursor = plane;
}
}
}
}
@ -214,12 +221,13 @@ void DrmGpu::initDrmResources()
continue;
}
planes.removeOne(primary);
auto c = new DrmCrtc(this, resources->crtcs[i], i, primary);
auto c = new DrmCrtc(this, crtcId, i, primary, cursor);
if (!c->init()) {
delete c;
continue;
}
m_crtcs << c;
m_allObjects << c;
}
}
@ -263,6 +271,7 @@ bool DrmGpu::updateOutputs()
continue;
}
m_connectors << conn;
m_allObjects << conn;
} else {
removedConnectors.removeOne(conn);
conn->updateProperties();
@ -299,6 +308,7 @@ bool DrmGpu::updateOutputs()
removeLeaseOutput(leaseOutput);
}
m_connectors.removeOne(connector);
m_allObjects.removeOne(connector);
delete connector;
}
@ -348,13 +358,14 @@ bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector*> connectors, QVector<DrmC
leasePipelines << output->pipeline();
}
}
bool test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
const auto unused = unusedObjects();
bool test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unused);
if (!leasePipelines.isEmpty() && test) {
// non-desktop outputs should be disabled for normal usage
for (const auto &pipeline : qAsConst(leasePipelines)) {
pipeline->pending.active = false;
}
bool ret = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
bool ret = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unused);
if (ret) {
for (const auto &pipeline : qAsConst(leasePipelines)) {
pipeline->applyPendingChanges();
@ -728,6 +739,8 @@ bool DrmGpu::needsModeset() const
{
return std::any_of(m_pipelines.constBegin(), m_pipelines.constEnd(), [](const auto &pipeline) {
return pipeline->needsModeset();
}) || std::any_of(m_allObjects.constBegin(), m_allObjects.constEnd(), [](const auto &object) {
return object->needsModeset();
});
}
@ -743,11 +756,28 @@ bool DrmGpu::maybeModeset()
return pipeline->modesetPresentPending() || !pipeline->pending.active;
});
if (presentPendingForAll) {
return DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset);
return DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset, unusedObjects());
} else {
// commit only once all pipelines are ready for presentation
return true;
}
}
QVector<DrmObject*> DrmGpu::unusedObjects() const
{
if (!m_atomicModeSetting) {
return {};
}
QVector<DrmObject*> ret = m_allObjects;
for (const auto &pipeline : m_pipelines) {
ret.removeOne(pipeline->connector());
if (pipeline->pending.crtc) {
ret.removeOne(pipeline->pending.crtc);
ret.removeOne(pipeline->pending.crtc->primaryPlane());
ret.removeOne(pipeline->pending.crtc->cursorPlane());
}
}
return ret;
}
}

View file

@ -101,6 +101,7 @@ private:
void initDrmResources();
bool checkCrtcAssignment(QVector<DrmConnector*> connectors, QVector<DrmCrtc*> crtcs);
QVector<DrmObject*> unusedObjects() const;
void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest);
void handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease);
@ -123,6 +124,7 @@ private:
QVector<DrmPlane*> m_planes;
QVector<DrmCrtc*> m_crtcs;
QVector<DrmConnector*> m_connectors;
QVector<DrmObject*> m_allObjects;
QVector<DrmPipeline*> m_pipelines;
QVector<DrmOutput*> m_drmOutputs;

View file

@ -36,6 +36,11 @@ public:
*/
virtual bool init() = 0;
/**
* Set the properties in such a way that this resource won't be used anymore
*/
virtual void disable() = 0;
uint32_t id() const;
DrmGpu *gpu() const;
uint32_t type() const;

View file

@ -369,6 +369,11 @@ DrmPipeline *DrmConnector::pipeline() const
return m_pipeline.data();
}
void DrmConnector::disable()
{
setPending(PropertyIndex::CrtcId, 0);
}
QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj)
{
QDebugStateSaver saver(s);

View file

@ -53,6 +53,7 @@ public:
bool init() override;
bool needsModeset() const override;
bool updateProperties() override;
void disable() override;
QVector<uint32_t> encoders() const;
bool isConnected() const;

View file

@ -18,7 +18,7 @@
namespace KWin
{
DrmCrtc::DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane)
DrmCrtc::DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane, DrmPlane *cursorPlane)
: DrmObject(gpu, crtcId, {
PropertyDefinition(QByteArrayLiteral("MODE_ID"), Requirement::Required),
PropertyDefinition(QByteArrayLiteral("ACTIVE"), Requirement::Required),
@ -29,6 +29,7 @@ DrmCrtc::DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryP
, m_crtc(drmModeGetCrtc(gpu->fd(), crtcId))
, m_pipeIndex(pipeIndex)
, m_primaryPlane(primaryPlane)
, m_cursorPlane(cursorPlane)
{
}
@ -147,4 +148,15 @@ DrmPlane *DrmCrtc::primaryPlane() const
return m_primaryPlane;
}
DrmPlane *DrmCrtc::cursorPlane() const
{
return m_cursorPlane;
}
void DrmCrtc::disable()
{
setPending(PropertyIndex::Active, 0);
setPendingBlob(PropertyIndex::ModeId, nullptr, sizeof(drmModeModeInfo));
}
}

View file

@ -27,7 +27,7 @@ class DrmPlane;
class DrmCrtc : public DrmObject
{
public:
DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane);
DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane, DrmPlane *cursorPlane);
enum class PropertyIndex : uint32_t {
ModeId = 0,
@ -40,10 +40,12 @@ public:
bool init() override;
bool needsModeset() const override;
void disable() override;
int pipeIndex() const;
int gammaRampSize() const;
DrmPlane *primaryPlane() const;
DrmPlane *cursorPlane() const;
drmModeModeInfo queryCurrentMode();
QSharedPointer<DrmBuffer> current() const;
@ -64,6 +66,8 @@ private:
QSharedPointer<DrmBuffer> m_nextBuffer;
int m_pipeIndex;
DrmPlane *m_primaryPlane;
// currently unused but needs to be filtered out for modesets
DrmPlane *m_cursorPlane;
struct {
QPoint pos;

View file

@ -208,4 +208,10 @@ DrmPlane::Transformations DrmPlane::supportedTransformations() const
return m_supportedTransformations;
}
void DrmPlane::disable()
{
setPending(PropertyIndex::CrtcId, 0);
setPending(PropertyIndex::FbId, 0);
}
}

View file

@ -65,6 +65,7 @@ public:
bool init() override;
bool needsModeset() const override;
void disable() override;
TypeIndex type();
bool isCrtcSupported(int pipeIndex) const;

View file

@ -111,7 +111,7 @@ bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
return true;
}
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode)
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
{
Q_ASSERT(!pipelines.isEmpty());
if (pipelines[0]->gpu()->atomicModeSetting()) {
@ -121,7 +121,7 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
return false;
}
uint32_t flags = 0;
const auto &failed = [pipelines, req, mode](){
const auto &failed = [pipelines, req, mode, unusedObjects](){
drmModeAtomicFree(req);
for (const auto &pipeline : pipelines) {
pipeline->printDebugInfo();
@ -139,6 +139,9 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
pipeline->output()->presentFailed();
}
}
for (const auto &obj : unusedObjects) {
obj->rollbackPending();
}
return false;
};
for (const auto &pipeline : pipelines) {
@ -151,6 +154,16 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
return failed();
}
}
for (const auto &unused : unusedObjects) {
unused->disable();
if (unused->needsModeset()) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
}
if (!unused->atomicPopulate(req)) {
qCWarning(KWIN_DRM) << "Populating atomic values failed for unused resource" << unused;
return failed();
}
}
bool modeset = flags & DRM_MODE_ATOMIC_ALLOW_MODESET;
Q_ASSERT(!modeset || mode != CommitMode::Commit);
if (modeset) {
@ -176,6 +189,7 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
if (pipeline->pending.crtc) {
pipeline->pending.crtc->commitPending();
pipeline->pending.crtc->primaryPlane()->commitPending();
pipeline->pending.crtc->cursorPlane()->commitPending();
}
if (mode != CommitMode::Test) {
pipeline->m_modesetPresentPending = false;
@ -185,6 +199,7 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
pipeline->pending.crtc->primaryPlane()->setNext(pipeline->m_primaryBuffer);
pipeline->pending.crtc->commit();
pipeline->pending.crtc->primaryPlane()->commit();
pipeline->pending.crtc->cursorPlane()->commit();
}
pipeline->m_current = pipeline->pending;
if (modeset && pipeline->activePending()) {
@ -192,6 +207,12 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
}
}
}
for (const auto &obj : unusedObjects) {
obj->commitPending();
if (mode != CommitMode::Test) {
obj->commit();
}
}
drmModeAtomicFree(req);
return true;
} else {

View file

@ -101,7 +101,7 @@ public:
CommitModeset
};
Q_ENUM(CommitMode);
static bool commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode);
static bool commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects = {});
private:
bool populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);