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
This commit is contained in:
Roman Gilg 2019-10-26 05:40:01 +02:00
parent a151edd29b
commit 7459aabcac
9 changed files with 270 additions and 100 deletions

View file

@ -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

88
linux_dmabuf.cpp Normal file
View file

@ -0,0 +1,88 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright © 2019 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 "linux_dmabuf.h"
#include "wayland_server.h"
#include <unistd.h>
namespace KWin
{
DmabufBuffer::DmabufBuffer(const QVector<Plane> &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<Plane> &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<uint32_t, QSet<uint64_t> > &set)
{
waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(set);
}
}

74
linux_dmabuf.h Normal file
View file

@ -0,0 +1,74 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright © 2019 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/>.
*********************************************************************/
#pragma once
#include <kwin_export.h>
#include <KWayland/Server/linuxdmabuf_v1_interface.h>
#include <QVector>
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<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags);
~DmabufBuffer() override;
const QVector<Plane> &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<Plane> 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<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags) override;
protected:
void setSupportedFormatsAndModifiers(QHash<uint32_t, QSet<uint64_t> > &set);
};
}

View file

@ -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
)

View file

@ -18,7 +18,7 @@ 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 "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<DmabufBuffer *>(buffer->linuxDmabufBuffer())) {
if (EglDmabufBuffer *dmabuf = static_cast<EglDmabufBuffer *>(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<DmabufBuffer *>(buffer->linuxDmabufBuffer());
auto *dmabuf = static_cast<EglDmabufBuffer *>(buffer->linuxDmabufBuffer());
if (!dmabuf || dmabuf->images()[0] == EGL_NO_IMAGE_KHR) {
qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer";
q->discard();

View file

@ -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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "linux_dmabuf.h"
#include "egl_dmabuf.h"
#include "drm_fourcc.h"
#include "../../../wayland_server.h"
#include <KWayland/Server/display.h>
#include <unistd.h>
#include <EGL/eglmesaext.h>
@ -173,67 +171,59 @@ YuvFormat yuvFormats[] = {
}
};
DmabufBuffer::DmabufBuffer(EGLImage image,
const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags,
LinuxDmabuf *interfaceImpl)
: DmabufBuffer(planes, format, size, flags, interfaceImpl)
EglDmabufBuffer::EglDmabufBuffer(EGLImage image,
const QVector<Plane> &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<Plane> &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<Plane> &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<Plane> &planes,
uint32_t format,
const QSize &size)
EGLImage EglDmabuf::createImage(const QVector<Plane> &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<Plane> &planes,
return image;
}
KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::importBuffer(const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags)
KWayland::Server::LinuxDmabufUnstableV1Buffer* EglDmabuf::importBuffer(const QVector<Plane> &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<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags)
KWayland::Server::LinuxDmabufUnstableV1Buffer* EglDmabuf::yuvImport(const QVector<Plane> &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<EglDmabufBuffer*>(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<EglDmabufBuffer*>(buffer);
buf->removeImages();
}
}
const uint32_t s_multiPlaneFormats[] = {
@ -457,7 +446,7 @@ void filterFormatsWithMultiplePlanes(QVector<uint32_t> &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<uint64_t>());
}
m_interface->setSupportedFormatsWithModifiers(set);
LinuxDmabuf::setSupportedFormatsAndModifiers(set);
}
}

View file

@ -20,17 +20,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#pragma once
#include "abstract_egl_backend.h"
#include "../../../linux_dmabuf.h"
#include <KWayland/Server/linuxdmabuf_v1_interface.h>
#include "abstract_egl_backend.h"
#include <QVector>
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<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags,
LinuxDmabuf *interfaceImpl);
EglDmabufBuffer(EGLImage image,
const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags,
EglDmabuf *interfaceImpl);
DmabufBuffer(const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags,
LinuxDmabuf *interfaceImpl);
EglDmabufBuffer(const QVector<Plane> &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<EGLImage> images() const { return m_images; }
Flags flags() const { return m_flags; }
const QVector<Plane> &planes() const { return m_planes; }
private:
QVector<EGLImage> m_images;
QVector<Plane> 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<Plane> &planes,
uint32_t format,
@ -92,20 +89,16 @@ private:
uint32_t format,
const QSize &size);
KWayland::Server::LinuxDmabufUnstableV1Buffer *yuvImport(const QVector<Plane> &planes,
uint32_t format,
const QSize &size,
Flags flags);
void removeBuffer(DmabufBuffer *buffer);
void setSupportedFormatsAndModifiers();
KWayland::Server::LinuxDmabufUnstableV1Interface *m_interface;
QSet<DmabufBuffer*> m_buffers;
AbstractEglBackend *m_backend;
friend class DmabufBuffer;
friend class EglDmabufBuffer;
};
}

View file

@ -45,6 +45,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/dpms_interface.h>
#include <KWayland/Server/idle_interface.h>
#include <KWayland/Server/idleinhibit_interface.h>
#include <KWayland/Server/linuxdmabuf_v1_interface.h>
#include <KWayland/Server/output_interface.h>
#include <KWayland/Server/plasmashell_interface.h>
#include <KWayland/Server/plasmavirtualdesktop_interface.h>
@ -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);

View file

@ -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<ShellClient*> clients() const {
return m_clients;
@ -224,6 +227,16 @@ public:
void simulateUserActivity();
void updateKeyState(KWin::Xkb::LEDs leds);
QSet<KWayland::Server::LinuxDmabufUnstableV1Buffer*> 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<KWayland::Server::LinuxDmabufUnstableV1Buffer*> m_linuxDmabufBuffers;
struct {
KWayland::Server::ClientConnection *client = nullptr;
QMetaObject::Connection destroyConnection;