From 997cf97c9f700c02bcf51afc279e87c83209ada4 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Wed, 31 Aug 2016 14:00:31 +0200 Subject: [PATCH] Atomic Mode Setting / Universal Plane preliminary support This is Milestone 1 of full support of Atomic Mode Setting (AMS) and Universal Planes in the KWin DRM backend. With Milestone 1 we can use the primary plane of a DRM output and do an AMS commit (this means mode setting aswell as page flipping), if the driver supports it. Until now the functionality is only tested on Intel graphics. You need the drm-next kernel for most recent DRM kernel developments. As boot option set "i915.nuclear_pageflip". Additionally at the moment AMS is still hidden behind the environment variable KWIN_DRM_AMS. Set it, if you want to try out AMS. What needs to be done next: Make it possible to transfer EGL buffers directly to planes and implement logic for deciding about using a plane or not for a specific buffer. You can read more about it on LWN: https://lwn.net/Articles/653071 And on Martin's blog: https://blog.martin-graesslin.com/blog/2015/08/layered-compositing/ I used as model previous work by Daniel Stone for Weston: https://git.collabora.com/cgit/user/daniels/weston.git Reviewed-by: mgraesslin Tags: #kwin Differential Revision: https://phabricator.kde.org/D2370 --- plugins/platforms/drm/CMakeLists.txt | 4 + plugins/platforms/drm/drm_backend.cpp | 72 ++- plugins/platforms/drm/drm_backend.h | 13 +- plugins/platforms/drm/drm_buffer.h | 5 + plugins/platforms/drm/drm_object.cpp | 145 ++++++ plugins/platforms/drm/drm_object.h | 137 ++++++ .../platforms/drm/drm_object_connector.cpp | 64 +++ plugins/platforms/drm/drm_object_connector.h | 48 ++ plugins/platforms/drm/drm_object_crtc.cpp | 64 +++ plugins/platforms/drm/drm_object_crtc.h | 51 +++ plugins/platforms/drm/drm_object_plane.cpp | 174 ++++++++ plugins/platforms/drm/drm_object_plane.h | 106 +++++ plugins/platforms/drm/drm_output.cpp | 415 ++++++++++++++---- plugins/platforms/drm/drm_output.h | 28 +- plugins/platforms/drm/egl_gbm_backend.cpp | 2 - 15 files changed, 1243 insertions(+), 85 deletions(-) create mode 100644 plugins/platforms/drm/drm_object.cpp create mode 100644 plugins/platforms/drm/drm_object.h create mode 100644 plugins/platforms/drm/drm_object_connector.cpp create mode 100644 plugins/platforms/drm/drm_object_connector.h create mode 100644 plugins/platforms/drm/drm_object_crtc.cpp create mode 100644 plugins/platforms/drm/drm_object_crtc.h create mode 100644 plugins/platforms/drm/drm_object_plane.cpp create mode 100644 plugins/platforms/drm/drm_object_plane.h diff --git a/plugins/platforms/drm/CMakeLists.txt b/plugins/platforms/drm/CMakeLists.txt index 3f6a99a359..4f3a3ab3cf 100644 --- a/plugins/platforms/drm/CMakeLists.txt +++ b/plugins/platforms/drm/CMakeLists.txt @@ -1,5 +1,9 @@ set(DRM_SOURCES drm_backend.cpp + drm_object.cpp + drm_object_connector.cpp + drm_object_crtc.cpp + drm_object_plane.cpp drm_output.cpp drm_buffer.cpp drm_inputeventfilter.cpp diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp index 1d840d2dcc..dc2b796965 100644 --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -19,6 +19,9 @@ along with this program. If not, see . *********************************************************************/ #include "drm_backend.h" #include "drm_output.h" +#include "drm_object_connector.h" +#include "drm_object_crtc.h" +#include "drm_object_plane.h" #include "composite.h" #include "cursor.h" #include "logging.h" @@ -84,6 +87,7 @@ DrmBackend::~DrmBackend() while (m_pageFlipsPending != 0) { QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); } + qDeleteAll(m_planes); qDeleteAll(m_outputs); delete m_cursor[0]; delete m_cursor[1]; @@ -242,6 +246,46 @@ void DrmBackend::openDrm() } ); m_drmId = device->sysNum(); + + // trying to activate Atomic Mode Setting (this means also Universal Planes) + if (qEnvironmentVariableIsSet("KWIN_DRM_AMS")) { + if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) { + qCDebug(KWIN_DRM) << "Using Atomic Mode Setting."; + m_atomicModeSetting = true; + + ScopedDrmPointer planeResources(drmModeGetPlaneResources(m_fd)); + if (!planeResources) { + qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode"; + m_atomicModeSetting = false; + } + + if (m_atomicModeSetting) { + qCDebug(KWIN_DRM) << "Number of planes:" << planeResources->count_planes; + + // create the plane objects + for (unsigned int i = 0; i < planeResources->count_planes; ++i) { + drmModePlane *kplane = drmModeGetPlane(m_fd, planeResources->planes[i]); + DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd); + + if (p->init()) { + p->setPossibleCrtcs(kplane->possible_crtcs); + p->setFormats(kplane->formats, kplane->count_formats); + m_planes << p; + } else { + delete p; + } + } + + if (m_planes.isEmpty()) { + qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode"; + m_atomicModeSetting = false; + } + } + } else { + qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode."; + } + } + queryResources(); if (m_outputs.isEmpty()) { qCWarning(KWIN_DRM) << "No outputs, cannot render, will terminate now"; @@ -320,12 +364,37 @@ void DrmBackend::queryResources() DrmOutput *drmOutput = new DrmOutput(this); connect(drmOutput, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); drmOutput->m_crtcId = crtcId; + drmOutput->m_connector = connector->connector_id; + + if (m_atomicModeSetting) { + drmOutput->m_crtc = new DrmCrtc(crtcId, m_fd); + if (drmOutput->m_crtc->init()) { + drmOutput->m_crtc->setOutput(drmOutput); + } else { + qCWarning(KWIN_DRM) << "Crtc object failed, skipping output on connector" << connector->connector_id; + delete drmOutput->m_crtc; + delete drmOutput; + continue; + } + + drmOutput->m_conn = new DrmConnector(connector->connector_id, m_fd); + if (drmOutput->m_conn->init()) { + drmOutput->m_conn->setOutput(drmOutput); + } else { + qCWarning(KWIN_DRM) << "Connector object failed, skipping output on connector" << connector->connector_id; + delete drmOutput->m_conn; + delete drmOutput; + continue; + } + } + if (crtc->mode_valid) { drmOutput->m_mode = crtc->mode; } else { drmOutput->m_mode = connector->modes[0]; } - drmOutput->m_connector = connector->connector_id; + 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; @@ -625,6 +694,7 @@ DrmBuffer *DrmBackend::createBuffer(gbm_surface *surface) { #if HAVE_GBM DrmBuffer *b = new DrmBuffer(this, surface); + b->m_deleteAfterPageFlip = true; m_buffers << b; return b; #else diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h index 0a731691eb..7aec98a54f 100644 --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -54,6 +54,7 @@ class Udev; class UdevMonitor; class DrmOutput; +class DrmPlane; class KWIN_EXPORT DrmBackend : public Platform @@ -85,11 +86,19 @@ public: QVector buffers() const { return m_buffers; } + QVector planes() const { + return m_planes; + } void bufferDestroyed(DrmBuffer *b); void outputWentOff(); void checkOutputsAreOn(); + // returns use of AMS, default is not/legacy + bool atomicModeSetting() const { + return m_atomicModeSetting; + } + void setGbmDevice(gbm_device *device) { m_gbmDevice = device; } @@ -100,7 +109,6 @@ public: public Q_SLOTS: void turnOutputsOn(); - Q_SIGNALS: void outputRemoved(KWin::DrmOutput *output); void outputAdded(KWin::DrmOutput *output); @@ -130,11 +138,14 @@ private: int m_drmId = 0; QVector m_outputs; DrmBuffer *m_cursor[2]; + 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; KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr; gbm_device *m_gbmDevice = nullptr; diff --git a/plugins/platforms/drm/drm_buffer.h b/plugins/platforms/drm/drm_buffer.h index 47db4f8d6e..00b513b27f 100644 --- a/plugins/platforms/drm/drm_buffer.h +++ b/plugins/platforms/drm/drm_buffer.h @@ -58,6 +58,10 @@ public: bool isGbm() const { return m_bo != nullptr; } + bool deleteAfterPageFlip() const { + return m_deleteAfterPageFlip; + } + void releaseGbm(); private: @@ -74,6 +78,7 @@ private: quint64 m_bufferSize = 0; void *m_memory = nullptr; QImage *m_image = nullptr; + bool m_deleteAfterPageFlip = false; }; } diff --git a/plugins/platforms/drm/drm_object.cpp b/plugins/platforms/drm/drm_object.cpp new file mode 100644 index 0000000000..d3a438d29c --- /dev/null +++ b/plugins/platforms/drm/drm_object.cpp @@ -0,0 +1,145 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_object.h" +#include "logging.h" + +namespace KWin +{ + +/* + * Defintions for class DrmObject + */ + +DrmObject::DrmObject(uint32_t object_id, int fd) + : m_fd(fd) + , m_id(object_id) +{ +} + +DrmObject::~DrmObject() +{ + foreach(Property* p, m_props) + delete p; +} + +void DrmObject::initProp(int n, drmModeObjectProperties *properties, QVector enumNames) +{ + m_props.resize(m_propsNames.size()); + for (unsigned int i = 0; i < properties->count_props; ++i) { + drmModePropertyRes *prop = drmModeGetProperty(m_fd, properties->props[i]); + if (!prop) { + continue; + } + if (prop->name == m_propsNames[n]) { + qCDebug(KWIN_DRM).nospace() << m_id << ": " << prop->name << "' (id " << prop->prop_id + << "): " << properties->prop_values[i]; + m_props[n] = new Property(prop, properties->prop_values[i], enumNames); + } + drmModeFreeProperty(prop); + } +} + +void DrmObject::setPropValue(int index, uint64_t new_value) +{ + Q_ASSERT(index < m_props.size()); + m_props[index]->setValue(new_value); + return; +} + +bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value) +{ + 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; + } + if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) < 0) { + // error when adding property + return false; + } + m_propsPending |= mask; + m_propsValid &= ~mask; + // adding property was successful + return true; +} + +/* + * Defintions for struct Prop + */ + +DrmObject::Property::Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames) + : m_propId(prop->prop_id) + , m_propName(prop->name) + , m_value(val) +{ + if (!enumNames.isEmpty()) { + qCDebug(KWIN_DRM) << m_propName << " has enums:" << enumNames; + m_enumNames = enumNames; + initEnumMap(prop); + } +} + +DrmObject::Property::~Property() = default; + +void DrmObject::Property::initEnumMap(drmModePropertyRes *prop) +{ + if (!(prop->flags & DRM_MODE_PROP_ENUM) || prop->count_enums < 1) { + qCWarning(KWIN_DRM) << "Property '" << prop->name << "' ( id =" + << m_propId << ") should be enum valued, but it is not."; + return; + } + + int nameCount = m_enumNames.size(); + m_enumMap.resize(nameCount); + + qCDebug(KWIN_DRM).nospace() << "Test all " << prop->count_enums << + " possible enums" <<":"; + + for (int i = 0; i < prop->count_enums; i++) { + + struct drm_mode_property_enum *en = &prop->enums[i]; + int j = 0; + + while (QByteArray(en->name) != m_enumNames[j]) { + j++; + if (j == nameCount) { + break; + } + } + + if (j == nameCount) { + qCWarning(KWIN_DRM).nospace() << m_propName << " has unrecognized enum '" << en->name << "'"; + } else { + qCDebug(KWIN_DRM).nospace() << "Enum '" + << en->name << "': runtime-value = " << en->value; + m_enumMap[j] = en->value; + } + } + + if (KWIN_DRM().isDebugEnabled()) { + for (int i = 0; i < m_enumMap.size(); i++) { + if (m_value == m_enumMap[i]) { + qCDebug(KWIN_DRM) << "=>" << m_propName << "with mapped enum value" << m_enumNames[i]; + } + } + } +} + +} diff --git a/plugins/platforms/drm/drm_object.h b/plugins/platforms/drm/drm_object.h new file mode 100644 index 0000000000..168c9c092d --- /dev/null +++ b/plugins/platforms/drm/drm_object.h @@ -0,0 +1,137 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_OBJECT_H +#define KWIN_DRM_OBJECT_H + +#include +#include + +// drm +#include + + +namespace KWin +{ + +class DrmOutput; + +class DrmObject +{ +public: + // creates drm object by its id delivered by the kernel + DrmObject(uint32_t object_id, int fd); + + virtual ~DrmObject() = 0; + + enum class AtomicReturn { + NoChange, + Success, + Error + }; + + virtual bool init() = 0; + + uint32_t id() const { + return m_id; + } + + DrmOutput* output() const { + return m_output; + } + void setOutput(DrmOutput* output) { + m_output = output; + } + + uint32_t propId(int index) { + return m_props[index]->propId(); + } + uint64_t propValue(int index) { + return m_props[index]->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; + } + + bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value); + +protected: + 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 + 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)); + + class Property + { + public: + Property(drmModePropertyRes *prop, uint64_t val, QVector enumNames); + virtual ~Property(); + + void initEnumMap(drmModePropertyRes *prop); + + uint64_t enumMap(int n) { + return m_enumMap[n]; // TODO: test on index out of bounds? + } + + uint32_t propId() { + return m_propId; + } + uint32_t value() { + return m_value; + } + void setValue(uint64_t new_value) { + m_value = new_value; + } + + private: + uint32_t m_propId = 0; + QByteArray m_propName; + + uint64_t m_value = 0; + QVector m_enumMap; + QVector m_enumNames; + }; +}; + + +} + +#endif + diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp new file mode 100644 index 0000000000..67a695797f --- /dev/null +++ b/plugins/platforms/drm/drm_object_connector.cpp @@ -0,0 +1,64 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_object_connector.h" +#include "logging.h" + +namespace KWin +{ + +DrmConnector::DrmConnector(uint32_t connector_id, int fd) + : DrmObject(connector_id, fd) +{ + +} + +DrmConnector::~DrmConnector() = default; + +bool DrmConnector::init() +{ + qCDebug(KWIN_DRM) << "Creating connector" << m_id; + + if (!initProps()) { + return false; + } + return true; +} + +bool DrmConnector::initProps() +{ + m_propsNames = { + QByteArrayLiteral("CRTC_ID"), + }; + + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CONNECTOR); + if (!properties) { + qCWarning(KWIN_DRM) << "Failed to get properties for connector " << m_id ; + return false; + } + + int propCount = int(PropertyIndex::Count); + for (int j = 0; j < propCount; ++j) { + initProp(j, properties); + } + drmModeFreeObjectProperties(properties); + return true; +} + +} diff --git a/plugins/platforms/drm/drm_object_connector.h b/plugins/platforms/drm/drm_object_connector.h new file mode 100644 index 0000000000..dcaf200c0d --- /dev/null +++ b/plugins/platforms/drm/drm_object_connector.h @@ -0,0 +1,48 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_OBJECT_CONNECTOR_H +#define KWIN_DRM_OBJECT_CONNECTOR_H + +#include "drm_object.h" + +namespace KWin +{ + +class DrmConnector : public DrmObject +{ +public: + DrmConnector(uint32_t connector_id, int fd); + + virtual ~DrmConnector(); + + bool init(); + + enum class PropertyIndex { + CrtcId = 0, + Count + }; + + bool initProps(); +}; + +} + +#endif + diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp new file mode 100644 index 0000000000..1d5f03c8f7 --- /dev/null +++ b/plugins/platforms/drm/drm_object_crtc.cpp @@ -0,0 +1,64 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_object_crtc.h" +#include "logging.h" + +namespace KWin +{ + +DrmCrtc::DrmCrtc(uint32_t crtc_id, int fd) + : DrmObject(crtc_id, fd) +{ +} + +DrmCrtc::~DrmCrtc() = default; + +bool DrmCrtc::init() +{ + qCDebug(KWIN_DRM) << "Creating CRTC" << m_id; + + if (!initProps()) { + return false; + } + return true; +} + +bool DrmCrtc::initProps() +{ + m_propsNames = { + QByteArrayLiteral("MODE_ID"), + QByteArrayLiteral("ACTIVE"), + }; + + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_CRTC); + if (!properties) { + qCWarning(KWIN_DRM) << "Failed to get properties for crtc " << m_id ; + return false; + } + + int propCount = int(PropertyIndex::Count); + for (int j = 0; j < propCount; ++j) { + initProp(j, properties); + } + drmModeFreeObjectProperties(properties); + return true; +} + +} diff --git a/plugins/platforms/drm/drm_object_crtc.h b/plugins/platforms/drm/drm_object_crtc.h new file mode 100644 index 0000000000..cc9b5c94e1 --- /dev/null +++ b/plugins/platforms/drm/drm_object_crtc.h @@ -0,0 +1,51 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_OBJECT_CRTC_H +#define KWIN_DRM_OBJECT_CRTC_H + +#include "drm_object.h" + +namespace KWin +{ + +class DrmBuffer; + +class DrmCrtc : public DrmObject +{ +public: + DrmCrtc(uint32_t crtc_id, int fd); + + virtual ~DrmCrtc(); + + bool init(); + + enum class PropertyIndex { + ModeId = 0, + Active, + Count + }; + + bool initProps(); +}; + +} + +#endif + diff --git a/plugins/platforms/drm/drm_object_plane.cpp b/plugins/platforms/drm/drm_object_plane.cpp new file mode 100644 index 0000000000..85b4b44470 --- /dev/null +++ b/plugins/platforms/drm/drm_object_plane.cpp @@ -0,0 +1,174 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_object_plane.h" +#include "drm_buffer.h" +#include "drm_pointer.h" +#include "logging.h" + +namespace KWin +{ + +DrmPlane::DrmPlane(uint32_t plane_id, int fd) + : DrmObject(plane_id, fd) +{ +} + +DrmPlane::~DrmPlane() = default; + +bool DrmPlane::init() +{ + qCDebug(KWIN_DRM) << "Initialize plane" << m_id; + ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_fd, m_id)); + + if (!p) { + qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id; + return false; + } + + m_possibleCrtcs = p->possible_crtcs; + + m_formats.resize(p->count_formats); + for (int i = 0; i < p->count_formats; i++) { + m_formats[i] = p->formats[i]; + } + + if (!initProps()) { + return false; + } + return true; +} + +bool DrmPlane::initProps() +{ + m_propsNames = { + QByteArrayLiteral("type"), + QByteArrayLiteral("SRC_X"), + QByteArrayLiteral("SRC_Y"), + QByteArrayLiteral("SRC_W"), + QByteArrayLiteral("SRC_H"), + QByteArrayLiteral("CRTC_X"), + QByteArrayLiteral("CRTC_Y"), + QByteArrayLiteral("CRTC_W"), + QByteArrayLiteral("CRTC_H"), + QByteArrayLiteral("FB_ID"), + QByteArrayLiteral("CRTC_ID"), + }; + + QVector typeNames = { + QByteArrayLiteral("Primary"), + QByteArrayLiteral("Cursor"), + QByteArrayLiteral("Overlay"), + }; + + drmModeObjectProperties *properties = drmModeObjectGetProperties(m_fd, m_id, DRM_MODE_OBJECT_PLANE); + if (!properties){ + qCWarning(KWIN_DRM) << "Failed to get properties for plane " << m_id ; + return false; + } + + int propCount = int(PropertyIndex::Count); + for (int j = 0; j < propCount; ++j) { + if (j == int(PropertyIndex::Type)) { + initProp(j, properties, typeNames); + } else { + initProp(j, properties); + } + } + + drmModeFreeObjectProperties(properties); + return true; +} + +DrmPlane::TypeIndex DrmPlane::type() +{ + uint64_t value = propValue(int(PropertyIndex::Type)); + int typeCount = int(TypeIndex::Count); + for (int i = 0; i < typeCount; i++) { + if (m_props[int(PropertyIndex::Type)]->enumMap(i) == value) { + 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; + } +} + +DrmObject::AtomicReturn DrmPlane::atomicReqPlanePopulate(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); + } + + 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 DrmObject::AtomicReturn::Success; +} + +} diff --git a/plugins/platforms/drm/drm_object_plane.h b/plugins/platforms/drm/drm_object_plane.h new file mode 100644 index 0000000000..bef27ce9fc --- /dev/null +++ b/plugins/platforms/drm/drm_object_plane.h @@ -0,0 +1,106 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Roman Gilg + +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_OBJECT_PLANE_H +#define KWIN_DRM_OBJECT_PLANE_H + +#include "drm_object.h" +// drm +#include + +namespace KWin +{ + +class DrmBuffer; + +class DrmPlane : public DrmObject +{ +public: + DrmPlane(uint32_t plane_id, int fd); + + virtual ~DrmPlane(); + + enum class PropertyIndex { + Type = 0, + SrcX, + SrcY, + SrcW, + SrcH, + CrtcX, + CrtcY, + CrtcW, + CrtcH, + FbId, + CrtcId, + Count + }; + + enum class TypeIndex { + Primary = 0, + Cursor, + Overlay, + Count + }; + + bool init(); + bool initProps(); + TypeIndex type(); + bool isCrtcSupported(uint32_t crtc); + DrmObject::AtomicReturn atomicReqPlanePopulate(drmModeAtomicReq *req); + + DrmBuffer *current(){ + return m_current; + } + DrmBuffer *next(){ + return m_next; + } + void setCurrent(DrmBuffer *b){ + m_current = b; + } + void setNext(DrmBuffer *b){ + m_next = 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; + } + +private: + DrmBuffer *m_current = nullptr; + DrmBuffer *m_next = nullptr; + + // TODO: See weston drm_output_check_plane_format for future use of these member variables + QVector m_formats; // Possible formats, which can be presented on this plane + + // TODO: when using overlay planes in the future: restrict possible screens / crtcs of planes + uint32_t m_possibleCrtcs; +}; + +} + +#endif + diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp index 69b1ed6c41..abcdc4dde1 100644 --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -19,6 +19,11 @@ along with this program. If not, see . *********************************************************************/ #include "drm_output.h" #include "drm_backend.h" +#include "drm_object_plane.h" +#include "drm_object_crtc.h" +#include "drm_object_connector.h" + +#include #include "composite.h" #include "logind.h" @@ -58,6 +63,8 @@ DrmOutput::~DrmOutput() { hideCursor(); cleanupBlackBuffer(); + delete m_crtc; + delete m_conn; delete m_waylandOutput.data(); delete m_waylandOutputDevice.data(); } @@ -97,55 +104,6 @@ QRect DrmOutput::geometry() const return QRect(m_globalPos, size()); } -bool DrmOutput::present(DrmBuffer *buffer) -{ - if (!buffer || buffer->bufferId() == 0) { - return false; - } - if (!LogindIntegration::self()->isActiveSession()) { - m_currentBuffer = buffer; - return false; - } - if (m_dpmsMode != DpmsMode::On) { - return false; - } - if (m_currentBuffer) { - return false; - } - if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()) { - // need to set a new mode first - if (!setMode(buffer)) { - return false; - } - } - const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; - if (ok) { - m_currentBuffer = buffer; - } else { - qCWarning(KWIN_DRM) << "Page flip failed"; - buffer->releaseGbm(); - } - return ok; -} - -void DrmOutput::pageFlipped() -{ - if (!m_currentBuffer) { - return; - } - m_currentBuffer->releaseGbm(); - m_currentBuffer = nullptr; - cleanupBlackBuffer(); -} - -void DrmOutput::cleanupBlackBuffer() -{ - if (m_blackBuffer) { - delete m_blackBuffer; - m_blackBuffer = nullptr; - } -} - static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode) { using namespace KWayland::Server; @@ -185,6 +143,11 @@ bool DrmOutput::init(drmModeConnector *connector) initEdid(connector); initDpms(connector); initUuid(); + if (m_backend->atomicModeSetting()) { + if (!initPrimaryPlane()) { + return false; + } + } m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); if (!blank()) { return false; @@ -240,6 +203,10 @@ bool DrmOutput::init(drmModeConnector *connector) // read in mode information for (int i = 0; i < connector->count_modes; ++i) { + + // TODO: in AMS here we could read and store for later every mode's blob_id + // would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set + auto *m = &connector->modes[i]; KWayland::Server::OutputInterface::ModeFlags flags; KWayland::Server::OutputDeviceInterface::ModeFlags deviceflags; @@ -321,31 +288,6 @@ bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const && qstrcmp(mode->name, m_mode.name) == 0; } -bool DrmOutput::blank() -{ - if (!m_blackBuffer) { - m_blackBuffer = m_backend->createBuffer(size()); - if (!m_blackBuffer->map()) { - cleanupBlackBuffer(); - return false; - } - m_blackBuffer->image()->fill(Qt::black); - } - return setMode(m_blackBuffer); -} - -bool DrmOutput::setMode(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(); - return true; - } else { - qCWarning(KWIN_DRM) << "Mode setting failed"; - return false; - } -} - static bool verifyEdidHeader(drmModePropertyBlobPtr edid) { const uint8_t *data = reinterpret_cast(edid->data); @@ -505,6 +447,61 @@ void DrmOutput::initEdid(drmModeConnector *connector) m_edid.physicalSize = extractPhysicalSize(edid.data()); } +bool DrmOutput::initPrimaryPlane() +{ + for (int i = 0; i < m_backend->planes().size(); ++i) { + DrmPlane* p = m_backend->planes()[i]; + if (!p) { + continue; + } + if (p->type() != DrmPlane::TypeIndex::Primary) { + continue; + } + if (p->output()) { // Plane already has an output + continue; + } + if (m_primaryPlane) { // Output already has a primary plane + continue; + } + if (!p->isCrtcSupported(m_crtcId)) { + continue; + } + p->setOutput(this); + m_primaryPlane = p; + qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId; + return true; + } + qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; + return false; +} + +bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) +{ + for (int i = 0; i < m_backend->planes().size(); ++i) { + DrmPlane* p = m_backend->planes()[i]; + if (!p) { + continue; + } + if (p->type() != DrmPlane::TypeIndex::Cursor) { + continue; + } + if (p->output()) { // Plane already has an output + continue; + } + if (m_cursorPlane) { // Output already has a cursor plane + continue; + } + if (!p->isCrtcSupported(m_crtcId)) { + continue; + } + p->setOutput(this); + m_cursorPlane = p; + qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId; + return true; + } + return false; +} + void DrmOutput::initDpms(drmModeConnector *connector) { for (int i = 0; i < connector->count_props; ++i) { @@ -525,12 +522,30 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) return; } if (mode == m_dpmsMode) { + qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; return; } - if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) != 0) { - qCWarning(KWIN_DRM) << "Setting DPMS failed"; - return; + + if (m_backend->atomicModeSetting()) { + drmModeAtomicReq *req = drmModeAtomicAlloc(); + + 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; + } else { + qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId; + } + drmModeAtomicFree(req); + } else { + if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) { + qCWarning(KWIN_DRM) << "Setting DPMS failed"; + return; + } } + m_dpmsMode = mode; if (m_waylandOutput) { m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); @@ -621,5 +636,251 @@ bool DrmOutput::commitChanges() return true; } +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(); +} + +void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) +{ + if (newbuffer->deleteAfterPageFlip()) { + if ( oldbuffer && oldbuffer != newbuffer ) { + delete oldbuffer; + } + } else { + // although oldbuffer's pointer is remapped in pageFlipped(), + // we ignore the pointer completely anywhere else in this case + newbuffer->releaseGbm(); + } +} + +void DrmOutput::cleanupBlackBuffer() +{ + if (m_blackBuffer) { + delete m_blackBuffer; + m_blackBuffer = nullptr; + } +} + +bool DrmOutput::blank() +{ + 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); +} + +bool DrmOutput::present(DrmBuffer *buffer) +{ + if (!buffer || buffer->bufferId() == 0) { + return false; + } + if (m_backend->atomicModeSetting()) { + return presentAtomically(buffer); + } else { + return presentLegacy(buffer); + } +} + +bool DrmOutput::presentAtomically(DrmBuffer *buffer) +{ + if (!LogindIntegration::self()->isActiveSession()) { + qCWarning(KWIN_DRM) << "Logind session not active."; + return false; + } + if (m_dpmsMode != DpmsMode::On) { + qCWarning(KWIN_DRM) << "No present() while screen off."; + 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; + + // 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"; + + 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; + } + } + + 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; + } + } + + // 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(); + 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()); + } + + drmModeAtomicFree(req); + return true; +} + + +bool DrmOutput::presentLegacy(DrmBuffer *buffer) +{ + if (m_nextBuffer) { + return false; + } + if (!LogindIntegration::self()->isActiveSession()) { + m_nextBuffer = 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)) + 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) { + m_nextBuffer = buffer; + } else { + errno_save = errno; + qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); + delete buffer; + } + 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(); + return true; + } else { + qCWarning(KWIN_DRM) << "Mode setting failed"; + return false; + } +} + +DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) +{ + 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); + } 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); + } + + bool ret = true; + + m_crtc->setPropsPending(0); + m_conn->setPropsPending(0); + + 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 (!ret) { + qCWarning(KWIN_DRM) << "Failed to populate atomic modeset"; + return DrmObject::AtomicReturn::Error; + } + if (!m_crtc->propsPending() && !m_conn->propsPending()) { + return DrmObject::AtomicReturn::NoChange; + } + return DrmObject::AtomicReturn::Success; +} } diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index 628bb5da95..aef6b8a9dd 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -21,11 +21,13 @@ along with this program. If not, see . #define KWIN_DRM_OUTPUT_H #include "drm_pointer.h" +#include "drm_object.h" #include #include #include #include +#include #include namespace KWayland @@ -44,6 +46,9 @@ namespace KWin class DrmBackend; class DrmBuffer; +class DrmPlane; +class DrmConnector; +class DrmCrtc; class DrmOutput : public QObject { @@ -59,9 +64,9 @@ public: void showCursor(DrmBuffer *buffer); void hideCursor(); void moveCursor(const QPoint &globalPos); + bool init(drmModeConnector *connector); bool present(DrmBuffer *buffer); void pageFlipped(); - bool init(drmModeConnector *connector); void restoreSaved(); bool blank(); @@ -75,6 +80,7 @@ public: QRect geometry() const; QString name() const; int currentRefreshRate() const; + // These values are defined by the kernel enum class DpmsMode { On = DRM_MODE_DPMS_ON, Standby = DRM_MODE_DPMS_STANDBY, @@ -97,13 +103,20 @@ private: friend class DrmBackend; DrmOutput(DrmBackend *backend); void cleanupBlackBuffer(); - bool setMode(DrmBuffer *buffer); + bool presentAtomically(DrmBuffer *buffer); + 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); + DrmBackend *m_backend; QPoint m_globalPos; quint32 m_crtcId = 0; @@ -112,10 +125,11 @@ private: 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); + drmModeFreeCrtc(ptr); // TODO: Atomically? See compositor-drm.c l.3670 } }; Edid m_edid; @@ -126,8 +140,14 @@ private: 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; + QVector m_planesFlipList; +}; } diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index 9ed4c03cdc..38e130be65 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -234,10 +234,8 @@ void EglGbmBackend::present() void EglGbmBackend::presentOnOutput(EglGbmBackend::Output &o) { eglSwapBuffers(eglDisplay(), o.eglSurface); - auto oldBuffer = o.buffer; o.buffer = m_backend->createBuffer(o.gbmSurface); m_backend->present(o.buffer, o.output); - delete oldBuffer; if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); }