backends/drm: handle failing commits better
It can happen that the drm backend temporarily lacks permission to do atomic commits, or that the cached drm property values become out of sync with the real values held by the kernel. Instead of failing with both, attempt to update property values and try the commits again at a later time.
This commit is contained in:
parent
6ff6dcac26
commit
42c5e6bcf6
8 changed files with 158 additions and 93 deletions
|
@ -706,7 +706,7 @@ bool DrmBackend::applyOutputChanges(const OutputConfiguration &config)
|
|||
toBeDisabled << output;
|
||||
}
|
||||
}
|
||||
if (!gpu->testPendingConfiguration()) {
|
||||
if (gpu->testPendingConfiguration() != DrmPipeline::Error::None) {
|
||||
for (const auto &output : qAsConst(toBeEnabled)) {
|
||||
output->revertQueuedChanges();
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
DrmRenderBackend *renderBackend() const;
|
||||
|
||||
void releaseBuffers();
|
||||
void updateOutputs();
|
||||
|
||||
public Q_SLOTS:
|
||||
void turnOutputsOn();
|
||||
|
@ -96,7 +97,6 @@ private:
|
|||
void activate(bool active);
|
||||
void reactivate();
|
||||
void deactivate();
|
||||
void updateOutputs();
|
||||
bool readOutputsConfiguration(const QVector<DrmAbstractOutput *> &outputs);
|
||||
void handleUdevEvent();
|
||||
DrmGpu *addGpu(const QString &fileName);
|
||||
|
|
|
@ -316,8 +316,8 @@ bool DrmGpu::updateOutputs()
|
|||
for (const auto &plane : qAsConst(m_planes)) {
|
||||
plane->updateProperties();
|
||||
}
|
||||
|
||||
if (testPendingConfiguration()) {
|
||||
DrmPipeline::Error err = testPendingConfiguration();
|
||||
if (err == DrmPipeline::Error::None) {
|
||||
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||
pipeline->applyPendingChanges();
|
||||
if (pipeline->output() && !pipeline->crtc()) {
|
||||
|
@ -325,6 +325,15 @@ bool DrmGpu::updateOutputs()
|
|||
pipeline->output()->setEnabled(false);
|
||||
}
|
||||
}
|
||||
} else if (err == DrmPipeline::Error::NoPermission) {
|
||||
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||
pipeline->revertPendingChanges();
|
||||
}
|
||||
for (const auto &output : qAsConst(addedOutputs)) {
|
||||
m_connectors.removeOne(output->connector());
|
||||
removeOutput(output);
|
||||
}
|
||||
QTimer::singleShot(50, m_platform, &DrmBackend::updateOutputs);
|
||||
} else {
|
||||
qCWarning(KWIN_DRM, "Failed to find a working setup for new outputs!");
|
||||
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||
|
@ -340,12 +349,12 @@ bool DrmGpu::updateOutputs()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVector<DrmCrtc *> &crtcs)
|
||||
DrmPipeline::Error DrmGpu::checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVector<DrmCrtc *> &crtcs)
|
||||
{
|
||||
if (connectors.isEmpty() || crtcs.isEmpty()) {
|
||||
if (m_pipelines.isEmpty()) {
|
||||
// nothing to do
|
||||
return true;
|
||||
return DrmPipeline::Error::None;
|
||||
}
|
||||
// remaining connectors can't be powered
|
||||
for (const auto &conn : qAsConst(connectors)) {
|
||||
|
@ -374,8 +383,9 @@ bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVect
|
|||
crtcsLeft.removeOne(currentCrtc);
|
||||
pipeline->setCrtc(currentCrtc);
|
||||
do {
|
||||
if (checkCrtcAssignment(connectors, crtcsLeft)) {
|
||||
return true;
|
||||
DrmPipeline::Error err = checkCrtcAssignment(connectors, crtcsLeft);
|
||||
if (err == DrmPipeline::Error::None || err == DrmPipeline::Error::NoPermission || err == DrmPipeline::Error::FramePending) {
|
||||
return err;
|
||||
}
|
||||
} while (pipeline->pruneModifier());
|
||||
}
|
||||
|
@ -386,16 +396,17 @@ bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVect
|
|||
crtcsLeft.removeOne(crtc);
|
||||
pipeline->setCrtc(crtc);
|
||||
do {
|
||||
if (checkCrtcAssignment(connectors, crtcsLeft)) {
|
||||
return true;
|
||||
DrmPipeline::Error err = checkCrtcAssignment(connectors, crtcsLeft);
|
||||
if (err == DrmPipeline::Error::None || err == DrmPipeline::Error::NoPermission || err == DrmPipeline::Error::FramePending) {
|
||||
return err;
|
||||
}
|
||||
} while (pipeline->pruneModifier());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return DrmPipeline::Error::InvalidArguments;
|
||||
}
|
||||
|
||||
bool DrmGpu::testPendingConfiguration()
|
||||
DrmPipeline::Error DrmGpu::testPendingConfiguration()
|
||||
{
|
||||
QVector<DrmConnector *> connectors;
|
||||
for (const auto &conn : qAsConst(m_connectors)) {
|
||||
|
@ -417,8 +428,9 @@ bool DrmGpu::testPendingConfiguration()
|
|||
return c1->getProp(DrmConnector::PropertyIndex::CrtcId)->current() > c2->getProp(DrmConnector::PropertyIndex::CrtcId)->current();
|
||||
});
|
||||
}
|
||||
if (checkCrtcAssignment(connectors, crtcs)) {
|
||||
return true;
|
||||
DrmPipeline::Error err = checkCrtcAssignment(connectors, crtcs);
|
||||
if (err == DrmPipeline::Error::None || err == DrmPipeline::Error::NoPermission || err == DrmPipeline::Error::FramePending) {
|
||||
return err;
|
||||
} else {
|
||||
// try again without hw rotation
|
||||
bool hwRotationUsed = false;
|
||||
|
@ -426,25 +438,27 @@ bool DrmGpu::testPendingConfiguration()
|
|||
hwRotationUsed |= (pipeline->bufferOrientation() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0));
|
||||
pipeline->setBufferOrientation(DrmPlane::Transformation::Rotate0);
|
||||
}
|
||||
return hwRotationUsed ? checkCrtcAssignment(connectors, crtcs) : false;
|
||||
if (hwRotationUsed) {
|
||||
err = checkCrtcAssignment(connectors, crtcs);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmGpu::testPipelines()
|
||||
DrmPipeline::Error DrmGpu::testPipelines()
|
||||
{
|
||||
QVector<DrmPipeline *> inactivePipelines;
|
||||
std::copy_if(m_pipelines.constBegin(), m_pipelines.constEnd(), std::back_inserter(inactivePipelines), [](const auto pipeline) {
|
||||
return pipeline->enabled() && !pipeline->active();
|
||||
});
|
||||
const auto unused = unusedObjects();
|
||||
bool test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unused);
|
||||
if (!inactivePipelines.isEmpty() && test) {
|
||||
DrmPipeline::Error test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unusedObjects());
|
||||
if (!inactivePipelines.isEmpty() && test == DrmPipeline::Error::None) {
|
||||
// ensure that pipelines that are set as enabled but currently inactive
|
||||
// still work when they need to be set active again
|
||||
for (const auto pipeline : qAsConst(inactivePipelines)) {
|
||||
pipeline->setActive(true);
|
||||
}
|
||||
test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unused);
|
||||
test = DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test, unusedObjects());
|
||||
for (const auto pipeline : qAsConst(inactivePipelines)) {
|
||||
pipeline->setActive(false);
|
||||
}
|
||||
|
@ -750,16 +764,23 @@ bool DrmGpu::maybeModeset()
|
|||
}
|
||||
// make sure there's no pending pageflips
|
||||
waitIdle();
|
||||
const bool ok = DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset, unusedObjects());
|
||||
const DrmPipeline::Error err = DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::CommitModeset, unusedObjects());
|
||||
for (DrmPipeline *pipeline : qAsConst(pipelines)) {
|
||||
if (pipeline->modesetPresentPending()) {
|
||||
pipeline->resetModesetPresentPending();
|
||||
if (!ok) {
|
||||
if (err != DrmPipeline::Error::None) {
|
||||
pipeline->output()->frameFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
if (err == DrmPipeline::Error::None) {
|
||||
return true;
|
||||
} else {
|
||||
if (err != DrmPipeline::Error::FramePending) {
|
||||
QTimer::singleShot(0, m_platform, &DrmBackend::updateOutputs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QVector<DrmObject *> DrmGpu::unusedObjects() const
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef DRM_GPU_H
|
||||
#define DRM_GPU_H
|
||||
|
||||
#include "drm_pipeline.h"
|
||||
#include "drm_virtual_output.h"
|
||||
|
||||
#include <QPointer>
|
||||
|
@ -39,7 +40,6 @@ class DrmConnector;
|
|||
class DrmPlane;
|
||||
class DrmBackend;
|
||||
class EglGbmBackend;
|
||||
class DrmPipeline;
|
||||
class DrmAbstractOutput;
|
||||
class DrmLeaseOutput;
|
||||
class DrmRenderBackend;
|
||||
|
@ -78,7 +78,7 @@ public:
|
|||
DrmVirtualOutput *createVirtualOutput(const QString &name, const QSize &size, double scale, DrmVirtualOutput::Type type);
|
||||
void removeVirtualOutput(DrmVirtualOutput *output);
|
||||
|
||||
bool testPendingConfiguration();
|
||||
DrmPipeline::Error testPendingConfiguration();
|
||||
bool needsModeset() const;
|
||||
bool maybeModeset();
|
||||
|
||||
|
@ -100,8 +100,8 @@ private:
|
|||
void initDrmResources();
|
||||
void waitIdle();
|
||||
|
||||
bool checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVector<DrmCrtc *> &crtcs);
|
||||
bool testPipelines();
|
||||
DrmPipeline::Error checkCrtcAssignment(QVector<DrmConnector *> connectors, const QVector<DrmCrtc *> &crtcs);
|
||||
DrmPipeline::Error testPipelines();
|
||||
QVector<DrmObject *> unusedObjects() const;
|
||||
|
||||
void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest);
|
||||
|
|
|
@ -231,7 +231,7 @@ bool DrmOutput::setDrmDpmsMode(DpmsMode mode)
|
|||
return true;
|
||||
}
|
||||
m_pipeline->setActive(active);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::CommitModeset)) {
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::CommitModeset) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
setDpmsModeInternal(mode);
|
||||
if (active) {
|
||||
|
@ -289,7 +289,7 @@ void DrmOutput::updateModes()
|
|||
if (currentMode != m_pipeline->mode()) {
|
||||
// DrmConnector::findCurrentMode might fail
|
||||
m_pipeline->setMode(currentMode ? currentMode : m_pipeline->connector()->modes().constFirst());
|
||||
if (m_gpu->testPendingConfiguration()) {
|
||||
if (m_gpu->testPendingConfiguration() == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_renderLoop->setRefreshRate(m_pipeline->mode()->refreshRate());
|
||||
} else {
|
||||
|
@ -312,17 +312,27 @@ bool DrmOutput::present()
|
|||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop.get());
|
||||
if (m_pipeline->syncMode() != renderLoopPrivate->presentMode) {
|
||||
m_pipeline->setSyncMode(renderLoopPrivate->presentMode);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
} else {
|
||||
m_pipeline->revertPendingChanges();
|
||||
}
|
||||
}
|
||||
bool modeset = gpu()->needsModeset();
|
||||
if (modeset ? m_pipeline->maybeModeset() : m_pipeline->present()) {
|
||||
const bool needsModeset = gpu()->needsModeset();
|
||||
bool success;
|
||||
if (needsModeset) {
|
||||
success = m_pipeline->maybeModeset();
|
||||
} else {
|
||||
DrmPipeline::Error err = m_pipeline->present();
|
||||
success = err == DrmPipeline::Error::None;
|
||||
if (err == DrmPipeline::Error::InvalidArguments) {
|
||||
QTimer::singleShot(0, m_gpu->platform(), &DrmBackend::updateOutputs);
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
Q_EMIT outputChange(m_pipeline->primaryLayer()->currentDamage());
|
||||
return true;
|
||||
} else if (!modeset) {
|
||||
} else if (!needsModeset) {
|
||||
qCWarning(KWIN_DRM) << "Presentation failed!" << strerror(errno);
|
||||
frameFailed();
|
||||
}
|
||||
|
@ -413,7 +423,7 @@ DrmOutputLayer *DrmOutput::outputLayer() const
|
|||
void DrmOutput::setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
m_pipeline->setColorTransformation(transformation);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_renderLoop->scheduleRepaint();
|
||||
} else {
|
||||
|
|
|
@ -53,15 +53,15 @@ bool DrmPipeline::testScanout()
|
|||
return false;
|
||||
}
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
return commitPipelines({this}, CommitMode::Test);
|
||||
return commitPipelines({this}, CommitMode::Test) == Error::None;
|
||||
} else {
|
||||
// no other way to test than to do it.
|
||||
// As we only have a maximum of one test per scanout cycle, this is fine
|
||||
return presentLegacy();
|
||||
return presentLegacy() == Error::None;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::present()
|
||||
DrmPipeline::Error DrmPipeline::present()
|
||||
{
|
||||
Q_ASSERT(m_pending.crtc);
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
|
@ -69,14 +69,11 @@ bool DrmPipeline::present()
|
|||
} else {
|
||||
if (m_pending.layer->hasDirectScanoutBuffer()) {
|
||||
// already presented
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
if (!presentLegacy()) {
|
||||
return false;
|
||||
return presentLegacy();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::maybeModeset()
|
||||
{
|
||||
|
@ -84,7 +81,7 @@ bool DrmPipeline::maybeModeset()
|
|||
return gpu()->maybeModeset();
|
||||
}
|
||||
|
||||
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects)
|
||||
DrmPipeline::Error DrmPipeline::commitPipelines(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects)
|
||||
{
|
||||
Q_ASSERT(!pipelines.isEmpty());
|
||||
if (pipelines[0]->gpu()->atomicModeSetting()) {
|
||||
|
@ -94,12 +91,12 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline *> &pipelines, Commi
|
|||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects)
|
||||
DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects)
|
||||
{
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
if (!req) {
|
||||
qCCritical(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
||||
return false;
|
||||
return Error::OutofMemory;
|
||||
}
|
||||
uint32_t flags = 0;
|
||||
const auto &failed = [pipelines, req, &flags, unusedObjects]() {
|
||||
|
@ -113,16 +110,17 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines,
|
|||
printProps(obj, PrintMode::OnlyChanged);
|
||||
obj->rollbackPending();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
for (const auto &pipeline : pipelines) {
|
||||
if (pipeline->activePending() && !pipeline->m_pending.layer->checkTestBuffer()) {
|
||||
qCWarning(KWIN_DRM) << "Checking test buffer failed for" << mode;
|
||||
return failed();
|
||||
failed();
|
||||
return Error::TestBufferFailed;
|
||||
}
|
||||
if (!pipeline->populateAtomicValues(req, flags)) {
|
||||
if (Error err = pipeline->populateAtomicValues(req, flags); err != Error::None) {
|
||||
qCWarning(KWIN_DRM) << "Populating atomic values failed for" << mode;
|
||||
return failed();
|
||||
failed();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
for (const auto &unused : unusedObjects) {
|
||||
|
@ -132,7 +130,8 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines,
|
|||
}
|
||||
if (!unused->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Populating atomic values failed for unused resource" << unused;
|
||||
return failed();
|
||||
failed();
|
||||
return errnoToError();
|
||||
}
|
||||
}
|
||||
bool modeset = flags & DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
|
@ -148,11 +147,13 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines,
|
|||
}
|
||||
if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, nullptr) != 0) {
|
||||
qCDebug(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
|
||||
return failed();
|
||||
failed();
|
||||
return errnoToError();
|
||||
}
|
||||
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, nullptr) != 0) {
|
||||
qCCritical(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
|
||||
return failed();
|
||||
failed();
|
||||
return errnoToError();
|
||||
}
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->atomicCommitSuccessful(mode);
|
||||
|
@ -164,10 +165,10 @@ bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines,
|
|||
}
|
||||
}
|
||||
drmModeAtomicFree(req);
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
|
||||
bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
||||
DrmPipeline::Error DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
||||
{
|
||||
if (needsModeset()) {
|
||||
prepareAtomicModeset();
|
||||
|
@ -193,20 +194,20 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
|||
}
|
||||
}
|
||||
if (!m_connector->atomicPopulate(req)) {
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
if (m_pending.crtc) {
|
||||
if (!m_pending.crtc->atomicPopulate(req)) {
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
if (!m_pending.crtc->primaryPlane()->atomicPopulate(req)) {
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
if (m_pending.crtc->cursorPlane() && !m_pending.crtc->cursorPlane()->atomicPopulate(req)) {
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
|
||||
void DrmPipeline::prepareAtomicModeset()
|
||||
|
@ -262,6 +263,22 @@ uint32_t DrmPipeline::calculateUnderscan()
|
|||
return hborder;
|
||||
}
|
||||
|
||||
DrmPipeline::Error DrmPipeline::errnoToError()
|
||||
{
|
||||
switch (errno) {
|
||||
case EINVAL:
|
||||
return Error::InvalidArguments;
|
||||
case EBUSY:
|
||||
return Error::FramePending;
|
||||
case ENOMEM:
|
||||
return Error::OutofMemory;
|
||||
case EACCES:
|
||||
return Error::NoPermission;
|
||||
default:
|
||||
return Error::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void DrmPipeline::atomicCommitFailed()
|
||||
{
|
||||
m_connector->rollbackPending();
|
||||
|
@ -311,7 +328,7 @@ bool DrmPipeline::setCursor(const QPoint &hotspot)
|
|||
m_pending.cursorHotspot = hotspot;
|
||||
// explicitly check for the cursor plane and not for AMS, as we might not always have one
|
||||
if (m_pending.crtc->cursorPlane()) {
|
||||
result = commitPipelines({this}, CommitMode::Test);
|
||||
result = commitPipelines({this}, CommitMode::Test) == Error::None;
|
||||
if (result && m_output) {
|
||||
m_output->renderLoop()->scheduleRepaint();
|
||||
}
|
||||
|
@ -331,7 +348,7 @@ bool DrmPipeline::moveCursor()
|
|||
bool result;
|
||||
// explicitly check for the cursor plane and not for AMS, as we might not always have one
|
||||
if (m_pending.crtc->cursorPlane()) {
|
||||
result = commitPipelines({this}, CommitMode::Test);
|
||||
result = commitPipelines({this}, CommitMode::Test) == Error::None;
|
||||
} else {
|
||||
result = moveCursorLegacy();
|
||||
}
|
||||
|
|
|
@ -53,11 +53,22 @@ public:
|
|||
DrmPipeline(DrmConnector *conn);
|
||||
~DrmPipeline();
|
||||
|
||||
enum class Error {
|
||||
None,
|
||||
OutofMemory,
|
||||
InvalidArguments,
|
||||
NoPermission,
|
||||
FramePending,
|
||||
TestBufferFailed,
|
||||
Unknown,
|
||||
};
|
||||
Q_ENUM(Error);
|
||||
|
||||
/**
|
||||
* tests the pending commit first and commits it if the test passes
|
||||
* if the test fails, there is a guarantee for no lasting changes
|
||||
*/
|
||||
bool present();
|
||||
Error present();
|
||||
bool testScanout();
|
||||
bool maybeModeset();
|
||||
|
||||
|
@ -118,28 +129,29 @@ public:
|
|||
Commit,
|
||||
CommitModeset
|
||||
};
|
||||
Q_ENUM(CommitMode)
|
||||
static bool commitPipelines(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects = {});
|
||||
Q_ENUM(CommitMode);
|
||||
static Error commitPipelines(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects = {});
|
||||
|
||||
private:
|
||||
bool activePending() const;
|
||||
bool isBufferForDirectScanout() const;
|
||||
uint32_t calculateUnderscan();
|
||||
static Error errnoToError();
|
||||
|
||||
// legacy only
|
||||
bool presentLegacy();
|
||||
bool legacyModeset();
|
||||
bool applyPendingChangesLegacy();
|
||||
Error presentLegacy();
|
||||
Error legacyModeset();
|
||||
Error applyPendingChangesLegacy();
|
||||
bool setCursorLegacy();
|
||||
bool moveCursorLegacy();
|
||||
static bool commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines, CommitMode mode);
|
||||
static Error commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines, CommitMode mode);
|
||||
|
||||
// atomic modesetting only
|
||||
bool populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);
|
||||
Error populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);
|
||||
void atomicCommitFailed();
|
||||
void atomicCommitSuccessful(CommitMode mode);
|
||||
void prepareAtomicModeset();
|
||||
static bool commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects);
|
||||
static Error commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects);
|
||||
|
||||
// logging helpers
|
||||
enum class PrintMode {
|
||||
|
|
|
@ -21,31 +21,34 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
bool DrmPipeline::presentLegacy()
|
||||
DrmPipeline::Error DrmPipeline::presentLegacy()
|
||||
{
|
||||
if (!m_pending.crtc->current() && !legacyModeset()) {
|
||||
return false;
|
||||
if (!m_pending.crtc->current()) {
|
||||
Error err = legacyModeset();
|
||||
if (err != Error::None) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
const auto buffer = m_pending.layer->currentBuffer();
|
||||
if (drmModePageFlip(gpu()->fd(), m_pending.crtc->id(), buffer->framebufferId(), DRM_MODE_PAGE_FLIP_EVENT, nullptr) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
m_pageflipPending = true;
|
||||
m_pending.crtc->setNext(buffer);
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
|
||||
bool DrmPipeline::legacyModeset()
|
||||
DrmPipeline::Error DrmPipeline::legacyModeset()
|
||||
{
|
||||
uint32_t connId = m_connector->id();
|
||||
if (!m_pending.layer->checkTestBuffer()) {
|
||||
return false;
|
||||
return Error::TestBufferFailed;
|
||||
}
|
||||
const auto buffer = m_pending.layer->currentBuffer();
|
||||
if (drmModeSetCrtc(gpu()->fd(), m_pending.crtc->id(), buffer->framebufferId(), 0, 0, &connId, 1, m_pending.mode->nativeMode()) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
// make sure the buffer gets kept alive, or the modeset gets reverted by the kernel
|
||||
if (m_pending.crtc->current()) {
|
||||
|
@ -53,25 +56,24 @@ bool DrmPipeline::legacyModeset()
|
|||
} else {
|
||||
m_pending.crtc->setCurrent(buffer);
|
||||
}
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
|
||||
bool DrmPipeline::commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines, CommitMode mode)
|
||||
DrmPipeline::Error DrmPipeline::commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines, CommitMode mode)
|
||||
{
|
||||
bool failure = false;
|
||||
Error err = Error::None;
|
||||
for (const auto &pipeline : pipelines) {
|
||||
if (!pipeline->applyPendingChangesLegacy()) {
|
||||
failure = true;
|
||||
err = pipeline->applyPendingChangesLegacy();
|
||||
if (err != Error::None) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (failure) {
|
||||
if (err != Error::None) {
|
||||
// at least try to revert the config
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->revertPendingChanges();
|
||||
pipeline->applyPendingChangesLegacy();
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
for (const auto &pipeline : pipelines) {
|
||||
pipeline->applyPendingChanges();
|
||||
|
@ -80,11 +82,11 @@ bool DrmPipeline::commitPipelinesLegacy(const QVector<DrmPipeline *> &pipelines,
|
|||
pipeline->pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
bool DrmPipeline::applyPendingChangesLegacy()
|
||||
DrmPipeline::Error DrmPipeline::applyPendingChangesLegacy()
|
||||
{
|
||||
if (!m_pending.active && m_pending.crtc) {
|
||||
drmModeSetCursor(gpu()->fd(), m_pending.crtc->id(), 0, 0, 0);
|
||||
|
@ -93,7 +95,7 @@ bool DrmPipeline::applyPendingChangesLegacy()
|
|||
auto vrr = m_pending.crtc->getProp(DrmCrtc::PropertyIndex::VrrEnabled);
|
||||
if (vrr && !vrr->setPropertyLegacy(m_pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive)) {
|
||||
qCWarning(KWIN_DRM) << "Setting vrr failed!" << strerror(errno);
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
if (const auto &rgbRange = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
|
||||
rgbRange->setEnumLegacy(m_pending.rgbRange);
|
||||
|
@ -106,21 +108,24 @@ bool DrmPipeline::applyPendingChangesLegacy()
|
|||
m_connector->getProp(DrmConnector::PropertyIndex::Underscan_vborder)->setPropertyLegacy(m_pending.overscan);
|
||||
m_connector->getProp(DrmConnector::PropertyIndex::Underscan_hborder)->setPropertyLegacy(hborder);
|
||||
}
|
||||
if (needsModeset() && !legacyModeset()) {
|
||||
return false;
|
||||
if (needsModeset()) {
|
||||
Error err = legacyModeset();
|
||||
if (err != Error::None) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if (m_pending.gamma && drmModeCrtcSetGamma(gpu()->fd(), m_pending.crtc->id(), m_pending.gamma->lut().size(), m_pending.gamma->lut().red(), m_pending.gamma->lut().green(), m_pending.gamma->lut().blue()) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
setCursorLegacy();
|
||||
moveCursorLegacy();
|
||||
}
|
||||
if (!m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setPropertyLegacy(activePending() ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF)) {
|
||||
qCWarning(KWIN_DRM) << "Setting legacy dpms failed!" << strerror(errno);
|
||||
return false;
|
||||
return errnoToError();
|
||||
}
|
||||
return true;
|
||||
return Error::None;
|
||||
}
|
||||
|
||||
bool DrmPipeline::setCursorLegacy()
|
||||
|
|
Loading…
Reference in a new issue