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" @@ -44,6 +47,7 @@ class OutputDeviceInterface; class OutputChangeSet; class OutputManagementInterface; +class BufferInterface; } } @@ -55,6 +59,7 @@ class DrmOutput; class DrmPlane; +class DrmCrtc; class KWIN_EXPORT DrmBackend : public Platform @@ -72,27 +77,32 @@ OpenGLBackend* createOpenGLBackend() override; void init() override; - DrmBuffer *createBuffer(const QSize &size); - DrmBuffer *createBuffer(gbm_surface *surface); - void present(DrmBuffer *buffer, DrmOutput *output); + bool directScanoutChanged(DrmOutput *output, KWayland::Server::BufferInterface *buffer); + DrmDumbBuffer *createBuffer(const QSize &size); + DrmSurfaceBuffer *createBuffer(gbm_surface *surface); + DrmImportBuffer *createDirectScanoutBuffer(KWayland::Server::BufferInterface *buffer); + bool 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); + QVector overlayPlanes() const { + return m_overlayPlanes; + } 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 +138,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; @@ -140,15 +148,16 @@ int m_fd = -1; int m_drmId = 0; 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; + 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 @@ -38,6 +38,7 @@ // KWayland #include #include +#include // KF5 #include #include @@ -163,7 +164,7 @@ } 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; @@ -211,6 +212,13 @@ 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_backend->m_atomicModeSetting && + output->m_dpmsMode == DrmOutput::DpmsMode::On && + output->m_dpmsMode != output->m_dpmsModePending) { + output->dpmsOffAtomically(); + } + if (Compositor::self()) { Compositor::self()->bufferSwapComplete(); } @@ -268,9 +276,10 @@ DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd); if (p->init()) { - p->setPossibleCrtcs(kplane->possible_crtcs); - p->setFormats(kplane->formats, kplane->count_formats); m_planes << p; + if (p->type() == DrmPlane::TypeIndex::Overlay) { + m_overlayPlanes << p; + } } else { delete p; } @@ -334,6 +343,7 @@ qCWarning(KWIN_DRM) << "drmModeGetResources failed"; return; } + // TODO: Query min and max values to gate gbm buffer sizes (later for overlay planes)! QVector connectedOutputs; for (int i = 0; i < resources->count_connectors; ++i) { @@ -352,44 +362,76 @@ connectedOutputs << o; continue; } - bool crtcFound = false; - const quint32 crtcId = findCrtc(resources.data(), connector.data(), &crtcFound); - if (!crtcFound) { - continue; + + DrmCrtc *crtc = nullptr; + { + // let's iterate over all encoders to find a suitable crtc + drmModeRes *res = resources.data(); + drmModeConnector *con = connector.data(); + QVector outputs = connectedOutputs + m_outputs; + for (int i = 0; i < con->count_encoders; ++i) { + bool crtcFound = false; + ScopedDrmPointer<_drmModeEncoder, &drmModeFreeEncoder> encoder(drmModeGetEncoder(m_fd, con->encoders[i])); + if (!encoder) { + continue; + } + for (int j = 0; j < res->count_crtcs; ++j) { + if (!(encoder->possible_crtcs & (1 << j))) { + continue; + } + + // check if crtc isn't used yet + auto it = std::find_if(outputs.constBegin(), outputs.constEnd(), + [res, j] (DrmOutput *o) { + return o->m_crtc->id() == res->crtcs[j]; + } + ); + if (it != outputs.constEnd()) { + continue; + } + + crtc = new DrmCrtc(res->crtcs[j], m_fd, j); + crtcFound = true; + break; + } + if (crtcFound) { + break; + } + } } - ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> crtc(drmModeGetCrtc(m_fd, crtcId)); + if (!crtc) { continue; } + + ScopedDrmPointer<_drmModeCrtc, &drmModeFreeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id())); + if (!modeCrtc) { + delete crtc; + continue; + } DrmOutput *drmOutput = new DrmOutput(this); connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); - drmOutput->m_crtcId = crtcId; - drmOutput->m_connector = connector->connector_id; + drmOutput->m_crtc = crtc; + drmOutput->m_crtc->setOutput(drmOutput); + drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd); + drmOutput->m_conn->setOutput(drmOutput); if (m_atomicModeSetting) { - drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd); - if (drmOutput->m_crtc->init()) { - drmOutput->m_crtc->setOutput(drmOutput); - } else { + if (!drmOutput->m_crtc->init()) { 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 { + if (!drmOutput->m_conn->init()) { 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; + if (modeCrtc->mode_valid) { + drmOutput->m_mode = modeCrtc->mode; } else { drmOutput->m_mode = connector->modes[0]; } @@ -403,7 +445,7 @@ 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; }); + std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); }); // check for outputs which got removed auto it = m_outputs.begin(); while (it != m_outputs.end()) { @@ -481,7 +523,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; @@ -500,58 +542,30 @@ return nullptr; } -quint32 DrmBackend::findCrtc(drmModeRes *res, drmModeConnector *connector, bool *ok) +bool DrmBackend::directScanoutChanged(DrmOutput *output, KWayland::Server::BufferInterface *buffer) { - 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; + if (atomicModeSetting()) { + if (DrmImportBuffer *ib = dynamic_cast(output->m_primaryPlane->current())) { + return ib->bi() != buffer; } - } - // 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]; - } + } else { + if (DrmImportBuffer *ib = dynamic_cast(output->m_currentBuffer)) { + return ib->bi() != buffer; } } - 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(); + return true; } -void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) +bool DrmBackend::present(DrmBuffer *buffer, DrmOutput *output) { if (output->present(buffer)) { m_pageFlipsPending++; if (m_pageFlipsPending == 1 && Compositor::self()) { Compositor::self()->aboutToSwapBuffers(); } + return true; + } else { + return false; } } @@ -604,7 +618,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) { @@ -671,40 +685,48 @@ QPainterBackend *DrmBackend::createQPainterBackend() { + m_deleteBufferAfterPageFlip = false; return new DrmQPainterBackend(this); } OpenGLBackend *DrmBackend::createOpenGLBackend() { + m_deleteBufferAfterPageFlip = true; #if HAVE_GBM 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) +DrmImportBuffer *DrmBackend::createDirectScanoutBuffer(KWayland::Server::BufferInterface *buffer) { - m_buffers.removeAll(b); + if (!buffer) { + qCWarning(KWIN_DRM) << "Direct scanout buffer creation failed because Wayland BufferInterface was null."; + return nullptr; + } +#if HAVE_GBM + DrmImportBuffer *b = new DrmImportBuffer(this, buffer); + return b; +#else + return nullptr; +#endif } void DrmBackend::outputDpmsChanged() @@ -719,5 +741,4 @@ setOutputsEnabled(enabled); } - } 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,68 @@ #include #include -struct gbm_bo; -struct gbm_surface; - namespace KWin { class DrmBackend; class DrmBuffer { public: - ~DrmBuffer(); + DrmBuffer(DrmBackend *backend); + virtual ~DrmBuffer() = default; + + virtual bool needsModeChange(DrmBuffer *b) const {Q_UNUSED(b) return false;} - bool map(QImage::Format format = QImage::Format_RGB32); - QImage *image() const { - return m_image; - } - quint32 handle() const { - return m_handle; - } - const QSize &size() const { - return m_size; - } quint32 bufferId() const { return m_bufferId; } - quint32 stride() const { - return m_stride; + + const QSize &size() const { + return m_size; } - gbm_bo *gbm() const { - return m_bo; + + virtual void retire() {} + +protected: + DrmBackend *m_backend; + quint32 m_bufferId = 0; + quint64 m_bufferSize = 0; + QSize m_size; +}; + +class DrmDumbBuffer : public DrmBuffer +{ +public: + DrmDumbBuffer(DrmBackend *backend, const QSize &size); + ~DrmDumbBuffer(); + + bool needsModeChange(DrmBuffer *b) const override { + if (DrmDumbBuffer *db = dynamic_cast(b)) { + return m_stride != db->stride(); + } else { + return true; + } } - bool isGbm() const { - return m_bo != nullptr; + + bool map(QImage::Format format = QImage::Format_RGB32); + quint32 handle() const { + return m_handle; } - bool deleteAfterPageFlip() const { - return m_deleteAfterPageFlip; + QImage *image() const { + return m_image; } - void releaseGbm(); + quint32 stride() const { + return m_stride; + } 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,43 +59,8 @@ } } - -#if HAVE_GBM -static void gbmCallback(gbm_bo *bo, void *data) -{ - 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"; - } - gbm_bo_set_user_data(m_bo, m_backend, gbmCallback); -#endif -} - -DrmBuffer::~DrmBuffer() +DrmDumbBuffer::~DrmDumbBuffer() { - m_backend->bufferDestroyed(this); delete m_image; if (m_memory) { munmap(m_memory, m_bufferSize); @@ -106,10 +73,9 @@ destroyArgs.handle = m_handle; drmIoctl(m_backend->fd(), DRM_IOCTL_MODE_DESTROY_DUMB, &destroyArgs); } - releaseGbm(); } -bool DrmBuffer::map(QImage::Format format) +bool DrmDumbBuffer::map(QImage::Format format) { if (!m_handle || !m_bufferId) { return false; @@ -129,14 +95,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,88 @@ +/******************************************************************** + 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 KWayland +{ +namespace Server +{ +class BufferInterface; +} +} + +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 retire() override; + +private: + gbm_surface *m_surface = nullptr; + gbm_bo *m_bo = nullptr; +}; + +class DrmImportBuffer : public QObject, public DrmBuffer +{ + Q_OBJECT +public: + DrmImportBuffer(DrmBackend *backend, KWayland::Server::BufferInterface *buffer); + ~DrmImportBuffer(); + + bool hasBo() const { + return m_bo != nullptr; + } + QPointer bi() { + return m_bi; + } + +private: + QPointer m_bi; + 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,108 @@ +/******************************************************************** + 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 + +#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); + } + retire(); +} + +void DrmSurfaceBuffer::retire() +{ + if (m_bo) { + gbm_surface_release_buffer(m_surface, m_bo); + m_bo = nullptr; + } +} + +// DrmImportBuffer +DrmImportBuffer::DrmImportBuffer(DrmBackend *backend, KWayland::Server::BufferInterface *buffer) + : DrmBuffer(backend) +{ + m_bo = gbm_bo_import(backend->gbmDevice(), GBM_BO_IMPORT_WL_BUFFER, buffer->resource(), GBM_BO_USE_SCANOUT); + if (!m_bo) { + qCWarning(KWIN_DRM) << "Import of Wayland 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); + + m_bi = buffer; + m_bi->ref(); + QObject::connect(m_bi.data(), &KWayland::Server::BufferInterface::aboutToBeDestroyed, m_bi.data(), &KWayland::Server::BufferInterface::unref); + +} + +DrmImportBuffer::~DrmImportBuffer() +{ + if (m_bufferId) { + drmModeRmFB(m_backend->fd(), m_bufferId); + } + if (m_bo) { + gbm_bo_destroy(m_bo); + m_bo = nullptr; + } + if (m_bi) { + QObject::disconnect(m_bi.data(), &KWayland::Server::BufferInterface::aboutToBeDestroyed, m_bi.data(), &KWayland::Server::BufferInterface::unref); + m_bi->unref(); + m_bi = nullptr; + } +} + +} 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 @@ -40,63 +40,47 @@ virtual ~DrmObject() = 0; - enum class AtomicReturn { - NoChange, - Success, - Error - }; - virtual bool init() = 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: + 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); + const int m_fd = 0; 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 +97,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 @@ -56,27 +56,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_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,10 @@ namespace KWin { -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 +41,13 @@ }; bool initProps(); + + int resIndex() const { + return m_resIndex; + } + +private: + int m_resIndex; }; } 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 @@ -23,16 +23,17 @@ 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; bool DrmCrtc::init() { - qCDebug(KWIN_DRM) << "Creating CRTC" << m_id; + qCDebug(KWIN_DRM) << "Creating CRTC:" << resIndex() << "id:" << m_id; if (!initProps()) { return false; 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 @@ -34,7 +34,7 @@ public: DrmPlane(uint32_t plane_id, int fd); - virtual ~DrmPlane(); + ~DrmPlane(); enum class PropertyIndex { Type = 0, @@ -61,33 +61,26 @@ bool init(); 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); 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 @@ -30,7 +30,11 @@ { } -DrmPlane::~DrmPlane() = default; +DrmPlane::~DrmPlane() +{ + delete m_current; + delete m_next; +} bool DrmPlane::init() { @@ -44,8 +48,9 @@ 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]; } @@ -98,77 +103,34 @@ 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; - } - if (!m_propsPending) { - return DrmObject::AtomicReturn::NoChange; + return false; } - return DrmObject::AtomicReturn::Success; + return true; } } 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 @@ -38,14 +38,16 @@ class OutputDeviceInterface; class OutputChangeSet; class OutputManagementInterface; +class BufferInterface; } } namespace KWin { class DrmBackend; class DrmBuffer; +class DrmDumbBuffer; class DrmPlane; class DrmConnector; class DrmCrtc; @@ -61,10 +63,11 @@ QSize physicalSize; }; virtual ~DrmOutput(); - void showCursor(DrmBuffer *buffer); + void showCursor(DrmDumbBuffer *buffer); void hideCursor(); void moveCursor(const QPoint &globalPos); bool init(drmModeConnector *connector); + bool assignClientPlane(KWayland::Server::BufferInterface *buffer); bool present(DrmBuffer *buffer); void pageFlipped(); void restoreSaved(); @@ -89,7 +92,8 @@ }; void setDpms(DpmsMode mode); bool isDpmsEnabled() const { - return m_dpmsMode == DpmsMode::On; + // We care for current aswell as pending mode in order to allow first present in AMS. + return m_dpmsModePending == DpmsMode::On; } QByteArray uuid() const { @@ -104,29 +108,37 @@ DrmOutput(DrmBackend *backend); void cleanupBlackBuffer(); 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); void initDpms(drmModeConnector *connector); bool isCurrentMode(const drmModeModeInfo *mode) const; void initUuid(); void setGlobalPos(const QPoint &pos); - void pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer); bool initPrimaryPlane(); bool initCursorPlane(); - DrmObject::AtomicReturn atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); + + void dpmsOnHandler(); + void dpmsOffHandler(); + bool dpmsOffAtomically(); + bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); DrmBackend *m_backend; QPoint m_globalPos; - 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; + DrmDumbBuffer *m_blackBuffer = nullptr; struct CrtcCleanup { static void inline cleanup(_drmModeCrtc *ptr) { drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670 @@ -139,14 +151,16 @@ QPointer m_changeset; KWin::ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> m_dpms; DpmsMode m_dpmsMode = DpmsMode::On; + DpmsMode m_dpmsModePending = 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; - QVector m_planesFlipList; + QVector m_nextPlanesFlipList; + bool m_pageFlipPending = false; }; } 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,35 +63,73 @@ { hideCursor(); cleanupBlackBuffer(); + delete m_currentBuffer; + delete m_nextBuffer; + if (m_backend->atomicModeSetting()) { + // TODO: when having multiple planes, also clean up these + drmModeAtomicReq *req = drmModeAtomicAlloc(); + bool modeset = false; + if (m_primaryPlane) { + delete m_primaryPlane->current(); + delete m_primaryPlane->next(); + m_primaryPlane->setCurrent(nullptr); + m_primaryPlane->setNext(nullptr); + + 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); + + m_primaryPlane->atomicPopulate(req); + modeset = true; + } + if (m_conn) { + m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), 0); + m_conn->atomicPopulate(req); + modeset = true; + } + if (m_crtc) { + m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), 0); + m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), 0); + m_crtc->atomicPopulate(req); + modeset = true; + } + if (modeset) { + drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this); + } + m_primaryPlane->setOutput(nullptr); + } delete m_crtc; delete m_conn; delete m_waylandOutput.data(); delete m_waylandOutputDevice.data(); } void DrmOutput::hideCursor() { - drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); + drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0); } void DrmOutput::restoreSaved() { if (!m_savedCrtc.isNull()) { + uint32_t connId = m_conn->id(); drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, - m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); + m_savedCrtc->x, m_savedCrtc->y, &connId, 1, &m_savedCrtc->mode); } } -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; - drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); + drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); } QSize DrmOutput::size() const @@ -169,7 +207,7 @@ return false; } } - m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); + m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtc->id())); if (!blank()) { return false; } @@ -288,7 +326,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); @@ -489,12 +527,12 @@ if (m_primaryPlane) { // Output already has a primary plane continue; } - if (!p->isCrtcSupported(m_crtcId)) { + if (!p->isCrtcSupported(m_crtc->resIndex())) { 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."; @@ -517,12 +555,12 @@ if (m_cursorPlane) { // Output already has a cursor plane continue; } - if (!p->isCrtcSupported(m_crtcId)) { + if (!p->isCrtcSupported(m_crtc->resIndex())) { 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; @@ -547,45 +585,66 @@ 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_crtcId; - 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; + if (m_backend->atomicModeSetting()) { + if (mode == DpmsMode::On) { + if (m_pageFlipPending) { + m_pageFlipPending = false; + Compositor::self()->bufferSwapComplete(); + } + dpmsOnHandler(); } else { - qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId; + if (!m_pageFlipPending) { + dpmsOffAtomically(); + } } - 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) { + 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(); - blank(); - if (Compositor *compositor = Compositor::self()) { - compositor->addRepaintFull(); - } + + m_backend->checkOutputsAreOn(); + blank(); + 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 @@ -664,60 +723,113 @@ void DrmOutput::pageFlipped() { - if (m_backend->atomicModeSetting()){ - foreach (DrmPlane *p, m_planesFlipList) { - pageFlippedBufferRemover(p->current(), p->next()); - p->setCurrent(p->next()); - p->setNext(nullptr); - } - m_planesFlipList.clear(); - - } else { - if (!m_nextBuffer) { - return; - } - pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer); - m_currentBuffer = m_nextBuffer; - m_nextBuffer = nullptr; - } - cleanupBlackBuffer(); -} + // 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()) { + for (DrmPlane *p : m_nextPlanesFlipList) { + p->next()->retire(); + delete p->current(); + p->setCurrent(p->next()); + p->setNext(nullptr); + } + m_nextPlanesFlipList.clear(); -void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) -{ - if (newbuffer->deleteAfterPageFlip()) { - if ( oldbuffer && oldbuffer != newbuffer ) { - delete oldbuffer; + } else { + if (m_currentBuffer != m_nextBuffer) { + delete m_currentBuffer; + } + if (m_nextBuffer) { + m_nextBuffer->retire(); + } + m_currentBuffer = m_nextBuffer; + m_nextBuffer = nullptr; + cleanupBlackBuffer(); } } else { - // although oldbuffer's pointer is remapped in pageFlipped(), - // we ignore the pointer completely anywhere else in this case - newbuffer->releaseGbm(); + if (m_backend->atomicModeSetting()){ + for (DrmPlane *p : m_nextPlanesFlipList) { + p->setCurrent(p->next()); + p->setNext(nullptr); + } + m_nextPlanesFlipList.clear(); + } else { + m_currentBuffer = m_nextBuffer; + m_nextBuffer = nullptr; + cleanupBlackBuffer(); + } } + m_pageFlipPending = false; } void DrmOutput::cleanupBlackBuffer() { - if (m_blackBuffer) { - delete m_blackBuffer; - m_blackBuffer = nullptr; - } + delete m_blackBuffer; + m_blackBuffer = nullptr; } bool DrmOutput::blank() { + if (m_backend->atomicModeSetting()) { + // In Atomic Mode Setting we don't need any blanks. + return true; + } if (!m_blackBuffer) { m_blackBuffer = m_backend->createBuffer(size()); if (!m_blackBuffer->map()) { cleanupBlackBuffer(); return false; } m_blackBuffer->image()->fill(Qt::black); } - // TODO: Do this atomically return setModeLegacy(m_blackBuffer); } +// TODO: We also need a function to deassign a plane. +bool DrmOutput::assignClientPlane(KWayland::Server::BufferInterface *buffer) +{ + if (m_pageFlipPending) { + qCWarning(KWIN_DRM) << "Page not yet flipped."; + return false; + } + + for (DrmPlane *p : m_backend->overlayPlanes()) { + if (p->next()) { + // already assigned + continue; + } + if (!p->isCrtcSupported(m_crtc->resIndex()) ) { + continue; + } + + // TODO: use init function to test if gbm_bo_import was successful, otherwise use render! + DrmImportBuffer *b = new DrmImportBuffer(m_backend, buffer); + p->setOutput(this); + p->setNext(b); + + + //TODO: Query position values for overlays. + int x = 0; + int y = 0; + int width = b->size().width(); + int height = b->size().height(); + + p->setValue(int(DrmPlane::PropertyIndex::SrcX), x << 16); + p->setValue(int(DrmPlane::PropertyIndex::SrcY), y << 16); + p->setValue(int(DrmPlane::PropertyIndex::SrcW), width << 16); + p->setValue(int(DrmPlane::PropertyIndex::SrcH), height << 16); + p->setValue(int(DrmPlane::PropertyIndex::CrtcX), x); + p->setValue(int(DrmPlane::PropertyIndex::CrtcY), y); + p->setValue(int(DrmPlane::PropertyIndex::CrtcW), width); + p->setValue(int(DrmPlane::PropertyIndex::CrtcH), height); + p->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); + + m_nextPlanesFlipList << p; + return true; + } + return false; +} + bool DrmOutput::present(DrmBuffer *buffer) { if (!buffer || buffer->bufferId() == 0) { @@ -730,110 +842,58 @@ } } -bool DrmOutput::presentAtomically(DrmBuffer *buffer) +bool DrmOutput::dpmsOffAtomically() { - if (!LogindIntegration::self()->isActiveSession()) { - qCWarning(KWIN_DRM) << "Logind session not active."; + // TODO: With multiple planes: deactivate all of them here + 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_dpmsMode != DpmsMode::On) { - qCWarning(KWIN_DRM) << "No present() while screen off."; + if (!doAtomicCommit(AtomicCommitMode::Real)) { + qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting."; return false; } - if (m_primaryPlane->next()) { - qCWarning(KWIN_DRM) << "Page not yet flipped."; - return false; - } - - DrmObject::AtomicReturn ret; - uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; + m_nextPlanesFlipList.clear(); + dpmsOffHandler(); - // 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_nextBuffer) { @@ -848,65 +908,157 @@ } // Do we need to set a new mode first? - if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){ - if (!setModeLegacy(buffer)) + if (!m_currentBuffer || m_currentBuffer->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; - if (ok) { + if (drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0) { m_nextBuffer = buffer; + return true; } else { errno_save = errno; qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); - delete buffer; + if (m_backend->deleteBufferAfterPageFlip()) { + delete buffer; + } + return false; } - return ok; } 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"; return false; } } -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 + } + 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_primaryPlane->current() || m_dpmsModePending != m_dpmsMode) { + 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); + drmModeAtomicFree(req); + 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_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::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 { + delete m_primaryPlane->current(); + delete m_primaryPlane->next(); + m_primaryPlane->setCurrent(nullptr); + m_primaryPlane->setNext(nullptr); + + 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; } } diff --git a/plugins/platforms/drm/egl_gbm_backend.h b/plugins/platforms/drm/egl_gbm_backend.h --- a/plugins/platforms/drm/egl_gbm_backend.h +++ b/plugins/platforms/drm/egl_gbm_backend.h @@ -24,6 +24,14 @@ struct gbm_surface; +namespace KWayland +{ +namespace Server +{ +class BufferInterface; +} +} + namespace KWin { class DrmBackend; @@ -43,7 +51,8 @@ SceneOpenGL::TexturePrivate *createBackendTexture(SceneOpenGL::Texture *texture) override; QRegion prepareRenderingFrame() override; void endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override; - void endRenderingFrameForScreen(int screenId, const QRegion &damage, const QRegion &damagedRegion) override; + void endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion) override; + bool directScanoutForScreen(int screenId, KWayland::Server::BufferInterface *buffer) override; bool usesOverlayWindow() const override; bool perScreenRendering() const override; QRegion prepareRenderingForScreen(int screenId) override; 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 @@ -27,6 +27,8 @@ #include "screens.h" // kwin libs #include +// kwayland +#include // Qt #include // system @@ -43,6 +45,7 @@ // Egl is always direct rendering setIsDirectRendering(true); setSyncsToVBlank(true); + setSupportsDirectFullScreenScanout(true); connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::createOutput); connect(m_backend, &DrmBackend::outputRemoved, this, [this] (DrmOutput *output) { @@ -239,7 +242,6 @@ if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); } - } void EglGbmBackend::screenGeometryChanged(const QSize &size) @@ -324,6 +326,32 @@ } } +bool EglGbmBackend::directScanoutForScreen(int screenId, KWayland::Server::BufferInterface *buffer) +{ + Output &o = m_outputs[screenId]; + + if (!m_backend->directScanoutChanged(o.output, buffer)) { + return true; + } + o.buffer = m_backend->createDirectScanoutBuffer(buffer); + + if (o.buffer->size() != o.output->size()) { + delete o.buffer; + return false; + } + if (!m_backend->present(o.buffer, o.output)) { + return false; + } + + if (screenId == 0) { + for (auto &o : m_outputs) { + o.bufferAge = 0; + } + } + + return true; +} + bool EglGbmBackend::usesOverlayWindow() const { return false; 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; }; diff --git a/scene.h b/scene.h --- a/scene.h +++ b/scene.h @@ -223,12 +223,12 @@ // time since last repaint int time_diff; QElapsedTimer last_time; + // windows in their stacking order + QVector< Window* > stacking_order; private: void paintWindowThumbnails(Scene::Window *w, QRegion region, qreal opacity, qreal brightness, qreal saturation); void paintDesktopThumbnails(Scene::Window *w); QHash< Toplevel*, Window* > m_windows; - // windows in their stacking order - QVector< Window* > stacking_order; }; // The base class for windows representations in composite backends @@ -288,9 +288,6 @@ Shadow* shadow(); void referencePreviousPixmap(); void unreferencePreviousPixmap(); -protected: - WindowQuadList makeQuads(WindowQuadType type, const QRegion& reg, const QPoint &textureOffset = QPoint(0, 0)) const; - WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion) const; /** * @brief Returns the WindowPixmap for this Window. * @@ -307,6 +304,9 @@ * @return The WindowPixmap casted to T* or @c NULL if there is no valid window pixmap. */ template T *windowPixmap(); +protected: + WindowQuadList makeQuads(WindowQuadType type, const QRegion& reg, const QPoint &textureOffset = QPoint(0, 0)) const; + WindowQuadList makeDecorationQuads(const QRect *rects, const QRegion ®ion) const; template T *previousWindowPixmap(); /** * @brief Factory method to create a WindowPixmap. @@ -429,6 +429,12 @@ **/ KWayland::Server::SurfaceInterface *surface() const; + /** + * Should be called by the implementing subclasses when the Wayland Buffer changed and needs + * updating. + **/ + virtual void updateBuffer(); + protected: explicit WindowPixmap(Scene::Window *window); explicit WindowPixmap(const QPointer &subSurface, WindowPixmap *parent); @@ -439,12 +445,6 @@ Scene::Window *window(); /** - * Should be called by the implementing subclasses when the Wayland Buffer changed and needs - * updating. - **/ - virtual void updateBuffer(); - - /** * Sets the sub-surface tree to @p children. **/ void setChildren(const QVector &children) { diff --git a/scene_opengl.h b/scene_opengl.h --- a/scene_opengl.h +++ b/scene_opengl.h @@ -50,6 +50,7 @@ virtual bool initFailed() const; virtual bool hasPendingFlush() const; virtual qint64 paint(QRegion damage, ToplevelList windows); + bool directScanout(int screen); virtual Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame); virtual Shadow *createShadow(Toplevel *toplevel); virtual void screenGeometryChanged(const QSize &size); @@ -409,6 +410,7 @@ **/ virtual void endRenderingFrame(const QRegion &damage, const QRegion &damagedRegion) = 0; virtual void endRenderingFrameForScreen(int screenId, const QRegion &damage, const QRegion &damagedRegion); + virtual bool directScanoutForScreen(int screenId, KWayland::Server::BufferInterface *buffer); virtual bool makeCurrent() = 0; virtual void doneCurrent() = 0; virtual bool usesOverlayWindow() const = 0; @@ -482,6 +484,10 @@ return m_directRendering; } + bool supportsDirectFullScreenScanout() const { + return m_supportsDirectFullScreenScanout; + } + bool supportsBufferAge() const { return m_haveBufferAge; } @@ -570,6 +576,10 @@ m_haveBufferAge = value; } + void setSupportsDirectFullScreenScanout(bool value) { + m_supportsDirectFullScreenScanout = value; + } + /** * @return const QRegion& Damage of previously rendered frame **/ @@ -620,6 +630,10 @@ **/ bool m_directRendering; /** + * @brief Whether the backend supports aborting further repaint steps on fullscreen windows. + **/ + bool m_supportsDirectFullScreenScanout = false; + /** * @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age. */ bool m_haveBufferAge; diff --git a/scene_opengl.cpp b/scene_opengl.cpp --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include @@ -375,6 +376,13 @@ Q_UNUSED(damagedRegion) } +bool OpenGLBackend::directScanoutForScreen(int screenId, KWayland::Server::BufferInterface *buffer) +{ + Q_UNUSED(screenId) + Q_UNUSED(buffer) + return false; +} + bool OpenGLBackend::perScreenRendering() const { return false; @@ -689,15 +697,23 @@ // by prepareRenderingFrame(). validRegion is the region that has been // repainted, and may be larger than updateRegion. QRegion updateRegion, validRegion; + if (m_backend->perScreenRendering()) { // trigger start render timer m_backend->prepareRenderingFrame(); + for (int i = 0; i < screens()->count(); ++i) { + if (m_backend->supportsDirectFullScreenScanout() && directScanout(i)) { + continue; + } + const QRect &geo = screens()->geometry(i); QRegion update; QRegion valid; + // prepare rendering makes context current on the output QRegion repaint = m_backend->prepareRenderingForScreen(i); + GLVertexBuffer::setVirtualScreenGeometry(geo); GLRenderTarget::setVirtualScreenGeometry(geo); @@ -770,6 +786,30 @@ return m_backend->renderTime(); } +bool SceneOpenGL::directScanout(int screen) +{ + for (int j = stacking_order.count() - 1; 0 < j; --j) { + Scene::Window *w = stacking_order[j]; + AbstractClient *c = dynamic_cast(w->window()); + if (!c || c->isOnScreen(screen)) { + // check if opaque full screen windows + + // buffer exists and is NOT shm + + // no effects active + if (c && c->isFullScreen() && c->opacity() == 1.0 && !c->hasAlpha() && + c->surface() && c->surface()->buffer() && !c->surface()->buffer()->shmBuffer() && + !static_cast(effects)->activeEffects().count()) { // TODO: We need to test effects individually (some effects can be active all the time) + // also we lose the first frame of an animation because startPaint is done in paintScreen + // put wayland buffer on scanout plane + OpenGLWindowPixmap *wp = w->windowPixmap(); + wp->updateBuffer(); + return m_backend->directScanoutForScreen(screen, wp->buffer().data()); + } + return false; + } + } + return false; +} + QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const { QMatrix4x4 matrix;