From a0571ccf847983fcc5af19f8f921248bdb69f0a6 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Tue, 9 May 2017 20:02:49 +0200 Subject: [PATCH] [DRM plugin] Remember static kernel objects, amplify use of DrmCrtc To get an image from KWin to the screen in the DRM pipeline we combine a CRTC, an encoder and a connector. These objects are static in the sense, that they represent real hardware on the graphics card, which doesn't change in a session. See here for more details: https://01.org/linuxgraphics/gfx-docs/drm/gpu/drm-kms.html Until now we used DrmOutput as the main representation for such an active rendering pipeline. I.e. it gets created and destroyed on hot plug events of displays. On the other side we had no fixed representation of the static kernel objects throughout the lifetime of KWin. This has several disadvantages: * We always need to query all available static objects on an hot plug event. * We can't manipulate the frame buffer of a CRTC after an output has been disconnected * Adding functionality for driving multiple displays on a single CRTC (i.e. cloning) would be difficult * We can't destroy the last frame buffer on display disconnect because the CRTC still accesses it and have therefore a memory leak on every display disconnect This patch solves these issues by storing representations of all available CRTC and Connector objects in DrmBackend on init via DrmCrtc and DrmConnector instances. On an hotplug event these vectors are looped for a fitting CRTC and Connector combinations. Buffer handling is moved to the respective CRTC instance. All changes in overview: * Query all available CRTCs and Connectors and save for subsequent hotplug events * Fix logic errors in `queryResources()` * Move framebuffers, buffer flip and blank logic in DrmCrtc * Remove `restoreSaved()`. It isn't necessary and is dangerous if the old framebuffer was deleted in the meantime. Also could reveal sensitive user info from old session. Test Plan: Login, logout, VT switching, connect and disconnect external monitor, energy saving mode. Reviewers: #kwin Subscribers: kwin, #kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5118 --- plugins/platforms/drm/drm_backend.cpp | 229 +++++++++--------- plugins/platforms/drm/drm_backend.h | 9 +- plugins/platforms/drm/drm_buffer.cpp | 6 +- plugins/platforms/drm/drm_buffer.h | 1 - .../platforms/drm/drm_object_connector.cpp | 18 +- plugins/platforms/drm/drm_object_connector.h | 9 + plugins/platforms/drm/drm_object_crtc.cpp | 51 +++- plugins/platforms/drm/drm_object_crtc.h | 28 ++- plugins/platforms/drm/drm_output.cpp | 100 +++----- plugins/platforms/drm/drm_output.h | 22 +- plugins/platforms/drm/egl_gbm_backend.cpp | 2 +- 11 files changed, 262 insertions(+), 213 deletions(-) diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp index b9b665bbdb..cc79aca1d7 100644 --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -89,8 +89,10 @@ DrmBackend::~DrmBackend() while (m_pageFlipsPending != 0) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } - qDeleteAll(m_planes); qDeleteAll(m_outputs); + qDeleteAll(m_planes); + qDeleteAll(m_crtcs); + qDeleteAll(m_connectors); delete m_cursor[0]; delete m_cursor[1]; close(m_fd); @@ -170,7 +172,7 @@ void DrmBackend::reactivate() for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; o->pageFlipped(); - o->blank(); + o->m_crtc->blank(); o->showCursor(c); o->moveCursor(cp); } @@ -196,7 +198,6 @@ void DrmBackend::deactivate() for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { DrmOutput *o = *it; o->hideCursor(); - o->restoreSaved(); } m_active = false; } @@ -288,7 +289,35 @@ void DrmBackend::openDrm() } } + ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); + drmModeRes *res = resources.data(); + if (!resources) { + qCWarning(KWIN_DRM) << "drmModeGetResources failed"; + return; + } + + for (int i = 0; i < res->count_connectors; ++i) { + m_connectors << new DrmConnector(res->connectors[i], m_fd); + } + for (int i = 0; i < res->count_crtcs; ++i) { + m_crtcs << new DrmCrtc(res->crtcs[i], m_fd, i); + } + + if (m_atomicModeSetting) { + auto tryInit = [] (DrmObject *o) -> bool { + if (o->init()) { + return false; + } else { + delete o; + 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()); + } + queryResources(); + if (m_outputs.isEmpty()) { qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; emit initFailed(); @@ -331,6 +360,7 @@ void DrmBackend::queryResources() if (m_fd < 0) { return; } + ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); if (!resources) { qCWarning(KWIN_DRM) << "drmModeGetResources failed"; @@ -338,74 +368,21 @@ void DrmBackend::queryResources() } QVector connectedOutputs; - for (int i = 0; i < resources->count_connectors; ++i) { - const auto id = resources->connectors[i]; - ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, id)); - if (!connector) { + QVector pendingConnectors; + + // split up connected connectors in already or not yet assigned ones + for (DrmConnector *con : qAsConst(m_connectors)) { + if (!con->isConnected()) { continue; } - if (connector->connection != DRM_MODE_CONNECTED) { - continue; - } - if (connector->count_modes == 0) { - continue; - } - if (DrmOutput *o = findOutput(connector->connector_id)) { + + if (DrmOutput *o = findOutput(con->id())) { connectedOutputs << o; - continue; - } - bool crtcFound = false; - const quint32 crtcId = findCrtc(resources.data(), connector.data(), &crtcFound); - if (!crtcFound) { - continue; - } - ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> crtc(drmModeGetCrtc(m_fd, crtcId)); - if (!crtc) { - continue; - } - DrmOutput *drmOutput = new DrmOutput(this); - connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); - drmOutput->m_crtcId = crtcId; - drmOutput->m_connector = connector->connector_id; - - if (m_atomicModeSetting) { - drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd); - if (drmOutput->m_crtc->init()) { - drmOutput->m_crtc->setOutput(drmOutput); - } else { - qCWarning(KWIN_DRM) << "Crtc object failed, skipping output on connector" << connector->connector_id; - delete drmOutput->m_crtc; - delete drmOutput; - continue; - } - - drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd); - if (drmOutput->m_conn->init()) { - drmOutput->m_conn->setOutput(drmOutput); - } else { - qCWarning(KWIN_DRM) << "Connector object failed, skipping output on connector" << connector->connector_id; - delete drmOutput->m_conn; - delete drmOutput; - continue; - } - } - - if (crtc->mode_valid) { - drmOutput->m_mode = crtc->mode; } else { - drmOutput->m_mode = connector->modes[0]; + pendingConnectors << con; } - qCDebug(KWIN_DRM) << "For new output use mode " << drmOutput->m_mode.name; - - if (!drmOutput->init(connector.data())) { - qCWarning(KWIN_DRM) << "Failed to create output for connector " << connector->connector_id; - delete drmOutput; - continue; - } - qCDebug(KWIN_DRM) << "Found new output with uuid" << drmOutput->uuid(); - connectedOutputs << drmOutput; } - std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_connector < b->m_connector; }); + // check for outputs which got removed auto it = m_outputs.begin(); while (it != m_outputs.end()) { @@ -418,11 +395,80 @@ void DrmBackend::queryResources() emit outputRemoved(removed); delete removed; } - for (auto it = connectedOutputs.constBegin(); it != connectedOutputs.constEnd(); ++it) { - if (!m_outputs.contains(*it)) { - emit outputAdded(*it); + + // now check new connections + for (DrmConnector *con : qAsConst(pendingConnectors)) { + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> connector(drmModeGetConnector(m_fd, con->id())); + if (!connector) { + continue; + } + if (connector->count_modes == 0) { + continue; + } + bool outputDone = false; + + QVector encoders = con->encoders(); + for (auto encId : qAsConst(encoders)) { + ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, encId)); + if (!encoder) { + continue; + } + for (DrmCrtc *crtc : qAsConst(m_crtcs)) { + if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) { + continue; + } + + // check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode) + auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(), + [crtc] (DrmOutput *o) { + return o->m_crtc == crtc; + } + ); + if (it != connectedOutputs.constEnd()) { + continue; + } + + // we found a suitable encoder+crtc + // TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning + ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id())); + if (!modeCrtc) { + continue; + } + + DrmOutput *output = new DrmOutput(this); + con->setOutput(output); + output->m_conn = con; + crtc->setOutput(output); + output->m_crtc = crtc; + connect(output, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); + + if (modeCrtc->mode_valid) { + output->m_mode = modeCrtc->mode; + } else { + output->m_mode = connector->modes[0]; + } + qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name; + + if (!output->init(connector.data())) { + qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id(); + con->setOutput(nullptr); + crtc->setOutput(nullptr); + delete output; + continue; + } + qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid(); + + connectedOutputs << output; + emit outputAdded(output); + outputDone = true; + break; + } + if (outputDone) { + break; + } } } + std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); }); m_outputs = connectedOutputs; readOutputsConfiguration(); if (!m_outputs.isEmpty()) { @@ -484,7 +530,7 @@ void DrmBackend::configurationChangeRequested(KWayland::Server::OutputConfigurat DrmOutput *DrmBackend::findOutput(quint32 connector) { auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { - return o->m_connector == connector; + return o->m_conn->id() == connector; }); if (it != m_outputs.constEnd()) { return *it; @@ -503,51 +549,6 @@ DrmOutput *DrmBackend::findOutput(const QByteArray &uuid) return nullptr; } -quint32 DrmBackend::findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok) -{ - if (ok) { - *ok = false; - } - ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoder_id)); - if (encoder) { - if (!crtcIsUsed(encoder->crtc_id)) { - if (ok) { - *ok = true; - } - return encoder->crtc_id; - } - } - // let's iterate over all encoders to find a suitable crtc - for (int i = 0; i < connector->count_encoders; ++i) { - ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, connector->encoders[i])); - if (!encoder) { - continue; - } - for (int j = 0; j < res->count_crtcs; ++j) { - if (!(encoder->possible_crtcs & (1 << j))) { - continue; - } - if (!crtcIsUsed(res->crtcs[j])) { - if (ok) { - *ok = true; - } - return res->crtcs[j]; - } - } - } - return 0; -} - -bool DrmBackend::crtcIsUsed(quint32 crtc) -{ - auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), - [crtc] (DrmOutput *o) { - return o->m_crtcId == crtc; - } - ); - return it != m_outputs.constEnd(); -} - void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) { if (output->present(buffer)) { diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h index a3843696e2..fa056f5c86 100644 --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -55,6 +55,8 @@ class UdevMonitor; class DrmOutput; class DrmPlane; +class DrmCrtc; +class DrmConnector; class KWIN_EXPORT DrmBackend : public Platform @@ -128,8 +130,6 @@ private: void updateCursor(); void moveCursor(); void initCursor(); - quint32 findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok = nullptr); - bool crtcIsUsed(quint32 crtc); void outputDpmsChanged(); void readOutputsConfiguration(); QByteArray generateOutputConfigurationUuid() const; @@ -139,6 +139,11 @@ private: QScopedPointer m_udevMonitor; int m_fd = -1; int m_drmId = 0; + // all crtcs + QVector m_crtcs; + // all connectors + QVector m_connectors; + // currently active output pipelines (planes + crtc + encoder + connector) QVector m_outputs; DrmBuffer *m_cursor[2]; bool m_atomicModeSetting = false; diff --git a/plugins/platforms/drm/drm_buffer.cpp b/plugins/platforms/drm/drm_buffer.cpp index 0f1c7a41e5..d29059967a 100644 --- a/plugins/platforms/drm/drm_buffer.cpp +++ b/plugins/platforms/drm/drm_buffer.cpp @@ -93,14 +93,14 @@ DrmBuffer::DrmBuffer(DrmBackend *backend, gbm_surface *surface) DrmBuffer::~DrmBuffer() { + if (m_bufferId) { + drmModeRmFB(m_backend->fd(), m_bufferId); + } m_backend->bufferDestroyed(this); delete m_image; if (m_memory) { munmap(m_memory, m_bufferSize); } - if (m_bufferId) { - drmModeRmFB(m_backend->fd(), m_bufferId); - } if (m_handle) { drm_mode_destroy_dumb destroyArgs; destroyArgs.handle = m_handle; diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h index 00b513b27f..0c7083d7d6 100644 --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -61,7 +61,6 @@ public: bool deleteAfterPageFlip() const { return m_deleteAfterPageFlip; } - void releaseGbm(); private: diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp index 67a695797f..4f5552e57c 100644 --- a/plugins/platforms/drm/drm_object_connector.cpp +++ b/plugins/platforms/drm/drm_object_connector.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "drm_object_connector.h" +#include "drm_pointer.h" #include "logging.h" namespace KWin @@ -26,7 +27,13 @@ namespace KWin DrmConnector::DrmConnector(uint32_t connector_id, int fd) : DrmObject(connector_id, fd) { - + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(fd, connector_id)); + if (!con) { + return; + } + for (int i = 0; i < con->count_encoders; ++i) { + m_encoders << con->encoders[i]; + } } DrmConnector::~DrmConnector() = default; @@ -61,4 +68,13 @@ bool DrmConnector::initProps() return true; } +bool DrmConnector::isConnected() +{ + ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_fd, m_id)); + if (!con) { + return false; + } + return con->connection == DRM_MODE_CONNECTED; +} + } diff --git a/plugins/platforms/drm/drm_object_connector.h b/plugins/platforms/drm/drm_object_connector.h index dcaf200c0d..6257febb8f 100644 --- a/plugins/platforms/drm/drm_object_connector.h +++ b/plugins/platforms/drm/drm_object_connector.h @@ -38,8 +38,17 @@ public: CrtcId = 0, Count }; + + QVector encoders() { + return m_encoders; + } bool initProps(); + bool isConnected(); + + +private: + QVector m_encoders; }; } diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp index 1d5f03c8f7..ec83a3ca13 100644 --- a/plugins/platforms/drm/drm_object_crtc.cpp +++ b/plugins/platforms/drm/drm_object_crtc.cpp @@ -18,21 +18,27 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "drm_object_crtc.h" +#include "drm_backend.h" +#include "drm_output.h" +#include "drm_buffer.h" #include "logging.h" namespace KWin { -DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd) - : DrmObject(crtc_id, fd) +DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd, int resIndex) + : DrmObject(crtc_id, fd), + m_resIndex(resIndex) { } -DrmCrtc::~DrmCrtc() = default; +DrmCrtc::~DrmCrtc() +{ +} bool DrmCrtc::init() { - qCDebug(KWIN_DRM) << "Creating CRTC" << m_id; + qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id; if (!initProps()) { return false; @@ -61,4 +67,41 @@ bool DrmCrtc::initProps() return true; } +void DrmCrtc::flipBuffer() +{ + if (m_currentBuffer && m_currentBuffer->deleteAfterPageFlip() && m_currentBuffer != m_nextBuffer) { + delete m_currentBuffer; + } + m_currentBuffer = m_nextBuffer; + m_nextBuffer = nullptr; + + delete m_blackBuffer; + m_blackBuffer = nullptr; +} + +bool DrmCrtc::blank() +{ + if (!m_blackBuffer) { + DrmBuffer *blackBuffer = m_output->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_currentBuffer->deleteAfterPageFlip()) { + delete m_currentBuffer; + delete m_nextBuffer; + } + m_currentBuffer = nullptr; + m_nextBuffer = nullptr; + return true; + } + return false; +} + } diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h index cc9b5c94e1..1179c9b6a5 100644 --- a/plugins/platforms/drm/drm_object_crtc.h +++ b/plugins/platforms/drm/drm_object_crtc.h @@ -25,12 +25,13 @@ along with this program. If not, see . namespace KWin { +class DrmBackend; class DrmBuffer; class DrmCrtc : public DrmObject { public: - DrmCrtc(uint32_t crtc_id, int fd); + DrmCrtc(uint32_t crtc_id, int fd, int resIndex); virtual ~DrmCrtc(); @@ -43,6 +44,31 @@ public: }; bool initProps(); + + int resIndex() const { + return m_resIndex; + } + + DrmBuffer *current() { + return m_currentBuffer; + } + DrmBuffer *next() { + return m_nextBuffer; + } + void setNext(DrmBuffer *buffer) { + m_nextBuffer = buffer; + } + + void flipBuffer(); + bool blank(); + +private: + DrmBackend *m_backend; + int m_resIndex; + + DrmBuffer *m_currentBuffer = nullptr; + DrmBuffer *m_nextBuffer = nullptr; + DrmBuffer *m_blackBuffer = nullptr; }; } diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp index c983f2e87a..034fa8a622 100644 --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -62,54 +62,38 @@ DrmOutput::DrmOutput(DrmBackend *backend) DrmOutput::~DrmOutput() { hideCursor(); - cleanupBlackBuffer(); - delete m_crtc; - delete m_conn; + m_crtc->blank(); delete m_waylandOutput.data(); delete m_waylandOutputDevice.data(); } -void DrmOutput::cleanup() +void DrmOutput::releaseGbm() { - if (m_currentBuffer) { - m_currentBuffer->releaseGbm(); - } - if (m_nextBuffer) { - m_nextBuffer->releaseGbm(); + if (DrmBuffer *b = m_crtc->current()) { + b->releaseGbm(); } if (m_primaryPlane) { if (m_primaryPlane->current()) { m_primaryPlane->current()->releaseGbm(); } - if (m_primaryPlane->next()) { - m_primaryPlane->next()->releaseGbm(); - } } } void DrmOutput::hideCursor() { - drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); -} - -void DrmOutput::restoreSaved() -{ - if (!m_savedCrtc.isNull()) { - drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, - m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); - } + drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0); } void DrmOutput::showCursor(DrmBuffer *c) { const QSize &s = c->size(); - drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); + drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()); } void DrmOutput::moveCursor(const QPoint &globalPos) { const QPoint p = (globalPos - m_globalPos) * m_scale; - drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); + drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); } QSize DrmOutput::pixelSize() const @@ -193,8 +177,7 @@ bool DrmOutput::init(drmModeConnector *connector) return false; } } - m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); - if (!blank()) { + if (!m_crtc->blank()) { return false; } setDpms(DpmsMode::On); @@ -312,7 +295,7 @@ bool DrmOutput::init(drmModeConnector *connector) void DrmOutput::initUuid() { QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(QByteArray::number(m_connector)); + hash.addData(QByteArray::number(m_conn->id())); hash.addData(m_edid.eisaId); hash.addData(m_edid.monitorName); hash.addData(m_edid.serialNumber); @@ -513,12 +496,12 @@ bool DrmOutput::initPrimaryPlane() if (m_primaryPlane) { // Output already has a primary plane continue; } - if (!p->isCrtcSupported(m_crtcId)) { + if (!p->isCrtcSupported(m_crtc->id())) { continue; } p->setOutput(this); m_primaryPlane = p; - qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId; + qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtc->id(); return true; } qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; @@ -541,12 +524,12 @@ bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs lay if (m_cursorPlane) { // Output already has a cursor plane continue; } - if (!p->isCrtcSupported(m_crtcId)) { + if (!p->isCrtcSupported(m_crtc->id())) { continue; } p->setOutput(this); m_cursorPlane = p; - qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId; + qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtc->id(); return true; } return false; @@ -580,17 +563,17 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) drmModeAtomicReq *req = drmModeAtomicAlloc(); if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) { - qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtcId; + 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_crtcId; + qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtc->id(); } else { - qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId; + qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtc->id(); } drmModeAtomicFree(req); } else { - if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) { + if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) { qCWarning(KWIN_DRM) << "Setting DPMS failed"; return; } @@ -605,7 +588,7 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) m_backend->outputWentOff(); } else { m_backend->checkOutputsAreOn(); - blank(); + m_crtc->blank(); if (Compositor *compositor = Compositor::self()) { compositor->addRepaintFull(); } @@ -698,6 +681,9 @@ bool DrmOutput::commitChanges() void DrmOutput::pageFlipped() { + if (!m_crtc) { + return; + } if (m_backend->atomicModeSetting()){ foreach (DrmPlane *p, m_planesFlipList) { pageFlippedBufferRemover(p->current(), p->next()); @@ -707,18 +693,15 @@ void DrmOutput::pageFlipped() m_planesFlipList.clear(); } else { - if (!m_nextBuffer) { + if (!m_crtc->next()) { // on manual vt switch - if (m_currentBuffer) { - m_currentBuffer->releaseGbm(); + if (DrmBuffer *b = m_crtc->current()) { + b->releaseGbm(); } return; } - pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer); - m_currentBuffer = m_nextBuffer; - m_nextBuffer = nullptr; + m_crtc->flipBuffer(); } - cleanupBlackBuffer(); } void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) @@ -728,28 +711,6 @@ void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuf } } -void DrmOutput::cleanupBlackBuffer() -{ - if (m_blackBuffer) { - delete m_blackBuffer; - m_blackBuffer = nullptr; - } -} - -bool DrmOutput::blank() -{ - if (!m_blackBuffer) { - m_blackBuffer = m_backend->createBuffer(pixelSize()); - if (!m_blackBuffer->map()) { - cleanupBlackBuffer(); - return false; - } - m_blackBuffer->image()->fill(Qt::black); - } - // TODO: Do this atomically - return setModeLegacy(m_blackBuffer); -} - bool DrmOutput::present(DrmBuffer *buffer) { if (!buffer || buffer->bufferId() == 0) { @@ -868,11 +829,11 @@ bool DrmOutput::presentAtomically(DrmBuffer *buffer) bool DrmOutput::presentLegacy(DrmBuffer *buffer) { - if (m_nextBuffer) { + if (m_crtc->next()) { return false; } if (!LogindIntegration::self()->isActiveSession()) { - m_nextBuffer = buffer; + m_crtc->setNext(buffer); return false; } if (m_dpmsMode != DpmsMode::On) { @@ -885,9 +846,9 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer) return false; } int errno_save = 0; - const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; + const bool ok = drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; if (ok) { - m_nextBuffer = buffer; + m_crtc->setNext(buffer); } else { errno_save = errno; qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); @@ -898,7 +859,8 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer) bool DrmOutput::setModeLegacy(DrmBuffer *buffer) { - if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { + uint32_t connId = m_conn->id(); + if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { m_lastStride = buffer->stride(); m_lastGbm = buffer->isGbm(); return true; diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index 064ca214bb..bcf193b271 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -61,15 +61,13 @@ public: QSize physicalSize; }; virtual ~DrmOutput(); - void cleanup(); + void releaseGbm(); void showCursor(DrmBuffer *buffer); void hideCursor(); void moveCursor(const QPoint &globalPos); bool init(drmModeConnector *connector); bool present(DrmBuffer *buffer); void pageFlipped(); - void restoreSaved(); - bool blank(); /** * This sets the changes and tests them against the DRM output @@ -108,8 +106,9 @@ Q_SIGNALS: private: friend class DrmBackend; + friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc + // and save the connector ids in the DrmCrtc instance. DrmOutput(DrmBackend *backend); - void cleanupBlackBuffer(); bool presentAtomically(DrmBuffer *buffer); bool presentLegacy(DrmBuffer *buffer); bool setModeLegacy(DrmBuffer *buffer); @@ -126,23 +125,14 @@ private: DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); DrmBackend *m_backend; + DrmConnector *m_conn = nullptr; + DrmCrtc *m_crtc = nullptr; QPoint m_globalPos; qreal m_scale = 1; - quint32 m_crtcId = 0; - quint32 m_connector = 0; quint32 m_lastStride = 0; bool m_lastGbm = false; drmModeModeInfo m_mode; - DrmBuffer *m_currentBuffer = nullptr; - DrmBuffer *m_nextBuffer = nullptr; - DrmBuffer *m_blackBuffer = nullptr; - struct CrtcCleanup { - static void inline cleanup(_drmModeCrtc *ptr) { - drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670 - } - }; Edid m_edid; - QScopedPointer<_drmModeCrtc, CrtcCleanup> m_savedCrtc; QPointer m_waylandOutput; QPointer m_waylandOutputDevice; QPointer m_changeset; @@ -150,8 +140,6 @@ private: DpmsMode m_dpmsMode = DpmsMode::On; QByteArray m_uuid; - DrmConnector *m_conn = nullptr; - DrmCrtc *m_crtc = nullptr; uint32_t m_blobId = 0; DrmPlane* m_primaryPlane = nullptr; DrmPlane* m_cursorPlane = nullptr; diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index 3653ead7ad..a1d722857d 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -74,7 +74,7 @@ void EglGbmBackend::cleanupSurfaces() void EglGbmBackend::cleanupOutput(const Output &o) { - o.output->cleanup(); + o.output->releaseGbm(); if (o.eglSurface != EGL_NO_SURFACE) { eglDestroySurface(eglDisplay(), o.eglSurface);