wayland: Refactor linux-dmabuf buffer management

At the moment, the render backend provides its specific implementation
of LinuxDmaBufV1ClientBuffer. This has some of its limitations. For
example, due to the strong coupling, compositing restarts must be
handled carefully. It's hard to have a generic code path to import
dmabufs, which would be nice to have in order to unify graphics buffer
allocation across various backends; currently, it's all scattered.

To make the code simpler, this change drops the commented out YUV import
code path for now. Given that Mesa implicitly handles it, the need for
it is no longer so urgent.
This commit is contained in:
Vlad Zahorodnii 2023-04-07 13:04:06 +03:00
parent 5c7e53d635
commit 466868f5d4
20 changed files with 200 additions and 612 deletions

View file

@ -98,7 +98,6 @@ target_sources(kwin PRIVATE
layers.cpp
layershellv1integration.cpp
layershellv1window.cpp
linux_dmabuf.cpp
main.cpp
modifier_only_shortcuts.cpp
mousebuttons.cpp

View file

@ -10,7 +10,6 @@
#include "drm_egl_backend.h"
#include "drm_gpu.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include "wayland/surface_interface.h"
@ -62,7 +61,7 @@ void DmabufFeedback::scanoutFailed(KWaylandServer::SurfaceInterface *surface, co
if (!m_attemptedFormats[dmabufAttrs.format].contains(dmabufAttrs.modifier)) {
m_attemptedFormats[dmabufAttrs.format] << dmabufAttrs.modifier;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> scanoutTranches;
const auto tranches = m_eglBackend->dmabuf()->tranches();
const auto tranches = m_eglBackend->tranches();
for (const auto &tranche : tranches) {
KWaylandServer::LinuxDmaBufV1Feedback::Tranche scanoutTranche;
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {

View file

@ -25,9 +25,7 @@
#include "drm_virtual_egl_layer.h"
#include "gbm_dmabuf.h"
#include "kwineglutils_p.h"
#include "linux_dmabuf.h"
#include "options.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "scene/surfaceitem_wayland.h"
#include "wayland/clientconnection.h"
#include "wayland/linuxdmabufv1clientbuffer.h"

View file

@ -15,7 +15,6 @@
#include "drm_logging.h"
#include "drm_output.h"
#include "drm_pipeline.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "scene/surfaceitem_wayland.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include "wayland/surface_interface.h"

View file

@ -20,7 +20,6 @@
#include "gbm_dmabuf.h"
#include "kwineglutils_p.h"
#include "libkwineffects/kwinglplatform.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "scene/surfaceitem_wayland.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include "wayland/surface_interface.h"

View file

@ -19,7 +19,6 @@
#include "drm_virtual_output.h"
#include "gbm_dmabuf.h"
#include "kwineglutils_p.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "scene/surfaceitem_wayland.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include "wayland/surface_interface.h"
@ -99,7 +98,7 @@ std::shared_ptr<GbmSwapchain> VirtualEglGbmLayer::createGbmSwapchain() const
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
const bool allowModifiers = !modifiersEnvSet || modifiersEnv;
const auto tranches = m_eglBackend->dmabuf()->tranches();
const auto tranches = m_eglBackend->tranches();
for (const auto &tranche : tranches) {
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
const auto size = m_output->pixelSize();

View file

@ -32,6 +32,11 @@ bool RenderBackend::checkGraphicsReset()
return false;
}
bool RenderBackend::testImportBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
return false;
}
QHash<uint32_t, QVector<uint64_t>> RenderBackend::supportedFormats() const
{
return QHash<uint32_t, QVector<uint64_t>>{{DRM_FORMAT_XRGB8888, QVector<uint64_t>{DRM_FORMAT_MOD_LINEAR}}};

View file

@ -13,6 +13,11 @@
#include <memory>
namespace KWaylandServer
{
class LinuxDmaBufV1ClientBuffer;
}
namespace KWin
{
@ -43,6 +48,7 @@ public:
virtual OutputLayer *cursorLayer(Output *output);
virtual void present(Output *output) = 0;
virtual bool testImportBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer);
virtual QHash<uint32_t, QVector<uint64_t>> supportedFormats() const;
virtual std::unique_ptr<SurfaceTexture> createSurfaceTextureInternal(SurfacePixmapInternal *pixmap);

View file

@ -1,50 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "linux_dmabuf.h"
#include "wayland_server.h"
namespace KWin
{
LinuxDmaBufV1ClientBuffer::LinuxDmaBufV1ClientBuffer(DmaBufAttributes &&attrs, quint32 flags)
: KWaylandServer::LinuxDmaBufV1ClientBuffer(std::move(attrs), flags)
{
waylandServer()->addLinuxDmabufBuffer(this);
}
LinuxDmaBufV1ClientBuffer::~LinuxDmaBufV1ClientBuffer()
{
if (waylandServer()) {
waylandServer()->removeLinuxDmabufBuffer(this);
}
}
LinuxDmaBufV1RendererInterface::LinuxDmaBufV1RendererInterface()
{
Q_ASSERT(waylandServer());
waylandServer()->linuxDmabuf()->setRendererInterface(this);
}
LinuxDmaBufV1RendererInterface::~LinuxDmaBufV1RendererInterface()
{
waylandServer()->linuxDmabuf()->setRendererInterface(nullptr);
}
KWaylandServer::LinuxDmaBufV1ClientBuffer *LinuxDmaBufV1RendererInterface::importBuffer(DmaBufAttributes &&attrs, quint32 flags)
{
return nullptr;
}
void LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(const QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> &tranches)
{
waylandServer()->linuxDmabuf()->setSupportedFormatsWithModifiers(tranches);
}
}

View file

@ -1,37 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <kwin_export.h>
#include "wayland/linuxdmabufv1clientbuffer.h"
namespace KWin
{
class KWIN_EXPORT LinuxDmaBufV1ClientBuffer : public KWaylandServer::LinuxDmaBufV1ClientBuffer
{
public:
LinuxDmaBufV1ClientBuffer(DmaBufAttributes &&attrs, quint32 flags);
~LinuxDmaBufV1ClientBuffer() override;
};
class KWIN_EXPORT LinuxDmaBufV1RendererInterface : public KWaylandServer::LinuxDmaBufV1ClientBufferIntegration::RendererInterface
{
public:
explicit LinuxDmaBufV1RendererInterface();
~LinuxDmaBufV1RendererInterface() override;
KWaylandServer::LinuxDmaBufV1ClientBuffer *importBuffer(DmaBufAttributes &&attrs, quint32 flags) override;
protected:
void setSupportedFormatsAndModifiers(const QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> &tranches);
};
}

View file

@ -2,7 +2,6 @@ target_sources(kwin PRIVATE
abstract_egl_backend.cpp
basiceglsurfacetexture_internal.cpp
basiceglsurfacetexture_wayland.cpp
egl_dmabuf.cpp
openglbackend.cpp
openglsurfacetexture.cpp
openglsurfacetexture_internal.cpp

View file

@ -8,11 +8,8 @@
*/
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
#include "composite.h"
#include "core/output.h"
#include "core/outputbackend.h"
#include "dmabuftexture.h"
#include "options.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "utils/common.h"
#include "utils/egl_context_attribute_builder.h"
#include "wayland/display.h"
@ -32,6 +29,11 @@
namespace KWin
{
typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT = nullptr;
eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT = nullptr;
static EGLContext s_globalShareContext = EGL_NO_CONTEXT;
static bool isOpenGLES_helper()
@ -50,7 +52,6 @@ AbstractEglBackend::AbstractEglBackend(dev_t deviceId)
AbstractEglBackend::~AbstractEglBackend()
{
delete m_dmaBuf;
}
EGLContext AbstractEglBackend::ensureGlobalShareContext()
@ -85,6 +86,10 @@ void AbstractEglBackend::teardown()
void AbstractEglBackend::cleanup()
{
for (const EGLImageKHR &image : m_importedBuffers) {
eglDestroyImageKHR(m_display, image);
}
cleanupSurfaces();
cleanupGL();
doneCurrent();
@ -172,6 +177,34 @@ void AbstractEglBackend::initBufferAge()
setSupportsSwapBuffersWithDamage(hasExtension(QByteArrayLiteral("EGL_EXT_swap_buffers_with_damage")));
}
static int bpcForFormat(uint32_t format)
{
switch (format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_RGB888:
case DRM_FORMAT_BGR888:
return 8;
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
return 10;
default:
return -1;
}
}
void AbstractEglBackend::initWayland()
{
if (!WaylandServer::self()) {
@ -192,8 +225,78 @@ void AbstractEglBackend::initWayland()
}
}
Q_ASSERT(!m_dmaBuf);
m_dmaBuf = EglDmabuf::factory(this);
if (hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import")) && hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) {
eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
EGLint count = 0;
EGLBoolean success = eglQueryDmaBufFormatsEXT(m_display, 0, nullptr, &count);
if (!success || count == 0) {
qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT failed:" << getEglErrorString();
return;
}
QVector<uint32_t> formats(count);
if (!eglQueryDmaBufFormatsEXT(m_display, count, (EGLint *)formats.data(), &count)) {
qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT with count" << count << "failed:" << getEglErrorString();
return;
}
for (auto format : std::as_const(formats)) {
EGLint count = 0;
const EGLBoolean success = eglQueryDmaBufModifiersEXT(m_display, format, 0, nullptr, nullptr, &count);
if (success && count > 0) {
QVector<uint64_t> modifiers(count);
QVector<EGLBoolean> externalOnly(count);
if (eglQueryDmaBufModifiersEXT(m_display, format, count, modifiers.data(), externalOnly.data(), &count)) {
for (int i = modifiers.size() - 1; i >= 0; i--) {
if (externalOnly[i]) {
modifiers.remove(i);
externalOnly.remove(i);
}
}
if (!modifiers.empty()) {
m_supportedFormats.insert(format, modifiers);
}
continue;
}
}
m_supportedFormats.insert(format, {DRM_FORMAT_MOD_INVALID});
}
qCDebug(KWIN_OPENGL) << "EGL driver advertises" << m_supportedFormats.count() << "supported dmabuf formats";
auto filterFormats = [this](int bpc) {
QHash<uint32_t, QVector<uint64_t>> set;
for (auto it = m_supportedFormats.constBegin(); it != m_supportedFormats.constEnd(); it++) {
if (bpcForFormat(it.key()) == bpc) {
set.insert(it.key(), it.value());
}
}
return set;
};
if (prefer10bpc()) {
m_tranches.append({
.device = deviceId(),
.flags = {},
.formatTable = filterFormats(10),
});
}
m_tranches.append({
.device = deviceId(),
.flags = {},
.formatTable = filterFormats(8),
});
m_tranches.append({
.device = deviceId(),
.flags = {},
.formatTable = filterFormats(-1),
});
KWaylandServer::LinuxDmaBufV1ClientBufferIntegration *dmabuf = waylandServer()->linuxDmabuf();
dmabuf->setRenderBackend(this);
dmabuf->setSupportedFormatsWithModifiers(m_tranches);
}
}
void AbstractEglBackend::initClientExtensions()
@ -367,6 +470,11 @@ void AbstractEglBackend::setSurface(const EGLSurface &surface)
m_surface = surface;
}
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> AbstractEglBackend::tranches() const
{
return m_tranches;
}
dev_t AbstractEglBackend::deviceId() const
{
return m_deviceId;
@ -377,9 +485,22 @@ bool AbstractEglBackend::prefer10bpc() const
return false;
}
EglDmabuf *AbstractEglBackend::dmabuf() const
EGLImageKHR AbstractEglBackend::importBufferAsImage(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
return m_dmaBuf;
auto it = m_importedBuffers.constFind(buffer);
if (Q_LIKELY(it != m_importedBuffers.constEnd())) {
return *it;
}
EGLImageKHR image = importDmaBufAsImage(buffer->attributes());
if (image != EGL_NO_IMAGE_KHR) {
m_importedBuffers[buffer] = image;
connect(buffer, &QObject::destroyed, this, [this, buffer]() {
eglDestroyImageKHR(m_display, m_importedBuffers.take(buffer));
});
}
return image;
}
EGLImageKHR AbstractEglBackend::importDmaBufAsImage(const DmaBufAttributes &dmabuf) const
@ -455,13 +576,14 @@ std::shared_ptr<GLTexture> AbstractEglBackend::importDmaBufAsTexture(const DmaBu
}
}
bool AbstractEglBackend::testImportBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
return importBufferAsImage(buffer) != EGL_NO_IMAGE_KHR;
}
QHash<uint32_t, QVector<uint64_t>> AbstractEglBackend::supportedFormats() const
{
if (m_dmaBuf) {
return m_dmaBuf->supportedFormats();
} else {
return RenderBackend::supportedFormats();
}
return m_supportedFormats;
}
bool AbstractEglBackend::initBufferConfigs()

View file

@ -8,6 +8,7 @@
*/
#pragma once
#include "platformsupport/scenes/opengl/openglbackend.h"
#include "wayland/linuxdmabufv1clientbuffer.h"
#include <QObject>
#include <epoxy/egl.h>
@ -30,7 +31,6 @@ struct AbstractEglBackendFunctions
};
struct DmaBufAttributes;
class EglDmabuf;
class Output;
class KWIN_EXPORT AbstractEglBackend : public OpenGLBackend
@ -62,14 +62,16 @@ public:
return m_config;
}
bool testImportBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer) override;
QHash<uint32_t, QVector<uint64_t>> supportedFormats() const override;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> tranches() const;
dev_t deviceId() const;
virtual bool prefer10bpc() const;
EglDmabuf *dmabuf() const;
std::shared_ptr<GLTexture> importDmaBufAsTexture(const DmaBufAttributes &attributes) const;
EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &attributes) const;
EGLImageKHR importBufferAsImage(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer);
protected:
AbstractEglBackend(dev_t deviceId = 0);
@ -100,10 +102,11 @@ private:
EGLSurface m_surface = EGL_NO_SURFACE;
EGLContext m_context = EGL_NO_CONTEXT;
EGLConfig m_config = nullptr;
// note: m_dmaBuf is nullptr if this is not the primary backend
EglDmabuf *m_dmaBuf = nullptr;
QList<QByteArray> m_clientExtensions;
const dev_t m_deviceId;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> m_tranches;
QHash<uint32_t, QVector<uint64_t>> m_supportedFormats;
QHash<KWaylandServer::LinuxDmaBufV1ClientBuffer *, EGLImageKHR> m_importedBuffers;
static AbstractEglBackend *s_primaryBackend;
};

View file

@ -7,7 +7,7 @@
#include "platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.h"
#include "kwineglext.h"
#include "libkwineffects/kwingltexture.h"
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
#include "scene/surfaceitem_wayland.h"
#include "utils/common.h"
#include "wayland/drmclientbuffer.h"
@ -155,21 +155,21 @@ void BasicEGLSurfaceTextureWayland::updateEglTexture(KWaylandServer::DrmClientBu
bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
auto dmabuf = static_cast<EglDmabufBuffer *>(buffer);
if (Q_UNLIKELY(dmabuf->images().constFirst() == EGL_NO_IMAGE_KHR)) {
EGLImageKHR image = backend()->importBufferAsImage(buffer);
if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) {
qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer";
return false;
}
m_texture.reset(new GLTexture(GL_TEXTURE_2D));
m_texture->setSize(dmabuf->size());
m_texture->setSize(buffer->size());
m_texture->create();
m_texture->setWrapMode(GL_CLAMP_TO_EDGE);
m_texture->setFilter(GL_NEAREST);
m_texture->bind();
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(dmabuf->images().constFirst()));
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(image));
m_texture->unbind();
m_texture->setContentTransform(dmabuf->origin() == KWaylandServer::ClientBuffer::Origin::TopLeft ? TextureTransform::MirrorY : TextureTransforms());
m_texture->setContentTransform(buffer->origin() == KWaylandServer::ClientBuffer::Origin::TopLeft ? TextureTransform::MirrorY : TextureTransforms());
m_bufferType = BufferType::DmaBuf;
return true;
@ -183,13 +183,12 @@ void BasicEGLSurfaceTextureWayland::updateDmabufTexture(KWaylandServer::LinuxDma
return;
}
auto dmabuf = static_cast<EglDmabufBuffer *>(buffer);
m_texture->bind();
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(dmabuf->images().constFirst()));
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(backend()->importBufferAsImage(buffer)));
m_texture->unbind();
// The origin in a dmabuf-buffer is at the upper-left corner, so the meaning
// of Y-inverted is the inverse of OpenGL.
m_texture->setContentTransform(dmabuf->origin() == KWaylandServer::ClientBuffer::Origin::TopLeft ? TextureTransform::MirrorY : TextureTransforms());
m_texture->setContentTransform(buffer->origin() == KWaylandServer::ClientBuffer::Origin::TopLeft ? TextureTransform::MirrorY : TextureTransforms());
}
EGLImageKHR BasicEGLSurfaceTextureWayland::attach(KWaylandServer::DrmClientBuffer *buffer)

