6613327a9c
Summary: This adds support for LinuxDmabufUnstableV1Interface in kwin. Test Plan: Session starts. `weston-simple-dmabuf-egl` and `weston-simple-dmabuf-drm` execute without errors. Reviewers: #kwin, #plasma, davidedmundson, mart, graesslin, fredrik Subscribers: meven, zzag, romangg, anthonyfieroni, plasma-devel, kwin Tags: #kwin Maniphest Tasks: T8067 Differential Revision: https://phabricator.kde.org/D10750
504 lines
16 KiB
C++
504 lines
16 KiB
C++
/********************************************************************
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
Copyright © 2019 Roman Gilg <subdiff@gmail.com>
|
|
Copyright © 2018 Fredrik Höglund <fredrik@kde.org>
|
|
|
|
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 "drm_fourcc.h"
|
|
#include "../../../wayland_server.h"
|
|
|
|
#include <KWayland/Server/display.h>
|
|
|
|
#include <unistd.h>
|
|
#include <EGL/eglmesaext.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;
|
|
|
|
#ifndef EGL_EXT_image_dma_buf_import
|
|
#define EGL_LINUX_DMA_BUF_EXT 0x3270
|
|
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
|
|
#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
|
|
#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
|
|
#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
|
|
#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
|
|
#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
|
|
#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
|
|
#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
|
|
#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
|
|
#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
|
|
#define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B
|
|
#define EGL_SAMPLE_RANGE_HINT_EXT 0x327C
|
|
#define EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D
|
|
#define EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E
|
|
#define EGL_ITU_REC601_EXT 0x327F
|
|
#define EGL_ITU_REC709_EXT 0x3280
|
|
#define EGL_ITU_REC2020_EXT 0x3281
|
|
#define EGL_YUV_FULL_RANGE_EXT 0x3282
|
|
#define EGL_YUV_NARROW_RANGE_EXT 0x3283
|
|
#define EGL_YUV_CHROMA_SITING_0_EXT 0x3284
|
|
#define EGL_YUV_CHROMA_SITING_0_5_EXT 0x3285
|
|
#endif // EGL_EXT_image_dma_buf_import
|
|
|
|
#ifndef EGL_EXT_image_dma_buf_import_modifiers
|
|
#define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440
|
|
#define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441
|
|
#define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442
|
|
#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443
|
|
#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444
|
|
#define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445
|
|
#define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446
|
|
#define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447
|
|
#define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448
|
|
#define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449
|
|
#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A
|
|
#endif // EGL_EXT_image_dma_buf_import_modifiers
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
DmabufBuffer::DmabufBuffer(EGLImage image,
|
|
const QVector<Plane> &planes,
|
|
uint32_t format,
|
|
const QSize &size,
|
|
Flags flags,
|
|
LinuxDmabuf *interfaceImpl)
|
|
: DmabufBuffer(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)
|
|
, m_interfaceImpl(interfaceImpl)
|
|
{
|
|
m_importType = ImportType::Conversion;
|
|
}
|
|
|
|
DmabufBuffer::~DmabufBuffer()
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
void DmabufBuffer::addImage(EGLImage image)
|
|
{
|
|
m_images << image;
|
|
}
|
|
|
|
void DmabufBuffer::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)
|
|
{
|
|
const bool hasModifiers = eglQueryDmaBufModifiersEXT != nullptr &&
|
|
planes[0].modifier != DRM_FORMAT_MOD_INVALID;
|
|
|
|
QVector<EGLint> attribs;
|
|
attribs << EGL_WIDTH << size.width()
|
|
<< EGL_HEIGHT << size.height()
|
|
<< EGL_LINUX_DRM_FOURCC_EXT << EGLint(format)
|
|
|
|
<< EGL_DMA_BUF_PLANE0_FD_EXT << planes[0].fd
|
|
<< EGL_DMA_BUF_PLANE0_OFFSET_EXT << EGLint(planes[0].offset)
|
|
<< EGL_DMA_BUF_PLANE0_PITCH_EXT << EGLint(planes[0].stride);
|
|
|
|
if (hasModifiers) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT << EGLint(planes[0].modifier & 0xffffffff)
|
|
<< EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT << EGLint(planes[0].modifier >> 32);
|
|
}
|
|
|
|
if (planes.count() > 1) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE1_FD_EXT << planes[1].fd
|
|
<< EGL_DMA_BUF_PLANE1_OFFSET_EXT << EGLint(planes[1].offset)
|
|
<< EGL_DMA_BUF_PLANE1_PITCH_EXT << EGLint(planes[1].stride);
|
|
|
|
if (hasModifiers) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT << EGLint(planes[1].modifier & 0xffffffff)
|
|
<< EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT << EGLint(planes[1].modifier >> 32);
|
|
}
|
|
}
|
|
|
|
if (planes.count() > 2) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE2_FD_EXT << planes[2].fd
|
|
<< EGL_DMA_BUF_PLANE2_OFFSET_EXT << EGLint(planes[2].offset)
|
|
<< EGL_DMA_BUF_PLANE2_PITCH_EXT << EGLint(planes[2].stride);
|
|
|
|
if (hasModifiers) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT << EGLint(planes[2].modifier & 0xffffffff)
|
|
<< EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT << EGLint(planes[2].modifier >> 32);
|
|
}
|
|
}
|
|
|
|
if (eglQueryDmaBufModifiersEXT != nullptr && planes.count() > 3) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE3_FD_EXT << planes[3].fd
|
|
<< EGL_DMA_BUF_PLANE3_OFFSET_EXT << EGLint(planes[3].offset)
|
|
<< EGL_DMA_BUF_PLANE3_PITCH_EXT << EGLint(planes[3].stride);
|
|
|
|
if (hasModifiers) {
|
|
attribs
|
|
<< EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT << EGLint(planes[3].modifier & 0xffffffff)
|
|
<< EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT << EGLint(planes[3].modifier >> 32);
|
|
}
|
|
}
|
|
|
|
attribs << EGL_NONE;
|
|
|
|
EGLImage image = eglCreateImageKHR(m_backend->eglDisplay(),
|
|
EGL_NO_CONTEXT,
|
|
EGL_LINUX_DMA_BUF_EXT,
|
|
(EGLClientBuffer) nullptr,
|
|
attribs.data());
|
|
if (image == EGL_NO_IMAGE_KHR) {
|
|
return nullptr;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::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);
|
|
}
|
|
|
|
// 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(planes, format, size, flags);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
KWayland::Server::LinuxDmabufUnstableV1Buffer* LinuxDmabuf::yuvImport(const QVector<Plane> &planes,
|
|
uint32_t format,
|
|
const QSize &size,
|
|
Flags flags)
|
|
{
|
|
YuvFormat yuvFormat;
|
|
for (YuvFormat f : yuvFormats) {
|
|
if (f.format == format) {
|
|
yuvFormat = f;
|
|
break;
|
|
}
|
|
}
|
|
if (yuvFormat.format == 0) {
|
|
return nullptr;
|
|
}
|
|
if (planes.count() != yuvFormat.inputPlanes) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto *buf = new DmabufBuffer(planes, format, size, flags, this);
|
|
|
|
for (int i = 0; i < yuvFormat.outputPlanes; i++) {
|
|
int planeIndex = yuvFormat.planes[i].planeIndex;
|
|
Plane plane = {
|
|
planes[planeIndex].fd,
|
|
planes[planeIndex].offset,
|
|
planes[planeIndex].stride,
|
|
planes[planeIndex].modifier
|
|
};
|
|
const auto planeFormat = yuvFormat.planes[i].format;
|
|
const auto planeSize = QSize(size.width() / yuvFormat.planes[i].widthDivisor,
|
|
size.height() / yuvFormat.planes[i].heightDivisor);
|
|
auto *image = createImage(QVector<Plane>(1, plane),
|
|
planeFormat,
|
|
planeSize);
|
|
if (!image) {
|
|
delete buf;
|
|
return nullptr;
|
|
}
|
|
buf->addImage(image);
|
|
}
|
|
// TODO: add buf import properties
|
|
return buf;
|
|
}
|
|
|
|
LinuxDmabuf* LinuxDmabuf::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 LinuxDmabuf(backend);
|
|
}
|
|
|
|
LinuxDmabuf::LinuxDmabuf(AbstractEglBackend *backend)
|
|
: KWayland::Server::LinuxDmabufUnstableV1Interface::Impl()
|
|
, 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();
|
|
}
|
|
}
|
|
|
|
void LinuxDmabuf::removeBuffer(DmabufBuffer *buffer)
|
|
{
|
|
m_buffers.remove(buffer);
|
|
}
|
|
|
|
const uint32_t s_multiPlaneFormats[] = {
|
|
DRM_FORMAT_XRGB8888_A8,
|
|
DRM_FORMAT_XBGR8888_A8,
|
|
DRM_FORMAT_RGBX8888_A8,
|
|
DRM_FORMAT_BGRX8888_A8,
|
|
DRM_FORMAT_RGB888_A8,
|
|
DRM_FORMAT_BGR888_A8,
|
|
DRM_FORMAT_RGB565_A8,
|
|
DRM_FORMAT_BGR565_A8,
|
|
|
|
DRM_FORMAT_NV12,
|
|
DRM_FORMAT_NV21,
|
|
DRM_FORMAT_NV16,
|
|
DRM_FORMAT_NV61,
|
|
DRM_FORMAT_NV24,
|
|
DRM_FORMAT_NV42,
|
|
|
|
DRM_FORMAT_YUV410,
|
|
DRM_FORMAT_YVU410,
|
|
DRM_FORMAT_YUV411,
|
|
DRM_FORMAT_YVU411,
|
|
DRM_FORMAT_YUV420,
|
|
DRM_FORMAT_YVU420,
|
|
DRM_FORMAT_YUV422,
|
|
DRM_FORMAT_YVU422,
|
|
DRM_FORMAT_YUV444,
|
|
DRM_FORMAT_YVU444
|
|
};
|
|
|
|
void filterFormatsWithMultiplePlanes(QVector<uint32_t> &formats)
|
|
{
|
|
QVector<uint32_t>::iterator it = formats.begin();
|
|
while (it != formats.end()) {
|
|
for (auto linuxFormat : s_multiPlaneFormats) {
|
|
if (*it == linuxFormat) {
|
|
qDebug() << "Filter multi-plane format" << *it;
|
|
it = formats.erase(it);
|
|
it--;
|
|
break;
|
|
}
|
|
}
|
|
it++;
|
|
}
|
|
}
|
|
|
|
void LinuxDmabuf::setSupportedFormatsAndModifiers()
|
|
{
|
|
const EGLDisplay eglDisplay = m_backend->eglDisplay();
|
|
EGLint count = 0;
|
|
EGLBoolean success = eglQueryDmaBufFormatsEXT(eglDisplay, 0, NULL, &count);
|
|
|
|
if (!success || count == 0) {
|
|
return;
|
|
}
|
|
|
|
QVector<uint32_t> formats(count);
|
|
if (!eglQueryDmaBufFormatsEXT(eglDisplay, count, (EGLint *) formats.data(), &count)) {
|
|
return;
|
|
}
|
|
|
|
filterFormatsWithMultiplePlanes(formats);
|
|
|
|
QHash<uint32_t, QSet<uint64_t> > set;
|
|
|
|
for (auto format : qAsConst(formats)) {
|
|
if (eglQueryDmaBufModifiersEXT != nullptr) {
|
|
count = 0;
|
|
success = eglQueryDmaBufModifiersEXT(eglDisplay, format, 0, NULL, NULL, &count);
|
|
|
|
if (success && count > 0) {
|
|
QVector<uint64_t> modifiers(count);
|
|
if (eglQueryDmaBufModifiersEXT(eglDisplay,
|
|
format, count, modifiers.data(),
|
|
NULL, &count)) {
|
|
QSet<uint64_t> modifiersSet;
|
|
for (auto mod : qAsConst(modifiers)) {
|
|
modifiersSet.insert(mod);
|
|
}
|
|
set.insert(format, modifiersSet);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
set.insert(format, QSet<uint64_t>());
|
|
}
|
|
|
|
m_interface->setSupportedFormatsWithModifiers(set);
|
|
}
|
|
|
|
}
|