diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt --- a/plugins/platforms/drm/CMakeLists.txt +++ b/plugins/platforms/drm/CMakeLists.txt @@ -13,7 +13,7 @@ ) if(HAVE_GBM) - set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp) + set(DRM_SOURCES ${DRM_SOURCES} egl_gbm_backend.cpp drm_buffer_gbm.cpp) endif() add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) 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 @@ -23,6 +23,9 @@ #include "input.h" #include "drm_buffer.h" +#if HAVE_GBM +#include "drm_buffer_gbm.h" +#endif #include "drm_inputeventfilter.h" #include "drm_pointer.h" @@ -55,6 +58,8 @@ class DrmOutput; class DrmPlane; +class DrmCrtc; +class DrmConnector; class KWIN_EXPORT DrmBackend : public Platform @@ -72,27 +77,27 @@ OpenGLBackend* createOpenGLBackend() override; void init() override; - DrmBuffer *createBuffer(const QSize &size); - DrmBuffer *createBuffer(gbm_surface *surface); + DrmDumbBuffer *createBuffer(const QSize &size); + DrmSurfaceBuffer *createBuffer(gbm_surface *surface); void present(DrmBuffer *buffer, DrmOutput *output); int fd() const { return m_fd; } QVector outputs() const { return m_outputs; } - QVector buffers() const { - return m_buffers; - } QVector planes() const { return m_planes; } - void bufferDestroyed(DrmBuffer *b); void outputWentOff(); void checkOutputsAreOn(); + // QPainter reuses buffers + bool deleteBufferAfterPageFlip() const { + return m_deleteBufferAfterPageFlip; + } // returns use of AMS, default is not/legacy bool atomicModeSetting() const { return m_atomicModeSetting; @@ -128,8 +133,6 @@ 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,14 +142,19 @@ 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]; + DrmDumbBuffer *m_cursor[2]; + bool m_deleteBufferAfterPageFlip; bool m_atomicModeSetting = false; bool m_cursorEnabled = false; int m_cursorIndex = 0; int m_pageFlipsPending = 0; bool m_active = false; - QVector m_buffers; // all available planes: primarys, cursors and overlays QVector m_planes; QScopedPointer m_dpmsFilter; 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 @@ -89,8 +89,10 @@ 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); @@ -165,12 +167,12 @@ } m_active = true; if (!usesSoftwareCursor()) { - DrmBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; + DrmDumbBuffer *c = m_cursor[(m_cursorIndex + 1) % 2]; const QPoint cp = Cursor::pos() - softwareCursorHotspot(); 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 @@ 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 @@ } } + 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,81 +360,29 @@ if (m_fd < 0) { return; } + ScopedDrmPointer<_drmModeRes, &drmModeFreeResources> resources(drmModeGetResources(m_fd)); if (!resources) { qCWarning(KWIN_DRM) << "drmModeGetResources failed"; return; } 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) { - continue; - } - if (connector->connection != DRM_MODE_CONNECTED) { - continue; - } - if (connector->count_modes == 0) { - continue; - } - if (DrmOutput *o = findOutput(connector->connector_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; - } + QVector pendingConnectors; - 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; - } + // split up connected connectors in already or not yet assigned ones + for (DrmConnector *con : qAsConst(m_connectors)) { + if (!con->isConnected()) { + continue; } - if (crtc->mode_valid) { - drmOutput->m_mode = crtc->mode; + if (DrmOutput *o = findOutput(con->id())) { + connectedOutputs << o; } 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 @@ 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 @@ 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 @@ 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)) { @@ -607,7 +608,7 @@ void DrmBackend::setCursor() { - DrmBuffer *c = m_cursor[m_cursorIndex]; + DrmDumbBuffer *c = m_cursor[m_cursorIndex]; m_cursorIndex = (m_cursorIndex + 1) % 2; if (m_cursorEnabled) { for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { @@ -674,42 +675,36 @@ QPainterBackend *DrmBackend::createQPainterBackend() { + m_deleteBufferAfterPageFlip = false; return new DrmQPainterBackend(this); } OpenGLBackend *DrmBackend::createOpenGLBackend() { #if HAVE_GBM + m_deleteBufferAfterPageFlip = true; return new EglGbmBackend(this); #else return Platform::createOpenGLBackend(); #endif } -DrmBuffer *DrmBackend::createBuffer(const QSize &size) +DrmDumbBuffer *DrmBackend::createBuffer(const QSize &size) { - DrmBuffer *b = new DrmBuffer(this, size); - m_buffers << b; + DrmDumbBuffer *b = new DrmDumbBuffer(this, size); return b; } -DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface) +DrmSurfaceBuffer *DrmBackend::createBuffer(gbm_surface *surface) { #if HAVE_GBM - DrmBuffer *b = new DrmBuffer(this, surface); - b->m_deleteAfterPageFlip = true; - m_buffers << b; + DrmSurfaceBuffer *b = new DrmSurfaceBuffer(this, surface); return b; #else return nullptr; #endif } -void DrmBackend::bufferDestroyed(DrmBuffer *b) -{ - m_buffers.removeAll(b); -} - void DrmBackend::outputDpmsChanged() { if (m_outputs.isEmpty()) { diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -23,62 +23,61 @@ #include #include -struct gbm_bo; -struct gbm_surface; - namespace KWin { class DrmBackend; class DrmBuffer { public: - ~DrmBuffer(); + DrmBuffer(DrmBackend *backend); + virtual ~DrmBuffer() = default; - bool map(QImage::Format format = QImage::Format_RGB32); - QImage *image() const { - return m_image; - } - quint32 handle() const { - return m_handle; + virtual bool needsModeChange(DrmBuffer *b) const {Q_UNUSED(b) return false;} + + quint32 bufferId() const { + return m_bufferId; } + const QSize &size() const { return m_size; } - quint32 bufferId() const { - return m_bufferId; + + virtual void releaseGbm() {} + +protected: + DrmBackend *m_backend; + quint32 m_bufferId = 0; + QSize m_size; +}; + +class DrmDumbBuffer : public DrmBuffer +{ +public: + DrmDumbBuffer(DrmBackend *backend, const QSize &size); + ~DrmDumbBuffer(); + + bool needsModeChange(DrmBuffer *b) const override; + + bool map(QImage::Format format = QImage::Format_RGB32); + quint32 handle() const { + return m_handle; + } + QImage *image() const { + return m_image; } + quint32 stride() const { return m_stride; } - gbm_bo *gbm() const { - return m_bo; - } - bool isGbm() const { - return m_bo != nullptr; - } - bool deleteAfterPageFlip() const { - return m_deleteAfterPageFlip; - } - - void releaseGbm(); private: - friend class DrmBackend; - DrmBuffer(DrmBackend *backend, const QSize &size); - DrmBuffer(DrmBackend *backend, gbm_surface *surface); - DrmBackend *m_backend; - gbm_surface *m_surface = nullptr; - gbm_bo *m_bo = nullptr; - QSize m_size; quint32 m_handle = 0; - quint32 m_bufferId = 0; - quint32 m_stride = 0; quint64 m_bufferSize = 0; void *m_memory = nullptr; QImage *m_image = nullptr; - bool m_deleteAfterPageFlip = false; + quint32 m_stride = 0; }; } diff --git a/plugins/platforms/drm/drm_buffer.cpp b/plugins/platforms/drm/drm_buffer.cpp --- a/plugins/platforms/drm/drm_buffer.cpp +++ b/plugins/platforms/drm/drm_buffer.cpp @@ -27,18 +27,20 @@ #include // drm #include -#if HAVE_GBM -#include -#endif namespace KWin { - -DrmBuffer::DrmBuffer(DrmBackend *backend, const QSize &size) +DrmBuffer:: DrmBuffer(DrmBackend *backend) : m_backend(backend) - , m_size(size) { +} + +// DrmDumbBuffer +DrmDumbBuffer::DrmDumbBuffer(DrmBackend *backend, const QSize &size) + : DrmBuffer(backend) +{ + m_size = size; drm_mode_create_dumb createArgs; memset(&createArgs, 0, sizeof createArgs); createArgs.bpp = 32; @@ -57,59 +59,32 @@ } } - -#if HAVE_GBM -static void gbmCallback(gbm_bo *bo, void *data) +DrmDumbBuffer::~DrmDumbBuffer() { - DrmBackend *backend = reinterpret_cast(data); - const auto &buffers = backend->buffers(); - for (auto buffer: buffers) { - if (buffer->gbm() == bo) { - delete buffer; - return; - } - } -} -#endif - -DrmBuffer::DrmBuffer(DrmBackend *backend, gbm_surface *surface) - : m_backend(backend) - , m_surface(surface) -{ -#if HAVE_GBM - m_bo = gbm_surface_lock_front_buffer(surface); - if (!m_bo) { - qCWarning(KWIN_DRM) << "Locking front buffer failed"; - return; - } - m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); - m_stride = gbm_bo_get_stride(m_bo); - if (drmModeAddFB(m_backend->fd(), m_size.width(), m_size.height(), 24, 32, m_stride, gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { - qCWarning(KWIN_DRM) << "drmModeAddFB failed"; + if (m_bufferId) { + drmModeRmFB(m_backend->fd(), m_bufferId); } - gbm_bo_set_user_data(m_bo, m_backend, gbmCallback); -#endif -} -DrmBuffer::~DrmBuffer() -{ - 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; drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); } - releaseGbm(); } -bool DrmBuffer::map(QImage::Format format) +bool DrmDumbBuffer::needsModeChange(DrmBuffer *b) const { + if (DrmDumbBuffer *db = dynamic_cast(b)) { + return m_stride != db->stride(); + } else { + return true; + } +} + +bool DrmDumbBuffer::map(QImage::Format format) { if (!m_handle || !m_bufferId) { return false; @@ -129,14 +104,4 @@ return !m_image->isNull(); } -void DrmBuffer::releaseGbm() -{ -#if HAVE_GBM - if (m_bo) { - gbm_surface_release_buffer(m_surface, m_bo); - m_bo = nullptr; - } -#endif -} - } diff --git a/plugins/platforms/drm/drm_buffer_gbm.h b/plugins/platforms/drm/drm_buffer_gbm.h new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_buffer_gbm.h @@ -0,0 +1,61 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2017 Roman Gilg +Copyright 2015 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_DRM_BUFFER_GBM_H +#define KWIN_DRM_BUFFER_GBM_H + +#include "drm_buffer.h" + +struct gbm_bo; +struct gbm_surface; + +namespace KWin +{ + +class DrmBackend; + +class DrmSurfaceBuffer : public DrmBuffer +{ +public: + DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface); + ~DrmSurfaceBuffer(); + + bool needsModeChange(DrmBuffer *b) const override { + if (DrmSurfaceBuffer *sb = dynamic_cast(b)) { + return hasBo() != sb->hasBo(); + } else { + return true; + } + } + + bool hasBo() const { + return m_bo != nullptr; + } + void releaseGbm() override; + +private: + gbm_surface *m_surface = nullptr; + gbm_bo *m_bo = nullptr; +}; + +} + +#endif + diff --git a/plugins/platforms/drm/drm_buffer_gbm.cpp b/plugins/platforms/drm/drm_buffer_gbm.cpp new file mode 100644 --- /dev/null +++ b/plugins/platforms/drm/drm_buffer_gbm.cpp @@ -0,0 +1,69 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2017 Roman Gilg +Copyright 2015 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "drm_backend.h" +#include "drm_buffer_gbm.h" + +#include "logging.h" + +// system +#include +#include +// drm +#include +#include + +namespace KWin +{ + +// DrmSurfaceBuffer +DrmSurfaceBuffer::DrmSurfaceBuffer(DrmBackend *backend, gbm_surface *surface) + : DrmBuffer(backend) + , m_surface(surface) +{ + m_bo = gbm_surface_lock_front_buffer(surface); + if (!m_bo) { + qCWarning(KWIN_DRM) << "Locking front buffer failed"; + return; + } + m_size = QSize(gbm_bo_get_width(m_bo), gbm_bo_get_height(m_bo)); + if (drmModeAddFB(m_backend->fd(), m_size.width(), m_size.height(), 24, 32, gbm_bo_get_stride(m_bo), gbm_bo_get_handle(m_bo).u32, &m_bufferId) != 0) { + qCWarning(KWIN_DRM) << "drmModeAddFB failed"; + } + gbm_bo_set_user_data(m_bo, this, nullptr); +} + +DrmSurfaceBuffer::~DrmSurfaceBuffer() +{ + if (m_bufferId) { + drmModeRmFB(m_backend->fd(), m_bufferId); + } + releaseGbm(); +} + +void DrmSurfaceBuffer::releaseGbm() +{ + if (m_bo) { + gbm_surface_release_buffer(m_surface, m_bo); + m_bo = nullptr; + } +} + +} 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 @@ -38,8 +38,17 @@ CrtcId = 0, Count }; + + QVector encoders() { + return m_encoders; + } bool initProps(); + bool isConnected(); + + +private: + QVector m_encoders; }; } 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,15 +18,22 @@ along with this program. If not, see . *********************************************************************/ #include "drm_object_connector.h" +#include "drm_pointer.h" #include "logging.h" 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 @@ 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_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 @@ -25,12 +25,14 @@ namespace KWin { +class DrmBackend; class DrmBuffer; +class DrmDumbBuffer; 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 +45,31 @@ }; 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; + DrmDumbBuffer *m_blackBuffer = 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 @@ -18,21 +18,27 @@ 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 @@ return true; } +void DrmCrtc::flipBuffer() +{ + if (m_currentBuffer && m_output->m_backend->deleteBufferAfterPageFlip() && 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) { + DrmDumbBuffer *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_output->m_backend->deleteBufferAfterPageFlip()) { + delete m_currentBuffer; + delete m_nextBuffer; + } + m_currentBuffer = nullptr; + m_nextBuffer = nullptr; + return true; + } + return false; +} + } 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 @@ -46,6 +46,7 @@ class DrmBackend; class DrmBuffer; +class DrmDumbBuffer; class DrmPlane; class DrmConnector; class DrmCrtc; @@ -61,15 +62,13 @@ QSize physicalSize; }; virtual ~DrmOutput(); - void cleanup(); - void showCursor(DrmBuffer *buffer); + void releaseGbm(); + void showCursor(DrmDumbBuffer *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 +107,9 @@ 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,32 +126,20 @@ 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; KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms; 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/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -62,54 +62,38 @@ 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) +void DrmOutput::showCursor(DrmDumbBuffer *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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ m_backend->outputWentOff(); } else { m_backend->checkOutputsAreOn(); - blank(); + m_crtc->blank(); if (Compositor *compositor = Compositor::self()) { compositor->addRepaintFull(); } @@ -698,6 +681,9 @@ void DrmOutput::pageFlipped() { + if (!m_crtc) { + return; + } if (m_backend->atomicModeSetting()){ foreach (DrmPlane *p, m_planesFlipList) { pageFlippedBufferRemover(p->current(), p->next()); @@ -707,49 +693,24 @@ 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) { - if (oldbuffer && oldbuffer->deleteAfterPageFlip() && oldbuffer != newbuffer) { + if (oldbuffer && m_backend->deleteBufferAfterPageFlip() && oldbuffer != newbuffer) { delete oldbuffer; } } -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,26 +829,27 @@ 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) { return false; } // Do we need to set a new mode first? - if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){ - if (!setModeLegacy(buffer)) + if (!m_crtc->current() || m_crtc->current()->needsModeChange(buffer)) { + if (!setModeLegacy(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,9 +860,8 @@ bool DrmOutput::setModeLegacy(DrmBuffer *buffer) { - if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { - m_lastStride = buffer->stride(); - m_lastGbm = buffer->isGbm(); + uint32_t connId = m_conn->id(); + if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { return true; } else { qCWarning(KWIN_DRM) << "Mode setting failed"; diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -74,7 +74,7 @@ void EglGbmBackend::cleanupOutput(const Output &o) { - o.output->cleanup(); + o.output->releaseGbm(); if (o.eglSurface != EGL_NO_SURFACE) { eglDestroySurface(eglDisplay(), o.eglSurface); diff --git a/plugins/platforms/drm/scene_qpainter_drm_backend.h b/plugins/platforms/drm/scene_qpainter_drm_backend.h --- a/plugins/platforms/drm/scene_qpainter_drm_backend.h +++ b/plugins/platforms/drm/scene_qpainter_drm_backend.h @@ -26,7 +26,7 @@ { class DrmBackend; -class DrmBuffer; +class DrmDumbBuffer; class DrmOutput; class DrmQPainterBackend : public QObject, public QPainterBackend @@ -47,7 +47,7 @@ private: void initOutput(DrmOutput *output); struct Output { - DrmBuffer *buffer[2]; + DrmDumbBuffer *buffer[2]; DrmOutput *output; int index = 0; };