View file

@ -1,340 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "platformsupport/scenes/opengl/egl_dmabuf.h"
#include "kwineglext.h"
#include "kwineglutils_p.h"
#include "utils/common.h"
#include "wayland_server.h"
#include <drm_fourcc.h>
#include <unistd.h>
namespace KWin
{
typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);
typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);
eglQueryDmaBufFormatsEXT_func eglQueryDmaBufFormatsEXT = nullptr;
eglQueryDmaBufModifiersEXT_func eglQueryDmaBufModifiersEXT = nullptr;
struct YuvPlane
{
int widthDivisor;
int heightDivisor;
uint32_t format;
int planeIndex;
};
struct YuvFormat
{
uint32_t format;
int inputPlanes;
int outputPlanes;
int textureType;
struct YuvPlane planes[3];
};
YuvFormat yuvFormats[] = {
{DRM_FORMAT_YUYV,
1,
2,
EGL_TEXTURE_Y_XUXV_WL,
{{1, 1,
DRM_FORMAT_GR88,
0},
{2, 1,
DRM_FORMAT_ARGB8888,
0}}},
{DRM_FORMAT_NV12,
2,
2,
EGL_TEXTURE_Y_UV_WL,
{{1, 1,
DRM_FORMAT_R8,
0},
{2, 2,
DRM_FORMAT_GR88,
1}}},
{DRM_FORMAT_YUV420,
3,
3,
EGL_TEXTURE_Y_U_V_WL,
{{1, 1,
DRM_FORMAT_R8,
0},
{2, 2,
DRM_FORMAT_R8,
1},
{2, 2,
DRM_FORMAT_R8,
2}}},
{DRM_FORMAT_YUV444,
3,
3,
EGL_TEXTURE_Y_U_V_WL,
{{1, 1,
DRM_FORMAT_R8,
0},
{1, 1,
DRM_FORMAT_R8,
1},
{1, 1,
DRM_FORMAT_R8,
2}}}};
EglDmabufBuffer::EglDmabufBuffer(EGLImage image,
DmaBufAttributes &&attrs,
quint32 flags,
EglDmabuf *interfaceImpl)
: EglDmabufBuffer(QVector<EGLImage>{image}, std::move(attrs), flags, interfaceImpl)
{
m_importType = ImportType::Direct;
}
EglDmabufBuffer::EglDmabufBuffer(const QVector<EGLImage> &images,
DmaBufAttributes &&attrs,
quint32 flags,
EglDmabuf *interfaceImpl)
: LinuxDmaBufV1ClientBuffer(std::move(attrs), flags)
, m_images(images)
, m_interfaceImpl(interfaceImpl)
, m_importType(ImportType::Conversion)
{
}
EglDmabufBuffer::~EglDmabufBuffer()
{
removeImages();
}
void EglDmabufBuffer::setInterfaceImplementation(EglDmabuf *interfaceImpl)
{
m_interfaceImpl = interfaceImpl;
}
void EglDmabufBuffer::setImages(const QVector<EGLImage> &images)
{
m_images = images;
}
void EglDmabufBuffer::removeImages()
{
for (auto image : std::as_const(m_images)) {
eglDestroyImageKHR(m_interfaceImpl->m_backend->eglDisplay(), image);
}
m_images.clear();
}
KWaylandServer::LinuxDmaBufV1ClientBuffer *EglDmabuf::importBuffer(DmaBufAttributes &&attrs, quint32 flags)
{
Q_ASSERT(attrs.planeCount > 0);
// Try first to import as a single image
if (auto *img = m_backend->importDmaBufAsImage(attrs)) {
return new EglDmabufBuffer(img, std::move(attrs), flags, this);
}
// TODO: to enable this we must be able to store multiple textures per window pixmap
// and when on window draw do yuv to rgb transformation per shader (see Weston)
// // not a single image, try yuv import
// return yuvImport(attrs, flags);
return nullptr;
}
KWaylandServer::LinuxDmaBufV1ClientBuffer *EglDmabuf::yuvImport(DmaBufAttributes &&attrs, quint32 flags)
{
YuvFormat yuvFormat;
for (YuvFormat f : yuvFormats) {
if (f.format == attrs.format) {
yuvFormat = f;
break;
}
}
if (yuvFormat.format == 0) {
return nullptr;
}
if (attrs.planeCount != yuvFormat.inputPlanes) {
return nullptr;
}
QVector<EGLImage> images;
for (int i = 0; i < yuvFormat.outputPlanes; i++) {
const int planeIndex = yuvFormat.planes[i].planeIndex;
const DmaBufAttributes planeAttrs{
.planeCount = 1,
.width = attrs.width / yuvFormat.planes[i].widthDivisor,
.height = attrs.height / yuvFormat.planes[i].heightDivisor,
.format = yuvFormat.planes[i].format,
.modifier = attrs.modifier,
.fd = {attrs.fd[planeIndex].duplicate()},
.offset = {attrs.offset[planeIndex], 0, 0, 0},
.pitch = {attrs.pitch[planeIndex], 0, 0, 0},
};
auto *image = m_backend->importDmaBufAsImage(planeAttrs);
if (!image) {
return nullptr;
}
images.push_back(image);
}
return new EglDmabufBuffer(images, std::move(attrs), flags, this);
}
EglDmabuf *EglDmabuf::factory(AbstractEglBackend *backend)
{
if (!backend->hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import"))) {
return nullptr;
}
if (backend->hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) {
eglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)eglGetProcAddress("eglQueryDmaBufFormatsEXT");
eglQueryDmaBufModifiersEXT = (eglQueryDmaBufModifiersEXT_func)eglGetProcAddress("eglQueryDmaBufModifiersEXT");
}
if (eglQueryDmaBufFormatsEXT == nullptr) {
return nullptr;
}
return new EglDmabuf(backend);
}
EglDmabuf::EglDmabuf(AbstractEglBackend *backend)
: m_backend(backend)
{
auto prevBuffersSet = waylandServer()->linuxDmabufBuffers();
for (auto *buffer : prevBuffersSet) {
auto *buf = static_cast<EglDmabufBuffer *>(buffer);
buf->setInterfaceImplementation(this);
buf->setImages({m_backend->importDmaBufAsImage(buf->attributes())});
}
setSupportedFormatsAndModifiers();
}
EglDmabuf::~EglDmabuf()
{
auto curBuffers = waylandServer()->linuxDmabufBuffers();
for (auto *buffer : curBuffers) {
auto *buf = static_cast<EglDmabufBuffer *>(buffer);
buf->removeImages();
}
}
static int bpcForFormat(uint32_t format)
{
switch (format) {
case DRM_FORMAT_XRGB8888:
case DRM_FORMAT_XBGR8888:
case DRM_FORMAT_RGBX8888:
case DRM_FORMAT_BGRX8888:
case DRM_FORMAT_ARGB8888:
case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_BGRA8888:
case DRM_FORMAT_RGB888:
case DRM_FORMAT_BGR888:
return 8;
case DRM_FORMAT_XRGB2101010:
case DRM_FORMAT_XBGR2101010:
case DRM_FORMAT_RGBX1010102:
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
return 10;
default:
return -1;
}
};
void EglDmabuf::setSupportedFormatsAndModifiers()
{
const EGLDisplay eglDisplay = m_backend->eglDisplay();
EGLint count = 0;
EGLBoolean success = eglQueryDmaBufFormatsEXT(eglDisplay, 0, nullptr, &count);
if (!success || count == 0) {
qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT failed!" << getEglErrorString();
return;
}
QVector<uint32_t> formats(count);
if (!eglQueryDmaBufFormatsEXT(eglDisplay, count, (EGLint *)formats.data(), &count)) {
qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT with count" << count << "failed!" << getEglErrorString();
return;
}
m_supportedFormats.clear();
for (auto format : std::as_const(formats)) {
if (eglQueryDmaBufModifiersEXT != nullptr) {
EGLint count = 0;
const EGLBoolean success = eglQueryDmaBufModifiersEXT(eglDisplay, format, 0, nullptr, nullptr, &count);
if (success && count > 0) {
QVector<uint64_t> modifiers(count);
QVector<EGLBoolean> externalOnly(count);
if (eglQueryDmaBufModifiersEXT(eglDisplay, format, count, modifiers.data(), externalOnly.data(), &count)) {
for (int i = modifiers.size() - 1; i >= 0; i--) {
if (externalOnly[i]) {
modifiers.remove(i);
externalOnly.remove(i);
}
}
if (!modifiers.empty()) {
m_supportedFormats.insert(format, modifiers);
}
continue;
}
}
}
m_supportedFormats.insert(format, {DRM_FORMAT_MOD_INVALID});
}
qCDebug(KWIN_OPENGL) << "EGL driver advertises" << m_supportedFormats.count() << "supported dmabuf formats" << (eglQueryDmaBufModifiersEXT != nullptr ? "with" : "without") << "modifiers";
auto filterFormats = [this](int bpc) {
QHash<uint32_t, QVector<uint64_t>> set;
for (auto it = m_supportedFormats.constBegin(); it != m_supportedFormats.constEnd(); it++) {
if (bpcForFormat(it.key()) == bpc) {
set.insert(it.key(), it.value());
}
}
return set;
};
if (m_backend->prefer10bpc()) {
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(10),
});
}
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(8),
});
m_tranches.append({
.device = m_backend->deviceId(),
.flags = {},
.formatTable = filterFormats(-1),
});
LinuxDmaBufV1RendererInterface::setSupportedFormatsAndModifiers(m_tranches);
}
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> EglDmabuf::tranches() const
{
return m_tranches;
}
QHash<uint32_t, QVector<uint64_t>> EglDmabuf::supportedFormats() const
{
return m_supportedFormats;
}
}

