diff --git a/plugins/platforms/drm/drm_object.cpp b/plugins/platforms/drm/drm_object.cpp index 79a473caa..48d297fa9 100644 --- a/plugins/platforms/drm/drm_object.cpp +++ b/plugins/platforms/drm/drm_object.cpp @@ -1,147 +1,156 @@ /******************************************************************** 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 "drm_backend.h" #include "logging.h" namespace KWin { /* * Defintions for class DrmObject */ DrmObject::DrmObject(uint32_t object_id, DrmBackend *backend) : m_backend(backend) , m_id(object_id) { } DrmObject::~DrmObject() { foreach(Property* p, m_props) delete p; } +void DrmObject::setPropertyNames(QVector &&vector) +{ + m_propsNames = std::move(vector); + m_props.fill(nullptr, m_propsNames.size()); +} + 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_backend->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); } } -bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value) +bool DrmObject::atomicAddProperty(drmModeAtomicReq *req, Property *property) { - if (drmModeAtomicAddProperty(req, m_id, m_props[prop]->propId(), value) <= 0) { - qCWarning(KWIN_DRM) << "Adding property" << m_propsNames[prop] << "to atomic commit failed for object" << this; + if (drmModeAtomicAddProperty(req, m_id, property->propId(), property->value()) <= 0) { + qCWarning(KWIN_DRM) << "Adding property" << property->name() << "to atomic commit failed for object" << this; return false; } return true; } bool DrmObject::atomicPopulate(drmModeAtomicReq *req) { bool ret = true; for (int i = 0; i < m_props.size(); i++) { - ret &= atomicAddProperty(req, i, m_props[i]->value()); + auto property = m_props.at(i); + if (!property) { + continue; + } + ret &= atomicAddProperty(req, property); } if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; return false; } 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->flags & DRM_MODE_PROP_BITMASK)) || 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 index 49ab7207f..50ec56f34 100644 --- a/plugins/platforms/drm/drm_object.h +++ b/plugins/platforms/drm/drm_object.h @@ -1,129 +1,132 @@ /******************************************************************** 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 DrmBackend; class DrmOutput; class DrmObject { public: // creates drm object by its id delivered by the kernel DrmObject(uint32_t object_id, DrmBackend *backend); virtual ~DrmObject() = 0; virtual bool atomicInit() = 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 prop) { - return m_props[prop]->propId(); - } - uint64_t value(int prop) { - return m_props[prop]->value(); - } bool propHasEnum(int prop, uint64_t value) const { - return m_props[prop]->hasEnum(value); + auto property = m_props.at(prop); + return property ? property->hasEnum(value) : false; } void setValue(int prop, uint64_t new_value) { Q_ASSERT(prop < m_props.size()); - m_props[prop]->setValue(new_value); + auto property = m_props.at(prop); + if (property) { + property->setValue(new_value); + } } virtual bool atomicPopulate(drmModeAtomicReq *req); protected: virtual bool initProps() = 0; // only derived classes know names and quantity of properties + void setPropertyNames(QVector &&vector); void initProp(int n, drmModeObjectProperties *properties, QVector enumNames = QVector(0)); - bool atomicAddProperty(drmModeAtomicReq *req, int prop, uint64_t value); + class Property; + bool atomicAddProperty(drmModeAtomicReq *req, Property *property); DrmBackend *m_backend; const uint32_t m_id = 0; DrmOutput *m_output = nullptr; // for comparision with received name of DRM object - QVector m_propsNames; - class Property; QVector m_props; 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? } bool hasEnum(uint64_t value) const { return m_enumMap.contains(value); } - uint32_t propId() { + uint32_t propId() const { return m_propId; } - uint64_t value() { + uint64_t value() const { return m_value; } void setValue(uint64_t new_value) { m_value = new_value; } + const QByteArray &name() const { + return m_propName; + } private: uint32_t m_propId = 0; QByteArray m_propName; uint64_t m_value = 0; QVector m_enumMap; QVector m_enumNames; }; + +private: + QVector m_propsNames; }; } #endif diff --git a/plugins/platforms/drm/drm_object_connector.cpp b/plugins/platforms/drm/drm_object_connector.cpp index 444ce4eef..f671f0c3c 100644 --- a/plugins/platforms/drm/drm_object_connector.cpp +++ b/plugins/platforms/drm/drm_object_connector.cpp @@ -1,81 +1,81 @@ /******************************************************************** 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 "drm_backend.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { DrmConnector::DrmConnector(uint32_t connector_id, DrmBackend *backend) : DrmObject(connector_id, backend) { ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(backend->fd(), connector_id)); if (!con) { return; } for (int i = 0; i < con->count_encoders; ++i) { m_encoders << con->encoders[i]; } } DrmConnector::~DrmConnector() = default; bool DrmConnector::atomicInit() { qCDebug(KWIN_DRM) << "Creating connector" << m_id; if (!initProps()) { return false; } return true; } bool DrmConnector::initProps() { - m_propsNames = { + setPropertyNames( { QByteArrayLiteral("CRTC_ID"), - }; + }); drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->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; } bool DrmConnector::isConnected() { ScopedDrmPointer<_drmModeConnector, &drmModeFreeConnector> con(drmModeGetConnector(m_backend->fd(), m_id)); if (!con) { return false; } return con->connection == DRM_MODE_CONNECTED; } } diff --git a/plugins/platforms/drm/drm_object_crtc.cpp b/plugins/platforms/drm/drm_object_crtc.cpp index 20b232ef8..d44ca787b 100644 --- a/plugins/platforms/drm/drm_object_crtc.cpp +++ b/plugins/platforms/drm/drm_object_crtc.cpp @@ -1,106 +1,109 @@ /******************************************************************** 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 "drm_backend.h" #include "drm_output.h" #include "drm_buffer.h" #include "logging.h" namespace KWin { DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex) : DrmObject(crtc_id, backend), m_resIndex(resIndex) { } DrmCrtc::~DrmCrtc() { } bool DrmCrtc::atomicInit() { qCDebug(KWIN_DRM) << "Atomic init for CRTC:" << resIndex() << "id:" << m_id; if (!initProps()) { return false; } return true; } bool DrmCrtc::initProps() { - m_propsNames = { + setPropertyNames({ QByteArrayLiteral("MODE_ID"), QByteArrayLiteral("ACTIVE"), - }; + }); drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->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; } void DrmCrtc::flipBuffer() { if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip() && m_currentBuffer != m_nextBuffer) { delete m_currentBuffer; } m_currentBuffer = m_nextBuffer; m_nextBuffer = nullptr; delete m_blackBuffer; m_blackBuffer = nullptr; } bool DrmCrtc::blank() { + if (!m_output) { + return false; + } if (!m_blackBuffer) { DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize()); if (!blackBuffer->map()) { delete blackBuffer; return false; } blackBuffer->image()->fill(Qt::black); m_blackBuffer = blackBuffer; } if (m_output->setModeLegacy(m_blackBuffer)) { if (m_currentBuffer && m_backend->deleteBufferAfterPageFlip()) { delete m_currentBuffer; delete m_nextBuffer; } m_currentBuffer = nullptr; m_nextBuffer = nullptr; return true; } return false; } } diff --git a/plugins/platforms/drm/drm_object_plane.cpp b/plugins/platforms/drm/drm_object_plane.cpp index 689976970..f9efc6161 100644 --- a/plugins/platforms/drm/drm_object_plane.cpp +++ b/plugins/platforms/drm/drm_object_plane.cpp @@ -1,186 +1,201 @@ /******************************************************************** 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_backend.h" #include "drm_buffer.h" #include "drm_pointer.h" #include "logging.h" namespace KWin { DrmPlane::DrmPlane(uint32_t plane_id, DrmBackend *backend) : DrmObject(plane_id, backend) { } DrmPlane::~DrmPlane() { delete m_current; delete m_next; } bool DrmPlane::atomicInit() { qCDebug(KWIN_DRM) << "Atomic init for plane:" << m_id; ScopedDrmPointer<_drmModePlane, &drmModeFreePlane> p(drmModeGetPlane(m_backend->fd(), m_id)); if (!p) { qCWarning(KWIN_DRM) << "Failed to get kernel plane" << m_id; return false; } m_possibleCrtcs = p->possible_crtcs; int count_formats = p->count_formats; m_formats.resize(count_formats); for (int i = 0; i < count_formats; i++) { m_formats[i] = p->formats[i]; } if (!initProps()) { return false; } return true; } bool DrmPlane::initProps() { - m_propsNames = { + setPropertyNames( { 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"), QByteArrayLiteral("rotation") - }; + }); QVector typeNames = { QByteArrayLiteral("Primary"), QByteArrayLiteral("Cursor"), QByteArrayLiteral("Overlay"), }; const QVector rotationNames{ QByteArrayLiteral("rotate-0"), QByteArrayLiteral("rotate-90"), QByteArrayLiteral("rotate-180"), QByteArrayLiteral("rotate-270"), QByteArrayLiteral("reflect-x"), QByteArrayLiteral("reflect-y") }; drmModeObjectProperties *properties = drmModeObjectGetProperties(m_backend->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 if (j == int(PropertyIndex::Rotation)) { initProp(j, properties, rotationNames); m_supportedTransformations = Transformations(); auto testTransform = [j, this] (uint64_t value, Transformation t) { if (propHasEnum(j, value)) { m_supportedTransformations |= t; } }; testTransform(0, Transformation::Rotate0); testTransform(1, Transformation::Rotate90); testTransform(2, Transformation::Rotate180); testTransform(3, Transformation::Rotate270); testTransform(4, Transformation::ReflectX); testTransform(5, Transformation::ReflectY); qCDebug(KWIN_DRM) << "Supported Transformations: " << m_supportedTransformations << " on plane " << m_id; } else { initProp(j, properties); } } drmModeFreeObjectProperties(properties); return true; } DrmPlane::TypeIndex DrmPlane::type() { - uint64_t v = value(int(PropertyIndex::Type)); + auto property = m_props.at(int(PropertyIndex::Type)); + if (!property) { + return TypeIndex::Overlay; + } int typeCount = int(TypeIndex::Count); for (int i = 0; i < typeCount; i++) { - if (m_props[int(PropertyIndex::Type)]->enumMap(i) == v) { + if (property->enumMap(i) == property->value()) { return TypeIndex(i); } } return TypeIndex::Overlay; } -void DrmPlane::setNext(DrmBuffer *b){ - setValue(int(PropertyIndex::FbId), b ? b->bufferId() : 0); +void DrmPlane::setNext(DrmBuffer *b) +{ + if (auto property = m_props.at(int(PropertyIndex::FbId))) { + property->setValue(b ? b->bufferId() : 0); + } m_next = b; } void DrmPlane::setTransformation(Transformations t) { - setValue(int(PropertyIndex::Rotation), int(t)); + if (auto property = m_props.at(int(PropertyIndex::Rotation))) { + property->setValue(int(t)); + } } DrmPlane::Transformations DrmPlane::transformation() { - return Transformations(int(value(int(PropertyIndex::Rotation)))); + if (auto property = m_props.at(int(PropertyIndex::Rotation))) { + return Transformations(int(property->value())); + } + return Transformations(Transformation::Rotate0); } bool DrmPlane::atomicPopulate(drmModeAtomicReq *req) { bool ret = true; for (int i = 1; i < m_props.size(); i++) { - ret &= atomicAddProperty(req, i, m_props[i]->value()); + auto property = m_props.at(i); + if (!property) { + continue; + } + ret &= atomicAddProperty(req, property); } if (!ret) { qCWarning(KWIN_DRM) << "Failed to populate atomic plane" << m_id; return false; } return true; } void DrmPlane::flipBuffer() { m_current = m_next; m_next = nullptr; } void DrmPlane::flipBufferWithDelete() { if (m_current != m_next) { delete m_current; } flipBuffer(); } } diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp index 51861f310..092cee377 100644 --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -1,378 +1,381 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "egl_gbm_backend.h" // kwin #include "composite.h" #include "drm_backend.h" #include "drm_output.h" #include "gbm_surface.h" #include "logging.h" #include "options.h" #include "screens.h" // kwin libs #include // Qt #include // system #include namespace KWin { EglGbmBackend::EglGbmBackend(DrmBackend *b) : AbstractEglBackend() , m_backend(b) { // Egl is always direct rendering setIsDirectRendering(true); setSyncsToVBlank(true); connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::createOutput); connect(m_backend, &DrmBackend::outputRemoved, this, [this] (DrmOutput *output) { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [output] (const Output &o) { return o.output == output; } ); if (it == m_outputs.end()) { return; } cleanupOutput(*it); m_outputs.erase(it); } ); } EglGbmBackend::~EglGbmBackend() { cleanup(); } void EglGbmBackend::cleanupSurfaces() { for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { cleanupOutput(*it); } m_outputs.clear(); } void EglGbmBackend::cleanupOutput(const Output &o) { o.output->releaseGbm(); if (o.eglSurface != EGL_NO_SURFACE) { eglDestroySurface(eglDisplay(), o.eglSurface); } } bool EglGbmBackend::initializeEgl() { initClientExtensions(); EGLDisplay display = m_backend->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. if (display == EGL_NO_DISPLAY) { if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); return false; } auto device = gbm_create_device(m_backend->fd()); if (!device) { setFailed("Could not create gbm device"); return false; } m_backend->setGbmDevice(device); display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, device, nullptr); } if (display == EGL_NO_DISPLAY) return false; setEglDisplay(display); return initEglAPI(); } void EglGbmBackend::init() { if (!initializeEgl()) { setFailed("Could not initialize egl"); return; } if (!initRenderingContext()) { setFailed("Could not initialize rendering context"); return; } initKWinGL(); initBufferAge(); initWayland(); } bool EglGbmBackend::initRenderingContext() { initBufferConfigs(); if (!createContext()) { return false; } const auto outputs = m_backend->outputs(); for (DrmOutput *drmOutput: outputs) { createOutput(drmOutput); } if (m_outputs.isEmpty()) { qCCritical(KWIN_DRM) << "Create Window Surfaces failed"; return false; } // set our first surface as the one for the abstract backend, just to make it happy setSurface(m_outputs.first().eglSurface); return makeContextCurrent(m_outputs.first()); } bool EglGbmBackend::resetOutput(Output &o, DrmOutput *drmOutput) { o.output = drmOutput; auto size = drmOutput->pixelSize(); auto gbmSurface = std::make_shared(m_backend->gbmDevice(), size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!gbmSurface) { qCCritical(KWIN_DRM) << "Create gbm surface failed"; return false; } auto eglSurface = eglCreatePlatformWindowSurfaceEXT(eglDisplay(), config(), (void *)(gbmSurface->surface()), nullptr); if (eglSurface == EGL_NO_SURFACE) { qCCritical(KWIN_DRM) << "Create Window Surface failed"; return false; } else { // destroy previous surface if (o.eglSurface != EGL_NO_SURFACE) { + if (surface() == o.eglSurface) { + setSurface(eglSurface); + } eglDestroySurface(eglDisplay(), o.eglSurface); } o.eglSurface = eglSurface; o.gbmSurface = gbmSurface; } return true; } void EglGbmBackend::createOutput(DrmOutput *drmOutput) { Output o; if (resetOutput(o, drmOutput)) { connect(drmOutput, &DrmOutput::modeChanged, this, [drmOutput, this] { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [drmOutput] (const auto &o) { return o.output == drmOutput; } ); if (it == m_outputs.end()) { return; } resetOutput(*it, drmOutput); } ); m_outputs << o; } } bool EglGbmBackend::makeContextCurrent(const Output &output) { const EGLSurface surface = output.eglSurface; if (surface == EGL_NO_SURFACE) { return false; } if (eglMakeCurrent(eglDisplay(), surface, surface, context()) == EGL_FALSE) { qCCritical(KWIN_DRM) << "Make Context Current failed"; return false; } EGLint error = eglGetError(); if (error != EGL_SUCCESS) { qCWarning(KWIN_DRM) << "Error occurred while creating context " << error; return false; } // TODO: ensure the viewport is set correctly each time const QSize &overall = screens()->size(); const QRect &v = output.output->geometry(); // TODO: are the values correct? qreal scale = output.output->scale(); glViewport(-v.x() * scale, (v.height() - overall.height() + v.y()) * scale, overall.width() * scale, overall.height() * scale); return true; } bool EglGbmBackend::initBufferConfigs() { const EGLint config_attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT, EGL_CONFIG_CAVEAT, EGL_NONE, EGL_NONE, }; EGLint count; EGLConfig configs[1024]; if (eglChooseConfig(eglDisplay(), config_attribs, configs, 1, &count) == EGL_FALSE) { qCCritical(KWIN_DRM) << "choose config failed"; return false; } if (count != 1) { qCCritical(KWIN_DRM) << "choose config did not return a config" << count; return false; } setConfig(configs[0]); return true; } void EglGbmBackend::present() { for (auto &o: m_outputs) { makeContextCurrent(o); presentOnOutput(o); } } void EglGbmBackend::presentOnOutput(EglGbmBackend::Output &o) { eglSwapBuffers(eglDisplay(), o.eglSurface); o.buffer = m_backend->createBuffer(o.gbmSurface); m_backend->present(o.buffer, o.output); if (supportsBufferAge()) { eglQuerySurface(eglDisplay(), o.eglSurface, EGL_BUFFER_AGE_EXT, &o.bufferAge); } } void EglGbmBackend::screenGeometryChanged(const QSize &size) { Q_UNUSED(size) // TODO, create new buffer? } SceneOpenGLTexturePrivate *EglGbmBackend::createBackendTexture(SceneOpenGLTexture *texture) { return new EglGbmTexture(texture, this); } QRegion EglGbmBackend::prepareRenderingFrame() { startRenderTimer(); return QRegion(); } QRegion EglGbmBackend::prepareRenderingForScreen(int screenId) { const Output &o = m_outputs.at(screenId); makeContextCurrent(o); if (supportsBufferAge()) { QRegion region; // Note: An age of zero means the buffer contents are undefined if (o.bufferAge > 0 && o.bufferAge <= o.damageHistory.count()) { for (int i = 0; i < o.bufferAge - 1; i++) region |= o.damageHistory[i]; } else { region = o.output->geometry(); } return region; } return QRegion(); } void EglGbmBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) { Q_UNUSED(renderedRegion) Q_UNUSED(damagedRegion) } void EglGbmBackend::endRenderingFrameForScreen(int screenId, const QRegion &renderedRegion, const QRegion &damagedRegion) { Output &o = m_outputs[screenId]; if (damagedRegion.intersected(o.output->geometry()).isEmpty() && screenId == 0) { // If the damaged region of a window is fully occluded, the only // rendering done, if any, will have been to repair a reused back // buffer, making it identical to the front buffer. // // In this case we won't post the back buffer. Instead we'll just // set the buffer age to 1, so the repaired regions won't be // rendered again in the next frame. if (!renderedRegion.intersected(o.output->geometry()).isEmpty()) glFlush(); for (auto &o: m_outputs) { o.bufferAge = 1; } return; } presentOnOutput(o); // Save the damaged region to history // Note: damage history is only collected for the first screen. For any other screen full repaints // are triggered. This is due to a limitation in Scene::paintGenericScreen which resets the Toplevel's // repaint. So multiple calls to Scene::paintScreen as it's done in multi-output rendering only // have correct damage information for the first screen. If we try to track damage nevertheless, // it creates artifacts. So for the time being we work around the problem by only supporting buffer // age on the first output. To properly support buffer age on all outputs the rendering needs to // be refactored in general. if (supportsBufferAge() && screenId == 0) { if (o.damageHistory.count() > 10) { o.damageHistory.removeLast(); } o.damageHistory.prepend(damagedRegion.intersected(o.output->geometry())); } } bool EglGbmBackend::usesOverlayWindow() const { return false; } bool EglGbmBackend::perScreenRendering() const { return true; } /************************************************ * EglTexture ************************************************/ EglGbmTexture::EglGbmTexture(KWin::SceneOpenGLTexture *texture, EglGbmBackend *backend) : AbstractEglTexture(texture, backend) { } EglGbmTexture::~EglGbmTexture() = default; } // namespace