From 7459aabcac2471862a35a7c045de7176b5435f1c Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Sat, 26 Oct 2019 05:40:01 +0200 Subject: [PATCH] Dmabuf recovery on EGL reset Summary: The EGL platform might go away at any time through reconfiguration or because of a graphic error. KWin then resets the graphics. The dmabuf implementation must respect that and recover from a graphics reset by recreating all EGL images for existing buffer. This assumes that we won't change our graphics API mid-session and that supported plane and modifier configuration stays constant. In practise we remember all current dmabufs in a single map and only remove them if the client did destroy the resource. BUG: 411980 CCBUG: 413403 FIXED-IN: 5.17.2 Test Plan: Applied screenedge configuration without crash. Reviewers: #kwin, zzag Reviewed By: #kwin, zzag Subscribers: fvogt, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D24954 --- CMakeLists.txt | 1 + linux_dmabuf.cpp | 88 +++++++++++++ linux_dmabuf.h | 74 +++++++++++ platformsupport/scenes/opengl/CMakeLists.txt | 2 +- .../scenes/opengl/abstract_egl_backend.cpp | 8 +- .../{linux_dmabuf.cpp => egl_dmabuf.cpp} | 119 ++++++++---------- .../opengl/{linux_dmabuf.h => egl_dmabuf.h} | 53 ++++---- wayland_server.cpp | 10 ++ wayland_server.h | 15 +++ 9 files changed, 270 insertions(+), 100 deletions(-) create mode 100644 linux_dmabuf.cpp create mode 100644 linux_dmabuf.h rename platformsupport/scenes/opengl/{linux_dmabuf.cpp => egl_dmabuf.cpp} (84%) rename platformsupport/scenes/opengl/{linux_dmabuf.h => egl_dmabuf.h} (68%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c976092c1..6a722646b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,6 +441,7 @@ set(kwin_KDEINIT_SRCS libinput/device.cpp libinput/events.cpp libinput/libinput_logging.cpp + linux_dmabuf.cpp logind.cpp main.cpp manage.cpp diff --git a/linux_dmabuf.cpp b/linux_dmabuf.cpp new file mode 100644 index 0000000000..867f358da2 --- /dev/null +++ b/linux_dmabuf.cpp @@ -0,0 +1,88 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright © 2019 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 "linux_dmabuf.h" + +#include "wayland_server.h" + +#include + +namespace KWin +{ + +DmabufBuffer::DmabufBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags) + : KWayland::Server::LinuxDmabufUnstableV1Buffer(format, size) + , m_planes(planes) + , m_format(format) + , m_size(size) + , m_flags(flags) +{ + waylandServer()->addLinuxDmabufBuffer(this); +} + +DmabufBuffer::~DmabufBuffer() +{ + // Close all open file descriptors + for (int i = 0; i < m_planes.count(); i++) { + if (m_planes[i].fd != -1) + ::close(m_planes[i].fd); + m_planes[i].fd = -1; + } + if (waylandServer()) { + waylandServer()->removeLinuxDmabufBuffer(this); + } +} + +LinuxDmabuf::LinuxDmabuf() + : KWayland::Server::LinuxDmabufUnstableV1Interface::Impl() +{ + Q_ASSERT(waylandServer()); + waylandServer()->linuxDmabuf()->setImpl(this); +} + +LinuxDmabuf::~LinuxDmabuf() +{ + waylandServer()->linuxDmabuf()->setImpl(nullptr); +} + +using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; +using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; + +KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::importBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags) +{ + Q_UNUSED(planes) + Q_UNUSED(format) + Q_UNUSED(size) + Q_UNUSED(flags) + + return nullptr; +} + +void LinuxDmabuf::setSupportedFormatsAndModifiers(QHash > &set) +{ + waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(set); +} + +} diff --git a/linux_dmabuf.h b/linux_dmabuf.h new file mode 100644 index 0000000000..834a48e32d --- /dev/null +++ b/linux_dmabuf.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright © 2019 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 . +*********************************************************************/ +#pragma once + +#include + +#include + +#include + +namespace KWin +{ + +class KWIN_EXPORT DmabufBuffer : public KWayland::Server::LinuxDmabufUnstableV1Buffer +{ +public: + using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; + using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; + + DmabufBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags); + + ~DmabufBuffer() override; + + const QVector &planes() const { return m_planes; } + uint32_t format() const { return m_format; } + QSize size() const { return m_size; } + Flags flags() const { return m_flags; } + +private: + QVector m_planes; + uint32_t m_format; + QSize m_size; + Flags m_flags; +}; + +class KWIN_EXPORT LinuxDmabuf : public KWayland::Server::LinuxDmabufUnstableV1Interface::Impl +{ +public: + using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; + using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; + + explicit LinuxDmabuf(); + ~LinuxDmabuf(); + + KWayland::Server::LinuxDmabufUnstableV1Buffer *importBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags) override; + +protected: + void setSupportedFormatsAndModifiers(QHash > &set); +}; + +} diff --git a/platformsupport/scenes/opengl/CMakeLists.txt b/platformsupport/scenes/opengl/CMakeLists.txt index c6a6e4ba03..7f3fd8f46b 100644 --- a/platformsupport/scenes/opengl/CMakeLists.txt +++ b/platformsupport/scenes/opengl/CMakeLists.txt @@ -1,7 +1,7 @@ set(SCENE_OPENGL_BACKEND_SRCS abstract_egl_backend.cpp backend.cpp - linux_dmabuf.cpp + egl_dmabuf.cpp swap_profiler.cpp texture.cpp ) diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp index f8050a17bc..ad61a27846 100644 --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -18,7 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "abstract_egl_backend.h" -#include "linux_dmabuf.h" +#include "egl_dmabuf.h" #include "texture.h" #include "composite.h" #include "egl_context_attribute_builder.h" @@ -171,7 +171,7 @@ void AbstractEglBackend::initWayland() } } - LinuxDmabuf::factory(this); + EglDmabuf::factory(this); } void AbstractEglBackend::initClientExtensions() @@ -377,7 +377,7 @@ void AbstractEglTexture::updateTexture(WindowPixmap *pixmap) return; } auto s = pixmap->surface(); - if (DmabufBuffer *dmabuf = static_cast(buffer->linuxDmabufBuffer())) { + if (EglDmabufBuffer *dmabuf = static_cast(buffer->linuxDmabufBuffer())) { q->bind(); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) dmabuf->images()[0]); //TODO q->unbind(); @@ -532,7 +532,7 @@ bool AbstractEglTexture::loadEglTexture(const QPointer< KWayland::Server::Buffer bool AbstractEglTexture::loadDmabufTexture(const QPointer< KWayland::Server::BufferInterface > &buffer) { - DmabufBuffer *dmabuf = static_cast(buffer->linuxDmabufBuffer()); + auto *dmabuf = static_cast(buffer->linuxDmabufBuffer()); if (!dmabuf || dmabuf->images()[0] == EGL_NO_IMAGE_KHR) { qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer"; q->discard(); diff --git a/platformsupport/scenes/opengl/linux_dmabuf.cpp b/platformsupport/scenes/opengl/egl_dmabuf.cpp similarity index 84% rename from platformsupport/scenes/opengl/linux_dmabuf.cpp rename to platformsupport/scenes/opengl/egl_dmabuf.cpp index 56941e9b58..3f75bc7e6a 100644 --- a/platformsupport/scenes/opengl/linux_dmabuf.cpp +++ b/platformsupport/scenes/opengl/egl_dmabuf.cpp @@ -18,13 +18,11 @@ 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 "linux_dmabuf.h" +#include "egl_dmabuf.h" #include "drm_fourcc.h" #include "../../../wayland_server.h" -#include - #include #include @@ -173,67 +171,59 @@ YuvFormat yuvFormats[] = { } }; -DmabufBuffer::DmabufBuffer(EGLImage image, - const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags, - LinuxDmabuf *interfaceImpl) - : DmabufBuffer(planes, format, size, flags, interfaceImpl) +EglDmabufBuffer::EglDmabufBuffer(EGLImage image, + const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags, + EglDmabuf *interfaceImpl) + : EglDmabufBuffer(planes, format, size, flags, interfaceImpl) { m_importType = ImportType::Direct; addImage(image); } -DmabufBuffer::DmabufBuffer(const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags, - LinuxDmabuf *interfaceImpl) - : KWayland::Server::LinuxDmabufUnstableV1Buffer(format, size) - , m_planes(planes) - , m_flags(flags) +EglDmabufBuffer::EglDmabufBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags, + EglDmabuf *interfaceImpl) + : DmabufBuffer(planes, format, size, flags) , m_interfaceImpl(interfaceImpl) { m_importType = ImportType::Conversion; } -DmabufBuffer::~DmabufBuffer() +EglDmabufBuffer::~EglDmabufBuffer() { - if (m_interfaceImpl) { - m_interfaceImpl->removeBuffer(this); - removeImages(); - } - - // Close all open file descriptors - for (int i = 0; i < m_planes.count(); i++) { - if (m_planes[i].fd != -1) - ::close(m_planes[i].fd); - m_planes[i].fd = -1; - } + removeImages(); } -void DmabufBuffer::addImage(EGLImage image) +void EglDmabufBuffer::setInterfaceImplementation(EglDmabuf *interfaceImpl) +{ + m_interfaceImpl = interfaceImpl; +} + +void EglDmabufBuffer::addImage(EGLImage image) { m_images << image; } -void DmabufBuffer::removeImages() +void EglDmabufBuffer::removeImages() { for (auto image : m_images) { eglDestroyImageKHR(m_interfaceImpl->m_backend->eglDisplay(), image); } m_images.clear(); - m_interfaceImpl = nullptr; } using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; -EGLImage LinuxDmabuf::createImage(const QVector &planes, - uint32_t format, - const QSize &size) +EGLImage EglDmabuf::createImage(const QVector &planes, + uint32_t format, + const QSize &size) { const bool hasModifiers = eglQueryDmaBufModifiersEXT != nullptr && planes[0].modifier != DRM_FORMAT_MOD_INVALID; @@ -306,16 +296,16 @@ EGLImage LinuxDmabuf::createImage(const QVector &planes, return image; } -KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::importBuffer(const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags) +KWayland::Server::LinuxDmabufUnstableV1Buffer* EglDmabuf::importBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags) { Q_ASSERT(planes.count() > 0); // Try first to import as a single image if (auto *img = createImage(planes, format, size)) { - return new DmabufBuffer(img, planes, format, size, flags, this); + return new EglDmabufBuffer(img, planes, format, size, flags, this); } // TODO: to enable this we must be able to store multiple textures per window pixmap @@ -326,10 +316,10 @@ KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::importBuffer(const Q return nullptr; } -KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::yuvImport(const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags) +KWayland::Server::LinuxDmabufUnstableV1Buffer* EglDmabuf::yuvImport(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags) { YuvFormat yuvFormat; for (YuvFormat f : yuvFormats) { @@ -345,7 +335,7 @@ KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::yuvImport(const QVec return nullptr; } - auto *buf = new DmabufBuffer(planes, format, size, flags, this); + auto *buf = new EglDmabufBuffer(planes, format, size, flags, this); for (int i = 0; i < yuvFormat.outputPlanes; i++) { int planeIndex = yuvFormat.planes[i].planeIndex; @@ -371,7 +361,7 @@ KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::yuvImport(const QVec return buf; } -LinuxDmabuf* LinuxDmabuf::factory(AbstractEglBackend *backend) +EglDmabuf* EglDmabuf::factory(AbstractEglBackend *backend) { if (!backend->hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import"))) { return nullptr; @@ -386,30 +376,29 @@ LinuxDmabuf* LinuxDmabuf::factory(AbstractEglBackend *backend) return nullptr; } - return new LinuxDmabuf(backend); + return new EglDmabuf(backend); } -LinuxDmabuf::LinuxDmabuf(AbstractEglBackend *backend) - : KWayland::Server::LinuxDmabufUnstableV1Interface::Impl() +EglDmabuf::EglDmabuf(AbstractEglBackend *backend) + : LinuxDmabuf() , m_backend(backend) { - Q_ASSERT(waylandServer()); - m_interface = waylandServer()->display()->createLinuxDmabufInterface(backend); - setSupportedFormatsAndModifiers(); - m_interface->setImpl(this); - m_interface->create(); -} - -LinuxDmabuf::~LinuxDmabuf() -{ - for (auto *dmabuf : qAsConst(m_buffers)) { - dmabuf->removeImages(); + auto prevBuffersSet = waylandServer()->linuxDmabufBuffers(); + for (auto *buffer : prevBuffersSet) { + auto *buf = static_cast(buffer); + buf->setInterfaceImplementation(this); + buf->addImage(createImage(buf->planes(), buf->format(), buf->size())); } + setSupportedFormatsAndModifiers(); } -void LinuxDmabuf::removeBuffer(DmabufBuffer *buffer) +EglDmabuf::~EglDmabuf() { - m_buffers.remove(buffer); + auto curBuffers = waylandServer()->linuxDmabufBuffers(); + for (auto *buffer : curBuffers) { + auto *buf = static_cast(buffer); + buf->removeImages(); + } } const uint32_t s_multiPlaneFormats[] = { @@ -457,7 +446,7 @@ void filterFormatsWithMultiplePlanes(QVector &formats) } } -void LinuxDmabuf::setSupportedFormatsAndModifiers() +void EglDmabuf::setSupportedFormatsAndModifiers() { const EGLDisplay eglDisplay = m_backend->eglDisplay(); EGLint count = 0; @@ -498,7 +487,7 @@ void LinuxDmabuf::setSupportedFormatsAndModifiers() set.insert(format, QSet()); } - m_interface->setSupportedFormatsWithModifiers(set); + LinuxDmabuf::setSupportedFormatsAndModifiers(set); } } diff --git a/platformsupport/scenes/opengl/linux_dmabuf.h b/platformsupport/scenes/opengl/egl_dmabuf.h similarity index 68% rename from platformsupport/scenes/opengl/linux_dmabuf.h rename to platformsupport/scenes/opengl/egl_dmabuf.h index 4962767eba..e01dc53269 100644 --- a/platformsupport/scenes/opengl/linux_dmabuf.h +++ b/platformsupport/scenes/opengl/egl_dmabuf.h @@ -20,17 +20,17 @@ along with this program. If not, see . *********************************************************************/ #pragma once -#include "abstract_egl_backend.h" +#include "../../../linux_dmabuf.h" -#include +#include "abstract_egl_backend.h" #include namespace KWin { -class LinuxDmabuf; +class EglDmabuf; -class DmabufBuffer : public KWayland::Server::LinuxDmabufUnstableV1Buffer +class EglDmabufBuffer : public DmabufBuffer { public: using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; @@ -41,46 +41,43 @@ public: Conversion }; - DmabufBuffer(EGLImage image, - const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags, - LinuxDmabuf *interfaceImpl); + EglDmabufBuffer(EGLImage image, + const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags, + EglDmabuf *interfaceImpl); - DmabufBuffer(const QVector &planes, - uint32_t format, - const QSize &size, - Flags flags, - LinuxDmabuf *interfaceImpl); + EglDmabufBuffer(const QVector &planes, + uint32_t format, + const QSize &size, + Flags flags, + EglDmabuf *interfaceImpl); - ~DmabufBuffer() override; + ~EglDmabufBuffer() override; + void setInterfaceImplementation(EglDmabuf *interfaceImpl); void addImage(EGLImage image); void removeImages(); QVector images() const { return m_images; } - Flags flags() const { return m_flags; } - const QVector &planes() const { return m_planes; } private: QVector m_images; - QVector m_planes; - Flags m_flags; - LinuxDmabuf *m_interfaceImpl; + EglDmabuf *m_interfaceImpl; ImportType m_importType; }; -class LinuxDmabuf : public KWayland::Server::LinuxDmabufUnstableV1Interface::Impl +class EglDmabuf : public LinuxDmabuf { public: using Plane = KWayland::Server::LinuxDmabufUnstableV1Interface::Plane; using Flags = KWayland::Server::LinuxDmabufUnstableV1Interface::Flags; - static LinuxDmabuf* factory(AbstractEglBackend *backend); + static EglDmabuf* factory(AbstractEglBackend *backend); - explicit LinuxDmabuf(AbstractEglBackend *backend); - ~LinuxDmabuf(); + explicit EglDmabuf(AbstractEglBackend *backend); + ~EglDmabuf(); KWayland::Server::LinuxDmabufUnstableV1Buffer *importBuffer(const QVector &planes, uint32_t format, @@ -92,20 +89,16 @@ private: uint32_t format, const QSize &size); - KWayland::Server::LinuxDmabufUnstableV1Buffer *yuvImport(const QVector &planes, uint32_t format, const QSize &size, Flags flags); - void removeBuffer(DmabufBuffer *buffer); void setSupportedFormatsAndModifiers(); - KWayland::Server::LinuxDmabufUnstableV1Interface *m_interface; - QSet m_buffers; AbstractEglBackend *m_backend; - friend class DmabufBuffer; + friend class EglDmabufBuffer; }; } diff --git a/wayland_server.cpp b/wayland_server.cpp index d6f2f6e87c..2cce2c5efa 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -45,6 +45,7 @@ along with this program. If not, see . #include #include #include +#include #include #include #include @@ -471,6 +472,15 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags) return true; } +KWayland::Server::LinuxDmabufUnstableV1Interface *WaylandServer::linuxDmabuf() +{ + if (!m_linuxDmabuf) { + m_linuxDmabuf = m_display->createLinuxDmabufInterface(m_display); + m_linuxDmabuf->create(); + } + return m_linuxDmabuf; +} + SurfaceInterface *WaylandServer::findForeignTransientForSurface(SurfaceInterface *surface) { return m_XdgForeign->transientFor(surface); diff --git a/wayland_server.h b/wayland_server.h index 6dde8ff680..a51e6f7ce8 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -68,6 +68,8 @@ class XdgShellInterface; class XdgForeignInterface; class XdgOutputManagerInterface; class KeyStateInterface; +class LinuxDmabufUnstableV1Interface; +class LinuxDmabufUnstableV1Buffer; } } @@ -122,6 +124,7 @@ public: KWayland::Server::XdgOutputManagerInterface *xdgOutputManager() const { return m_xdgOutputManager; } + KWayland::Server::LinuxDmabufUnstableV1Interface *linuxDmabuf(); QList clients() const { return m_clients; @@ -224,6 +227,16 @@ public: void simulateUserActivity(); void updateKeyState(KWin::Xkb::LEDs leds); + QSet linuxDmabufBuffers() const { + return m_linuxDmabufBuffers; + } + void addLinuxDmabufBuffer(KWayland::Server::LinuxDmabufUnstableV1Buffer *buffer) { + m_linuxDmabufBuffers << buffer; + } + void removeLinuxDmabufBuffer(KWayland::Server::LinuxDmabufUnstableV1Buffer *buffer) { + m_linuxDmabufBuffers.remove(buffer); + } + Q_SIGNALS: void shellClientAdded(KWin::ShellClient*); void shellClientRemoved(KWin::ShellClient*); @@ -256,6 +269,8 @@ private: KWayland::Server::IdleInterface *m_idle = nullptr; KWayland::Server::XdgOutputManagerInterface *m_xdgOutputManager = nullptr; KWayland::Server::XdgDecorationManagerInterface *m_xdgDecorationManager = nullptr; + KWayland::Server::LinuxDmabufUnstableV1Interface *m_linuxDmabuf = nullptr; + QSet m_linuxDmabufBuffers; struct { KWayland::Server::ClientConnection *client = nullptr; QMetaObject::Connection destroyConnection;