View file

@ -1,82 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2018 Fredrik Höglund <fredrik@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "platformsupport/scenes/opengl/abstract_egl_backend.h"
#include "linux_dmabuf.h"
#include <QVector>
namespace KWin
{
class EglDmabuf;
class EglDmabufBuffer : public LinuxDmaBufV1ClientBuffer
{
public:
enum class ImportType {
Direct,
Conversion
};
EglDmabufBuffer(EGLImage image,
DmaBufAttributes &&attrs,
quint32 flags,
EglDmabuf *interfaceImpl);
EglDmabufBuffer(const QVector<EGLImage> &images,
DmaBufAttributes &&attrs,
quint32 flags,
EglDmabuf *interfaceImpl);
~EglDmabufBuffer() override;
void setInterfaceImplementation(EglDmabuf *interfaceImpl);
void setImages(const QVector<EGLImage> &images);
void removeImages();
QVector<EGLImage> images() const
{
return m_images;
}
private:
QVector<EGLImage> m_images;
EglDmabuf *m_interfaceImpl;
ImportType m_importType;
};
class KWIN_EXPORT EglDmabuf : public LinuxDmaBufV1RendererInterface
{
public:
static EglDmabuf *factory(AbstractEglBackend *backend);
explicit EglDmabuf(AbstractEglBackend *backend);
~EglDmabuf() override;
KWaylandServer::LinuxDmaBufV1ClientBuffer *importBuffer(DmaBufAttributes &&attrs, quint32 flags) override;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> tranches() const;
QHash<uint32_t, QVector<uint64_t>> supportedFormats() const;
private:
KWaylandServer::LinuxDmaBufV1ClientBuffer *yuvImport(DmaBufAttributes &&attrs, quint32 flags);
void setSupportedFormatsAndModifiers();
AbstractEglBackend *m_backend;
QVector<KWaylandServer::LinuxDmaBufV1Feedback::Tranche> m_tranches;
QHash<uint32_t, QVector<uint64_t>> m_supportedFormats;
friend class EglDmabufBuffer;
};
}

View file

@ -11,6 +11,7 @@
*/
#include "linuxdmabufv1clientbuffer.h"
#include "core/renderbackend.h"
#include "linuxdmabufv1clientbuffer_p.h"
#include "surface_interface_p.h"
#include "utils/common.h"
@ -142,21 +143,26 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource,
return;
}
KWin::RenderBackend *renderBackend = m_integration->renderBackend();
if (Q_UNLIKELY(!renderBackend)) {
send_failed(resource->handle);
return;
}
m_isUsed = true;
m_attrs.width = width;
m_attrs.height = height;
m_attrs.format = format;
LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags);
if (!clientBuffer) {
auto clientBuffer = std::make_unique<LinuxDmaBufV1ClientBuffer>(std::move(m_attrs), flags);
if (!renderBackend->testImportBuffer(clientBuffer.get())) {
send_failed(resource->handle);
return;
}
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, 0);
if (!bufferResource) {
delete clientBuffer;
wl_resource_post_no_memory(resource->handle);
return;
}
@ -165,7 +171,7 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create(Resource *resource,
send_created(resource->handle, bufferResource);
DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
displayPrivate->registerClientBuffer(clientBuffer);
displayPrivate->registerClientBuffer(clientBuffer.release());
}
void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *resource,
@ -184,21 +190,26 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *reso
return;
}
KWin::RenderBackend *renderBackend = m_integration->renderBackend();
if (Q_UNLIKELY(!renderBackend)) {
wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
return;
}
m_isUsed = true;
m_attrs.width = width;
m_attrs.height = height;
m_attrs.format = format;
LinuxDmaBufV1ClientBuffer *clientBuffer = m_integration->rendererInterface()->importBuffer(std::move(m_attrs), flags);
if (!clientBuffer) {
auto clientBuffer = std::make_unique<LinuxDmaBufV1ClientBuffer>(std::move(m_attrs), flags);
if (!renderBackend->testImportBuffer(clientBuffer.get())) {
wl_resource_post_error(resource->handle, error_invalid_wl_buffer, "importing the supplied dmabufs failed");
return;
}
wl_resource *bufferResource = wl_resource_create(resource->client(), &wl_buffer_interface, 1, buffer_id);
if (!bufferResource) {
delete clientBuffer;
wl_resource_post_no_memory(resource->handle);
return;
}
@ -206,7 +217,7 @@ void LinuxDmaBufParamsV1::zwp_linux_buffer_params_v1_create_immed(Resource *reso
clientBuffer->initialize(bufferResource);
DisplayPrivate *displayPrivate = DisplayPrivate::get(m_integration->display());
displayPrivate->registerClientBuffer(clientBuffer);
displayPrivate->registerClientBuffer(clientBuffer.release());
}
bool LinuxDmaBufParamsV1::test(Resource *resource, uint32_t width, uint32_t height)
@ -279,21 +290,21 @@ LinuxDmaBufV1ClientBufferIntegration::~LinuxDmaBufV1ClientBufferIntegration()
{
}
LinuxDmaBufV1ClientBufferIntegration::RendererInterface *LinuxDmaBufV1ClientBufferIntegration::rendererInterface() const
{
return d->rendererInterface;
}
void LinuxDmaBufV1ClientBufferIntegration::setRendererInterface(RendererInterface *rendererInterface)
{
d->rendererInterface = rendererInterface;
}
bool operator==(const LinuxDmaBufV1Feedback::Tranche &t1, const LinuxDmaBufV1Feedback::Tranche &t2)
{
return t1.device == t2.device && t1.flags == t2.flags && t1.formatTable == t2.formatTable;
}
KWin::RenderBackend *LinuxDmaBufV1ClientBufferIntegration::renderBackend() const
{
return d->renderBackend;
}
void LinuxDmaBufV1ClientBufferIntegration::setRenderBackend(KWin::RenderBackend *renderBackend)
{
d->renderBackend = renderBackend;
}
void LinuxDmaBufV1ClientBufferIntegration::setSupportedFormatsWithModifiers(const QVector<LinuxDmaBufV1Feedback::Tranche> &tranches)
{
if (LinuxDmaBufV1FeedbackPrivate::get(d->defaultFeedback.get())->m_tranches != tranches) {

View file

@ -17,6 +17,11 @@
#include <QSet>
#include <sys/types.h>
namespace KWin
{
class RenderBackend;
}
namespace KWaylandServer
{
class LinuxDmaBufV1ClientBufferPrivate;
@ -93,39 +98,8 @@ public:
explicit LinuxDmaBufV1ClientBufferIntegration(Display *display);
~LinuxDmaBufV1ClientBufferIntegration() override;
/**
* The Iface class provides an interface from the LinuxDmabufInterface into the compositor
*/
class RendererInterface
{
public:
virtual ~RendererInterface() = default;
/**
* Imports a linux-dmabuf buffer into the compositor.
*
* The parent LinuxDmabufUnstableV1Interface class takes ownership of returned
* buffer objects.
*
* In return the returned buffer takes ownership of the file descriptor for each
* plane.
*
* Note that it is the responsibility of the caller to close the file descriptors
* when the import fails.
*
* @return The imported buffer on success, and nullptr otherwise.
*/
virtual LinuxDmaBufV1ClientBuffer *importBuffer(KWin::DmaBufAttributes &&attrs, quint32 flags) = 0;
};
RendererInterface *rendererInterface() const;
/**
* Sets the compositor implementation for the dmabuf interface.
*
* The ownership is not transferred by this call.
*/
void setRendererInterface(RendererInterface *rendererInterface);
KWin::RenderBackend *renderBackend() const;
void setRenderBackend(KWin::RenderBackend *renderBackend);
void setSupportedFormatsWithModifiers(const QVector<LinuxDmaBufV1Feedback::Tranche> &tranches);

View file

@ -35,10 +35,10 @@ public:
LinuxDmaBufV1ClientBufferIntegrationPrivate(LinuxDmaBufV1ClientBufferIntegration *q, Display *display);
LinuxDmaBufV1ClientBufferIntegration *q;
LinuxDmaBufV1ClientBufferIntegration::RendererInterface *rendererInterface = nullptr;
std::unique_ptr<LinuxDmaBufV1Feedback> defaultFeedback;
std::unique_ptr<LinuxDmaBufV1FormatTable> table;
dev_t mainDevice;
QPointer<KWin::RenderBackend> renderBackend;
QHash<uint32_t, QVector<uint64_t>> supportedModifiers;
protected:

View file

@ -42,7 +42,6 @@ class OutputManagementV2Interface;
class XdgForeignV2Interface;
class XdgOutputManagerV1Interface;
class LinuxDmaBufV1ClientBufferIntegration;
class LinuxDmaBufV1ClientBuffer;
class TabletManagerV2Interface;
class KeyboardShortcutsInhibitManagerV1Interface;
class XdgDecorationManagerV1Interface;
@ -214,19 +213,6 @@ public:
*/
SocketPairConnection createConnection();
QSet<KWaylandServer::LinuxDmaBufV1ClientBuffer *> linuxDmabufBuffers() const
{
return m_linuxDmabufBuffers;
}
void addLinuxDmabufBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
m_linuxDmabufBuffers << buffer;
}
void removeLinuxDmabufBuffer(KWaylandServer::LinuxDmaBufV1ClientBuffer *buffer)
{
m_linuxDmabufBuffers.remove(buffer);
}
/**
* Returns the first socket name that can be used to connect to this server.
* For a full list, use display()->socketNames()
@ -284,7 +270,6 @@ private:
KWaylandServer::XdgDecorationManagerV1Interface *m_xdgDecorationManagerV1 = nullptr;
KWaylandServer::LinuxDmaBufV1ClientBufferIntegration *m_linuxDmabuf = nullptr;
KWaylandServer::KeyboardShortcutsInhibitManagerV1Interface *m_keyboardShortcutsInhibitManager = nullptr;
QSet<KWaylandServer::LinuxDmaBufV1ClientBuffer *> m_linuxDmabufBuffers;
QPointer<KWaylandServer::ClientConnection> m_xwaylandConnection;
KWaylandServer::InputMethodV1Interface *m_inputMethod = nullptr;
QPointer<KWaylandServer::ClientConnection> m_inputMethodServerConnection;