diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -90,6 +90,9 @@ QVector planes() const { return m_planes; } + QVector overlayPlanes() const { + return m_overlayPlanes; + } void outputWentOff(); void checkOutputsAreOn(); @@ -128,7 +131,7 @@ void activate(bool active); void reactivate(); void deactivate(); - void queryResources(); + void updateOutputs(); void setCursor(); void updateCursor(); void moveCursor(); @@ -157,6 +160,7 @@ bool m_active = false; // all available planes: primarys, cursors and overlays QVector m_planes; + QVector m_overlayPlanes; QScopedPointer m_dpmsFilter; KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr; gbm_device *m_gbmDevice = nullptr; diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -154,8 +154,10 @@ void DrmBackend::activate(bool active) { if (active) { + qCDebug(KWIN_DRM) << "Activating session."; reactivate(); } else { + qCDebug(KWIN_DRM) << "Deactivating session."; deactivate(); } } @@ -171,7 +173,9 @@ const QPoint cp = Cursor::pos() - softwareCursorHotspot(); for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; - o->pageFlipped(); + // only relevant in atomic mode + o->m_modesetRequested = true; + o->pageFlipped(); // TODO: Do we really need this? o->m_crtc->blank(); o->showCursor(c); o->moveCursor(cp); @@ -214,6 +218,12 @@ if (output->m_backend->m_pageFlipsPending == 0) { // TODO: improve, this currently means we wait for all page flips or all outputs. // It would be better to driver the repaint per output + + if (output->m_dpmsAtomicOffPending) { + output->m_modesetRequested = true; + output->dpmsAtomicOff(); + } + if (Compositor::self()) { Compositor::self()->bufferSwapComplete(); } @@ -268,12 +278,12 @@ // create the plane objects for (unsigned int i = 0; i < planeResources->count_planes; ++i) { drmModePlane *kplane = drmModeGetPlane(m_fd, planeResources->planes[i]); - DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd); - - if (p->init()) { - p->setPossibleCrtcs(kplane->possible_crtcs); - p->setFormats(kplane->formats, kplane->count_formats); + DrmPlane *p = new DrmPlane(kplane->plane_id, this); + if (p->atomicInit()) { m_planes << p; + if (p->type() == DrmPlane::TypeIndex::Overlay) { + m_overlayPlanes << p; + } } else { delete p; } @@ -297,26 +307,26 @@ } for (int i = 0; i < res->count_connectors; ++i) { - m_connectors << new DrmConnector(res->connectors[i], m_fd); + m_connectors << new DrmConnector(res->connectors[i], this); } for (int i = 0; i < res->count_crtcs; ++i) { - m_crtcs << new DrmCrtc(res->crtcs[i], m_fd, i); + m_crtcs << new DrmCrtc(res->crtcs[i], this, i); } if (m_atomicModeSetting) { - auto tryInit = [] (DrmObject *o) -> bool { - if (o->init()) { + auto tryAtomicInit = [] (DrmObject *obj) -> bool { + if (obj->atomicInit()) { return false; } else { - delete o; + delete obj; return true; } }; - m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryInit), m_connectors.end()); - m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryInit), m_crtcs.end()); + m_connectors.erase(std::remove_if(m_connectors.begin(), m_connectors.end(), tryAtomicInit), m_connectors.end()); + m_crtcs.erase(std::remove_if(m_crtcs.begin(), m_crtcs.end(), tryAtomicInit), m_crtcs.end()); } - queryResources(); + updateOutputs(); if (m_outputs.isEmpty()) { qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; @@ -341,7 +351,7 @@ } if (device->hasProperty("HOTPLUG", "1")) { qCDebug(KWIN_DRM) << "Received hot plug event for monitored drm device"; - queryResources(); + updateOutputs(); m_cursorIndex = (m_cursorIndex + 1) % 2; updateCursor(); } @@ -355,7 +365,7 @@ initCursor(); } -void DrmBackend::queryResources() +void DrmBackend::updateOutputs() { if (m_fd < 0) { return; @@ -717,5 +727,4 @@ setOutputsEnabled(enabled); } - } diff --git a/plugins/platforms/drm/drm_object.h b/plugins/platforms/drm/drm_object.h --- a/plugins/platforms/drm/drm_object.h +++ b/plugins/platforms/drm/drm_object.h @@ -30,73 +30,58 @@ namespace KWin { +class DrmBackend; class DrmOutput; class DrmObject { public: // creates drm object by its id delivered by the kernel - DrmObject(uint32_t object_id, int fd); + DrmObject(uint32_t object_id, DrmBackend *backend); virtual ~DrmObject() = 0; - enum class AtomicReturn { - NoChange, - Success, - Error - }; - - virtual bool init() = 0; + virtual bool atomicInit() = 0; uint32_t id() const { return m_id; } - DrmOutput* output() const { + DrmOutput *output() const { return m_output; } void setOutput(DrmOutput* output) { m_output = output; } - uint32_t propId(int index) { - return m_props[index]->propId(); + uint32_t propId(int prop) { + return m_props[prop]->propId(); } - uint64_t propValue(int index) { - return m_props[index]->value(); + uint64_t value(int prop) { + return m_props[prop]->value(); } - void setPropValue(int index, uint64_t new_value); - uint32_t propsPending() { - return m_propsPending; - } - uint32_t propsValid() { - return m_propsValid; - } - void setPropsPending(uint32_t value) { - m_propsPending = value; - } - void setPropsValid(uint32_t value) { - m_propsValid = value; + void setValue(int prop, uint64_t new_value) + { + Q_ASSERT(prop < m_props.size()); + m_props[prop]->setValue(new_value); } - bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value); + virtual bool atomicPopulate(drmModeAtomicReq *req); protected: - const int m_fd = 0; - const uint32_t m_id = 0; + virtual bool initProps() = 0; // only derived classes know names and quantity of properties + void initProp(int n, drmModeObjectProperties *properties, QVector enumNames = QVector(0)); + bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value); + DrmBackend *m_backend; + const uint32_t m_id = 0; DrmOutput *m_output = nullptr; - QVector m_propsNames; // for comparision with received name of DRM object + // for comparision with received name of DRM object + QVector m_propsNames; class Property; - QVector m_props; - - uint32_t m_propsPending = 0; - uint32_t m_propsValid = 0; - - virtual bool initProps() = 0; // only derived classes know names and quantity of properties - void initProp(int n, drmModeObjectProperties *properties, QVector enumNames = QVector(0)); + QVector m_props; class Property { @@ -113,7 +98,7 @@ uint32_t propId() { return m_propId; } - uint32_t value() { + uint64_t value() { return m_value; } void setValue(uint64_t new_value) { diff --git a/plugins/platforms/drm/drm_object.cpp b/plugins/platforms/drm/drm_object.cpp --- a/plugins/platforms/drm/drm_object.cpp +++ b/plugins/platforms/drm/drm_object.cpp @@ -18,6 +18,8 @@ along with this program. If not, see . *********************************************************************/ #include "drm_object.h" + +#include "drm_backend.h" #include "logging.h" namespace KWin @@ -27,8 +29,8 @@ * Defintions for class DrmObject */ -DrmObject::DrmObject(uint32_t object_id, int fd) - : m_fd(fd) +DrmObject::DrmObject(uint32_t object_id, DrmBackend *backend) + : m_backend(backend) , m_id(object_id) { } @@ -43,7 +45,7 @@ { m_props.resize(m_propsNames.size()); for (unsigned int i = 0; i < properties->count_props; ++i) { - drmModePropertyRes *prop = drmModeGetProperty(m_fd, properties->props[i]); + drmModePropertyRes *prop = drmModeGetProperty(m_backend->fd(), properties->props[i]); if (!prop) { continue; } @@ -56,27 +58,27 @@ } } -void DrmObject::setPropValue(int index, uint64_t new_value) +bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value) { - Q_ASSERT(index < m_props.size()); - m_props[index]->setValue(new_value); - return; + if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) <= 0) { + qCWarning(KWIN_DRM) << "Adding property" << m_propsNames[prop] << "to atomic commit failed for object" << this; + return false; + } + return true; } -bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value) +bool DrmObject::atomicPopulate(drmModeAtomicReq *req) { - uint32_t mask = 1U << prop; - if ((m_propsPending | m_propsValid) & mask && value == propValue(prop)) { - // no change necessary, don't add property for next atomic commit - return true; + bool ret = true; + + for (int i = 0; i < m_props.size(); i++) { + ret &= atomicAddProperty(req, i, m_props[i]->value()); } - if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) < 0) { - // error when adding property + + if (!ret) { + qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; return false; } - m_propsPending |= mask; - m_propsValid &= ~mask; - // adding property was successful return true; } diff --git a/plugins/platforms/drm/drm_object_connector.h b/plugins/platforms/drm/drm_object_connector.h --- a/plugins/platforms/drm/drm_object_connector.h +++ b/plugins/platforms/drm/drm_object_connector.h @@ -28,11 +28,11 @@ class DrmConnector : public DrmObject { public: - DrmConnector(uint32_t connector_id, int fd); + DrmConnector(uint32_t connector_id, DrmBackend *backend); virtual ~DrmConnector(); - bool init(); + bool atomicInit(); enum class PropertyIndex { CrtcId = 0, diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp --- a/plugins/platforms/drm/drm_object_connector.cpp +++ b/plugins/platforms/drm/drm_object_connector.cpp @@ -18,16 +18,17 @@ along with this program. If not, see . *********************************************************************/ #include "drm_object_connector.h" +#include "drm_backend.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { -DrmConnector::DrmConnector(uint32_t connector_id, int fd) - : DrmObject(connector_id, fd) +DrmConnector::DrmConnector(uint32_t connector_id, DrmBackend *backend) + : DrmObject(connector_id, backend) { - ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd, connector_id)); + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(backend->fd(), connector_id)); if (!con) { return; } @@ -38,7 +39,7 @@ DrmConnector::~DrmConnector() = default; -bool DrmConnector::init() +bool DrmConnector::atomicInit() { qCDebug(KWIN_DRM) << "Creating connector" << m_id; @@ -54,7 +55,7 @@ QByteArrayLiteral("CRTC_ID"), }; - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CONNECTOR); + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CONNECTOR); if (!properties) { qCWarning(KWIN_DRM) << "Failed to get properties for connector " << m_id ; return false; @@ -70,7 +71,7 @@ bool DrmConnector::isConnected() { - ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_fd, m_id)); + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_backend->fd(), m_id)); if (!con) { return false; } diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h --- a/plugins/platforms/drm/drm_object_crtc.h +++ b/plugins/platforms/drm/drm_object_crtc.h @@ -32,11 +32,11 @@ class DrmCrtc : public DrmObject { public: - DrmCrtc(uint32_t crtc_id, int fd, int resIndex); + DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex); virtual ~DrmCrtc(); - bool init(); + bool atomicInit(); enum class PropertyIndex { ModeId = 0, @@ -64,7 +64,6 @@ bool blank(); private: - DrmBackend *m_backend; int m_resIndex; DrmBuffer *m_currentBuffer = nullptr; diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp --- a/plugins/platforms/drm/drm_object_crtc.cpp +++ b/plugins/platforms/drm/drm_object_crtc.cpp @@ -26,17 +26,17 @@ namespace KWin { -DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd, int resIndex) - : DrmObject(crtc_id, fd), +DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex) + : DrmObject(crtc_id, backend), m_resIndex(resIndex) { } DrmCrtc::~DrmCrtc() { } -bool DrmCrtc::init() +bool DrmCrtc::atomicInit() { qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id; @@ -53,7 +53,7 @@ QByteArrayLiteral("ACTIVE"), }; - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CRTC); + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_CRTC); if (!properties) { qCWarning(KWIN_DRM) << "Failed to get properties for crtc " << m_id ; return false; @@ -69,7 +69,7 @@ void DrmCrtc::flipBuffer() { - if (m_currentBuffer && m_output->m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) { + if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) { delete m_currentBuffer; } m_currentBuffer = m_nextBuffer; @@ -82,18 +82,17 @@ bool DrmCrtc::blank() { if (!m_blackBuffer) { - DrmDumbBuffer *blackBuffer = m_output->m_backend->createBuffer(m_output->pixelSize()); + DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize()); if (!blackBuffer->map()) { delete blackBuffer; return false; } blackBuffer->image()->fill(Qt::black); m_blackBuffer = blackBuffer; } - // TODO: Do this atomically if (m_output->setModeLegacy(m_blackBuffer)) { - if (m_currentBuffer && m_output->m_backend->deleteBufferAfterPageFlip()) { + if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip()) { delete m_currentBuffer; delete m_nextBuffer; } diff --git a/plugins/platforms/drm/drm_object_plane.h b/plugins/platforms/drm/drm_object_plane.h --- a/plugins/platforms/drm/drm_object_plane.h +++ b/plugins/platforms/drm/drm_object_plane.h @@ -32,9 +32,9 @@ class DrmPlane : public DrmObject { public: - DrmPlane(uint32_t plane_id, int fd); + DrmPlane(uint32_t plane_id, DrmBackend *backend); - virtual ~DrmPlane(); + ~DrmPlane(); enum class PropertyIndex { Type = 0, @@ -58,36 +58,31 @@ Count }; - bool init(); + bool atomicInit(); bool initProps(); TypeIndex type(); - bool isCrtcSupported(uint32_t crtc); - DrmObject::AtomicReturn atomicReqPlanePopulate(drmModeAtomicReq *req); - DrmBuffer *current(){ + bool isCrtcSupported(int resIndex) const { + return (m_possibleCrtcs & (1 << resIndex)); + } + QVector formats() const { + return m_formats; + } + + DrmBuffer *current() const { return m_current; } - DrmBuffer *next(){ + DrmBuffer *next() const { return m_next; } - void setCurrent(DrmBuffer *b){ + void setCurrent(DrmBuffer *b) { m_current = b; } - void setNext(DrmBuffer *b){ - m_next = b; - } + void setNext(DrmBuffer *b); - QVector formats(){ - return m_formats; - } - void setFormats(uint32_t const *f, int fcount); - - void setPossibleCrtcs(uint32_t value){ - m_possibleCrtcs = value; - } - uint32_t possibleCrtcs(){ - return m_possibleCrtcs; - } + bool atomicPopulate(drmModeAtomicReq *req); + void flipBuffer(); + void flipBufferWithDelete(); private: DrmBuffer *m_current = nullptr; diff --git a/plugins/platforms/drm/drm_object_plane.cpp b/plugins/platforms/drm/drm_object_plane.cpp --- a/plugins/platforms/drm/drm_object_plane.cpp +++ b/plugins/platforms/drm/drm_object_plane.cpp @@ -18,34 +18,40 @@ along with this program. If not, see . *********************************************************************/ #include "drm_object_plane.h" +#include "drm_backend.h" #include "drm_buffer.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { -DrmPlane::DrmPlane(uint32_t plane_id, int fd) - : DrmObject(plane_id, fd) +DrmPlane::DrmPlane(uint32_t plane_id, DrmBackend *backend) + : DrmObject(plane_id, backend) { } -DrmPlane::~DrmPlane() = default; +DrmPlane::~DrmPlane() +{ + delete m_current; + delete m_next; +} -bool DrmPlane::init() +bool DrmPlane::atomicInit() { - qCDebug(KWIN_DRM) << "Initialize plane" << m_id; - ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_fd, m_id)); + qCDebug(KWIN_DRM) << "Atomic init for plane:" << m_id; + ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_backend->fd(), m_id)); if (!p) { qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id; return false; } m_possibleCrtcs = p->possible_crtcs; - m_formats.resize(p->count_formats); - for (int i = 0; i < p->count_formats; i++) { + int count_formats = p->count_formats; + m_formats.resize(count_formats); + for (int i = 0; i < count_formats; i++) { m_formats[i] = p->formats[i]; } @@ -77,7 +83,7 @@ QByteArrayLiteral("Overlay"), }; - drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_PLANE); + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->fd(), m_id, DRM_MODE_OBJECT_PLANE); if (!properties){ qCWarning(KWIN_DRM) << "Failed to get properties for plane " << m_id ; return false; @@ -98,77 +104,48 @@ DrmPlane::TypeIndex DrmPlane::type() { - uint64_t value = propValue(int(PropertyIndex::Type)); + uint64_t v = value(int(PropertyIndex::Type)); int typeCount = int(TypeIndex::Count); for (int i = 0; i < typeCount; i++) { - if (m_props[int(PropertyIndex::Type)]->enumMap(i) == value) { + if (m_props[int(PropertyIndex::Type)]->enumMap(i) == v) { return TypeIndex(i); } } return TypeIndex::Overlay; } -bool DrmPlane::isCrtcSupported(uint32_t crtc) -{ - ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> res(drmModeGetResources(m_fd)); - if (!res) { - qCWarning(KWIN_DRM) << "Failed to get drm resources"; - } - for (int c = 0; c < res->count_crtcs; c++) { - if (res->crtcs[c] != crtc) { - continue; - } - qCDebug(KWIN_DRM) << "Mask " << m_possibleCrtcs << ", idx " << c; - if (m_possibleCrtcs & (1 << c)) { - return true; - } - } - qCDebug(KWIN_DRM) << "CRTC" << crtc << "not supported"; - return false; -} - - -void DrmPlane::setFormats(uint32_t const *f, int fcount) -{ - m_formats.resize(fcount); - for (int i = 0; i < fcount; i++) { - m_formats[i] = *f; - } +void DrmPlane::setNext(DrmBuffer *b){ + setValue(int(PropertyIndex::FbId), b ? b->bufferId() : 0); + m_next = b; } -DrmObject::AtomicReturn DrmPlane::atomicReqPlanePopulate(drmModeAtomicReq *req) +bool DrmPlane::atomicPopulate(drmModeAtomicReq *req) { bool ret = true; - if (m_next) { - setPropValue(int(PropertyIndex::FbId), m_next->bufferId()); - } else { - setPropValue(int(PropertyIndex::FbId), 0); - setPropValue(int(PropertyIndex::SrcX), 0); - setPropValue(int(PropertyIndex::SrcY), 0); - setPropValue(int(PropertyIndex::SrcW), 0); - setPropValue(int(PropertyIndex::SrcH), 0); - setPropValue(int(PropertyIndex::CrtcX), 0); - setPropValue(int(PropertyIndex::CrtcY), 0); - setPropValue(int(PropertyIndex::CrtcW), 0); - setPropValue(int(PropertyIndex::CrtcH), 0); + for (int i = 1; i < m_props.size(); i++) { + ret &= atomicAddProperty(req, i, m_props[i]->value()); } - m_propsPending = 0; - - for (int i = int(PropertyIndex::SrcX); i < int(PropertyIndex::CrtcId); i++) { - ret &= atomicAddProperty(req, i, propValue(i)); - } - ret &= atomicAddProperty(req, int(PropertyIndex::CrtcId), m_next ? propValue(int(PropertyIndex::CrtcId)) : 0); - if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; - return DrmObject::AtomicReturn::Error; + return false; } - if (!m_propsPending) { - return DrmObject::AtomicReturn::NoChange; + return true; +} + +void DrmPlane::flipBuffer() +{ + m_current = m_next; + m_next = nullptr; +} + +void DrmPlane::flipBufferWithDelete() +{ + if (m_current != m_next) { + delete m_current; } - return DrmObject::AtomicReturn::Success; + flipBuffer(); } } diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -95,7 +95,8 @@ }; void setDpms(DpmsMode mode); bool isDpmsEnabled() const { - return m_dpmsMode == DpmsMode::On; + // We care for current as well as pending mode in order to allow first present in AMS. + return m_dpmsModePending == DpmsMode::On; } QByteArray uuid() const { @@ -111,6 +112,13 @@ // and save the connector ids in the DrmCrtc instance. DrmOutput(DrmBackend *backend); bool presentAtomically(DrmBuffer *buffer); + + enum class AtomicCommitMode { + Test, + Real + }; + bool doAtomicCommit(AtomicCommitMode mode); + bool presentLegacy(DrmBuffer *buffer); bool setModeLegacy(DrmBuffer *buffer); void initEdid(drmModeConnector *connector); @@ -120,10 +128,13 @@ void setGlobalPos(const QPoint &pos); void setScale(qreal scale); - void pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer); bool initPrimaryPlane(); bool initCursorPlane(); - DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); + + void dpmsOnHandler(); + void dpmsOffHandler(); + bool dpmsAtomicOff(); + bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); DrmBackend *m_backend; DrmConnector *m_conn = nullptr; @@ -138,12 +149,16 @@ QPointer m_changeset; KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms; DpmsMode m_dpmsMode = DpmsMode::On; + DpmsMode m_dpmsModePending = DpmsMode::On; QByteArray m_uuid; uint32_t m_blobId = 0; DrmPlane* m_primaryPlane = nullptr; DrmPlane* m_cursorPlane = nullptr; - QVector m_planesFlipList; + QVector m_nextPlanesFlipList; + bool m_pageFlipPending = false; + bool m_dpmsAtomicOffPending = false; + bool m_modesetRequested = true; }; } diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -63,6 +63,20 @@ { hideCursor(); m_crtc->blank(); + + if (m_primaryPlane) { + // TODO: when having multiple planes, also clean up these + m_primaryPlane->setOutput(nullptr); + + if (m_backend->deleteBufferAfterPageFlip()) { + delete m_primaryPlane->current(); + } + m_primaryPlane->setCurrent(nullptr); + } + + m_crtc->setOutput(nullptr); + m_conn->setOutput(nullptr); + delete m_waylandOutput.data(); delete m_waylandOutputDevice.data(); } @@ -72,10 +86,8 @@ if (DrmBuffer *b = m_crtc->current()) { b->releaseGbm(); } - if (m_primaryPlane) { - if (m_primaryPlane->current()) { - m_primaryPlane->current()->releaseGbm(); - } + if (m_primaryPlane && m_primaryPlane->current()) { + m_primaryPlane->current()->releaseGbm(); } } @@ -176,10 +188,10 @@ if (!initPrimaryPlane()) { return false; } - } - if (!m_crtc->blank()) { + } else if (!m_crtc->blank()) { return false; } + setDpms(DpmsMode::On); if (!m_waylandOutput.isNull()) { delete m_waylandOutput.data(); @@ -496,7 +508,7 @@ if (m_primaryPlane) { // Output already has a primary plane continue; } - if (!p->isCrtcSupported(m_crtc->id())) { + if (!p->isCrtcSupported(m_crtc->resIndex())) { continue; } p->setOutput(this); @@ -524,7 +536,7 @@ if (m_cursorPlane) { // Output already has a cursor plane continue; } - if (!p->isCrtcSupported(m_crtc->id())) { + if (!p->isCrtcSupported(m_crtc->resIndex())) { continue; } p->setOutput(this); @@ -554,45 +566,70 @@ if (m_dpms.isNull()) { return; } - if (mode == m_dpmsMode) { + if (mode == m_dpmsModePending) { qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; return; } - if (m_backend->atomicModeSetting()) { - drmModeAtomicReq *req = drmModeAtomicAlloc(); + m_dpmsModePending = mode; - if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) { - qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtc->id(); - return; - } - if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) { - qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtc->id(); + if (m_backend->atomicModeSetting()) { + m_modesetRequested = true; + if (mode == DpmsMode::On) { + if (m_pageFlipPending) { + m_pageFlipPending = false; + Compositor::self()->bufferSwapComplete(); + } + dpmsOnHandler(); } else { - qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtc->id(); + m_dpmsAtomicOffPending = true; + if (!m_pageFlipPending) { + dpmsAtomicOff(); + } } - drmModeAtomicFree(req); } else { if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) { + m_dpmsModePending = m_dpmsMode; qCWarning(KWIN_DRM) << "Setting DPMS failed"; return; } + if (mode == DpmsMode::On) { + dpmsOnHandler(); + } else { + dpmsOffHandler(); + } + m_dpmsMode = m_dpmsModePending; } +} + +void DrmOutput::dpmsOnHandler() +{ + qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On."; - m_dpmsMode = mode; if (m_waylandOutput) { - m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); + m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); } emit dpmsChanged(); - if (m_dpmsMode != DpmsMode::On) { - m_backend->outputWentOff(); - } else { - m_backend->checkOutputsAreOn(); + + m_backend->checkOutputsAreOn(); + if (!m_backend->atomicModeSetting()) { m_crtc->blank(); - if (Compositor *compositor = Compositor::self()) { - compositor->addRepaintFull(); - } } + if (Compositor *compositor = Compositor::self()) { + compositor->addRepaintFull(); + } +} + +void DrmOutput::dpmsOffHandler() +{ + qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off."; + + if (m_waylandOutput) { + m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); + } + emit dpmsChanged(); + + m_backend->outputWentOff(); } QString DrmOutput::name() const @@ -681,36 +718,51 @@ void DrmOutput::pageFlipped() { + m_pageFlipPending = false; + if (!m_crtc) { return; } - if (m_backend->atomicModeSetting()){ - foreach (DrmPlane *p, m_planesFlipList) { - pageFlippedBufferRemover(p->current(), p->next()); - p->setCurrent(p->next()); - p->setNext(nullptr); + // Egl based surface buffers get destroyed, QPainter based dumb buffers not + // TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely? + if (m_backend->deleteBufferAfterPageFlip()) { + if (m_backend->atomicModeSetting()) { + if (!m_primaryPlane->next()) { + // on manual vt switch + // TODO: when we later use overlay planes it might happen, that we have a page flip with only + // damage on one of these, and therefore the primary plane has no next buffer + // -> Then we don't want to return here! + if (m_primaryPlane->current()) { + m_primaryPlane->current()->releaseGbm(); + } + return; + } + for (DrmPlane *p : m_nextPlanesFlipList) { + p->flipBufferWithDelete(); + } + m_nextPlanesFlipList.clear(); + } else { + if (!m_crtc->next()) { + // on manual vt switch + if (DrmBuffer *b = m_crtc->current()) { + b->releaseGbm(); + } + } + m_crtc->flipBuffer(); } - m_planesFlipList.clear(); - } else { - if (!m_crtc->next()) { - // on manual vt switch - if (DrmBuffer *b = m_crtc->current()) { - b->releaseGbm(); + if (m_backend->atomicModeSetting()){ + for (DrmPlane *p : m_nextPlanesFlipList) { + p->flipBuffer(); } - return; + m_nextPlanesFlipList.clear(); + } else { + m_crtc->flipBuffer(); } m_crtc->flipBuffer(); } } -void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) -{ - if (oldbuffer && m_backend->deleteBufferAfterPageFlip() && oldbuffer != newbuffer) { - delete oldbuffer; - } -} - bool DrmOutput::present(DrmBuffer *buffer) { if (!buffer || buffer->bufferId() == 0) { @@ -723,110 +775,61 @@ } } -bool DrmOutput::presentAtomically(DrmBuffer *buffer) +bool DrmOutput::dpmsAtomicOff() { - if (!LogindIntegration::self()->isActiveSession()) { - qCWarning(KWIN_DRM) << "Logind session not active."; - return false; - } - if (m_dpmsMode != DpmsMode::On) { - qCWarning(KWIN_DRM) << "No present() while screen off."; + m_dpmsAtomicOffPending = false; + + // TODO: With multiple planes: deactivate all of them here + delete m_primaryPlane->next(); + m_primaryPlane->setNext(nullptr); + m_nextPlanesFlipList << m_primaryPlane; + + if (!doAtomicCommit(AtomicCommitMode::Test)) { + qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting."; return false; } - if (m_primaryPlane->next()) { - qCWarning(KWIN_DRM) << "Page not yet flipped."; + if (!doAtomicCommit(AtomicCommitMode::Real)) { + qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting."; return false; } + m_nextPlanesFlipList.clear(); + dpmsOffHandler(); - DrmObject::AtomicReturn ret; - uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; - - // TODO: throwing an exception would be really handy here! (would mean change of compile options) - drmModeAtomicReq *req = drmModeAtomicAlloc(); - if (!req) { - qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; - delete buffer; - return false; - } - - // Do we need to set a new mode first? - bool doModeset = !m_primaryPlane->current(); - if (doModeset) { - qCDebug(KWIN_DRM) << "Atomic Modeset requested"; + return true; - if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId)) { - qCWarning(KWIN_DRM) << "Failed to create property blob"; - delete buffer; - return false; - } +} - ret = atomicReqModesetPopulate(req, true); - if (ret == DrmObject::AtomicReturn::Error){ - drmModeAtomicFree(req); - delete buffer; - return false; - } - if (ret == DrmObject::AtomicReturn::Success) { - flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; - } +bool DrmOutput::presentAtomically(DrmBuffer *buffer) +{ + if (!LogindIntegration::self()->isActiveSession()) { + qCWarning(KWIN_DRM) << "Logind session not active."; + return false; } - m_primaryPlane->setNext(buffer); // TODO: Later not only use the primary plane for the buffer! - // i.e.: Assign planes - bool anyDamage = false; - foreach (DrmPlane* p, m_backend->planes()){ - if (p->output() != this) { - continue; - } - ret = p->atomicReqPlanePopulate(req); - if (ret == DrmObject::AtomicReturn::Error) { - drmModeAtomicFree(req); - m_primaryPlane->setNext(nullptr); - m_planesFlipList.clear(); - delete buffer; - return false; - } - if (ret == DrmObject::AtomicReturn::Success) { - anyDamage = true; - m_planesFlipList << p; - } + if (m_pageFlipPending) { + qCWarning(KWIN_DRM) << "Page not yet flipped."; + return false; } - // no damage but force flip for atleast the primary plane anyway - if (!anyDamage) { - m_primaryPlane->setPropsValid(0); - if (m_primaryPlane->atomicReqPlanePopulate(req) == DrmObject::AtomicReturn::Error) { - drmModeAtomicFree(req); - m_primaryPlane->setNext(nullptr); - m_planesFlipList.clear(); + m_primaryPlane->setNext(buffer); + m_nextPlanesFlipList << m_primaryPlane; + + if (!doAtomicCommit(AtomicCommitMode::Test)) { + //TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout? + qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present."; + if (this->m_backend->deleteBufferAfterPageFlip()) { delete buffer; - return false; } - m_planesFlipList << m_primaryPlane; - } - - if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { - qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); - drmModeAtomicFree(req); - m_primaryPlane->setNext(nullptr); - m_planesFlipList.clear(); - delete buffer; return false; } - - if (doModeset) { - m_crtc->setPropsValid(m_crtc->propsValid() | m_crtc->propsPending()); - m_conn->setPropsValid(m_conn->propsValid() | m_conn->propsPending()); - } - foreach (DrmPlane* p, m_planesFlipList) { - p->setPropsValid(p->propsValid() | p->propsPending()); + if (!doAtomicCommit(AtomicCommitMode::Real)) { + qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present."; + return false; } - - drmModeAtomicFree(req); + m_pageFlipPending = true; return true; } - bool DrmOutput::presentLegacy(DrmBuffer *buffer) { if (m_crtc->next()) { @@ -869,37 +872,135 @@ } } -DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) +bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) { - if (enable) { - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); + drmModeAtomicReq *req = drmModeAtomicAlloc(); + + auto errorHandler = [this, mode, req] () { + if (mode == AtomicCommitMode::Test) { + // TODO: when we later test overlay planes, make sure we change only the right stuff back + } + if (req) { + drmModeAtomicFree(req); + } + + if (m_dpmsMode != m_dpmsModePending) { + qCWarning(KWIN_DRM) << "Setting DPMS failed"; + m_dpmsModePending = m_dpmsMode; + if (m_dpmsMode != DpmsMode::On) { + dpmsOffHandler(); + } + } + + // TODO: see above, rework later for overlay planes! + for (DrmPlane *p : m_nextPlanesFlipList) { + p->setNext(nullptr); + } + m_nextPlanesFlipList.clear(); + + }; + + if (!req) { + qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; + errorHandler(); + return false; + } + + uint32_t flags = 0; + + // Do we need to set a new mode? + if (m_modesetRequested) { + if (m_dpmsModePending == DpmsMode::On) { + if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) { + qCWarning(KWIN_DRM) << "Failed to create property blob"; + errorHandler(); + return false; + } + } + if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){ + qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset"; + errorHandler(); + return false; + } + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + } + + if (mode == AtomicCommitMode::Real) { + if (m_dpmsModePending == DpmsMode::On) { + if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { + // TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10. + flags |= DRM_MODE_ATOMIC_NONBLOCK; + } + flags |= DRM_MODE_PAGE_FLIP_EVENT; + } } else { - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), 0); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), 0); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), 0); - m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), 0); + flags |= DRM_MODE_ATOMIC_TEST_ONLY; } bool ret = true; + // TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order. + for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) { + DrmPlane *p = m_nextPlanesFlipList[i]; + ret &= p->atomicPopulate(req); + } - m_crtc->setPropsPending(0); - m_conn->setPropsPending(0); + if (!ret) { + qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!"; + errorHandler(); + return false; + } - ret &= m_conn->atomicAddProperty(req, int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); - ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); - ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::Active), enable); + if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { + qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); + errorHandler(); + return false; + } - if (!ret) { - qCWarning(KWIN_DRM) << "Failed to populate atomic modeset"; - return DrmObject::AtomicReturn::Error; + if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { + qCDebug(KWIN_DRM) << "Atomic Modeset successful."; + m_modesetRequested = false; + m_dpmsMode = m_dpmsModePending; } - if (!m_crtc->propsPending() && !m_conn->propsPending()) { - return DrmObject::AtomicReturn::NoChange; + + drmModeAtomicFree(req); + return true; +} + +bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) +{ + if (enable) { + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); + } else { + if (m_backend->deleteBufferAfterPageFlip()) { + delete m_primaryPlane->current(); + delete m_primaryPlane->next(); + } + m_primaryPlane->setCurrent(nullptr); + m_primaryPlane->setNext(nullptr); + + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); + m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); } - return DrmObject::AtomicReturn::Success; + m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); + m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); + m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), enable); + + bool ret = true; + ret &= m_conn->atomicPopulate(req); + ret &= m_crtc->atomicPopulate(req); + + return ret; } }