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
This commit is contained in:
parent
4c0e33a94c
commit
997cf97c9f
15 changed files with 1243 additions and 85 deletions
|
@ -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
|
||||
|
|
|
@ -19,6 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#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<drmModePlaneRes, &drmModeFreePlaneResources> 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
|
||||
|
|
|
@ -54,6 +54,7 @@ class Udev;
|
|||
class UdevMonitor;
|
||||
|
||||
class DrmOutput;
|
||||
class DrmPlane;
|
||||
|
||||
|
||||
class KWIN_EXPORT DrmBackend : public Platform
|
||||
|
@ -85,11 +86,19 @@ public:
|
|||
QVector<DrmBuffer*> buffers() const {
|
||||
return m_buffers;
|
||||
}
|
||||
QVector<DrmPlane*> 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<DrmOutput*> 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<DrmBuffer*> m_buffers;
|
||||
// all available planes: primarys, cursors and overlays
|
||||
QVector<DrmPlane*> m_planes;
|
||||
QScopedPointer<DpmsInputEventFilter> m_dpmsFilter;
|
||||
KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr;
|
||||
gbm_device *m_gbmDevice = nullptr;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
145
plugins/platforms/drm/drm_object.cpp
Normal file
145
plugins/platforms/drm/drm_object.cpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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<QByteArray> 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<QByteArray> 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
137
plugins/platforms/drm/drm_object.h
Normal file
137
plugins/platforms/drm/drm_object.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_DRM_OBJECT_H
|
||||
#define KWIN_DRM_OBJECT_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QByteArray>
|
||||
|
||||
// drm
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
|
||||
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<QByteArray> m_propsNames; // for comparision with received name of DRM object
|
||||
class Property;
|
||||
QVector<Property*> 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<QByteArray> enumNames = QVector<QByteArray>(0));
|
||||
|
||||
class Property
|
||||
{
|
||||
public:
|
||||
Property(drmModePropertyRes *prop, uint64_t val, QVector<QByteArray> 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<uint64_t> m_enumMap;
|
||||
QVector<QByteArray> m_enumNames;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
64
plugins/platforms/drm/drm_object_connector.cpp
Normal file
64
plugins/platforms/drm/drm_object_connector.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
48
plugins/platforms/drm/drm_object_connector.h
Normal file
48
plugins/platforms/drm/drm_object_connector.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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
|
||||
|
64
plugins/platforms/drm/drm_object_crtc.cpp
Normal file
64
plugins/platforms/drm/drm_object_crtc.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
51
plugins/platforms/drm/drm_object_crtc.h
Normal file
51
plugins/platforms/drm/drm_object_crtc.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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
|
||||
|
174
plugins/platforms/drm/drm_object_plane.cpp
Normal file
174
plugins/platforms/drm/drm_object_plane.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#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<QByteArray> 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;
|
||||
}
|
||||
|
||||
}
|
106
plugins/platforms/drm/drm_object_plane.h
Normal file
106
plugins/platforms/drm/drm_object_plane.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2016 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_DRM_OBJECT_PLANE_H
|
||||
#define KWIN_DRM_OBJECT_PLANE_H
|
||||
|
||||
#include "drm_object.h"
|
||||
// drm
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
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<uint32_t> 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<uint32_t> 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
|
||||
|
|
@ -19,6 +19,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#include "drm_output.h"
|
||||
#include "drm_backend.h"
|
||||
#include "drm_object_plane.h"
|
||||
#include "drm_object_crtc.h"
|
||||
#include "drm_object_connector.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#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<uint8_t*>(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,11 +21,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define KWIN_DRM_OUTPUT_H
|
||||
|
||||
#include "drm_pointer.h"
|
||||
#include "drm_object.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QPointer>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
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<DrmPlane*> m_planesFlipList;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue