62a7db7028
Summary: Because KWin is a very old project, we use three kinds of null pointer literals: 0, NULL, and nullptr. Since C++11, it's recommended to use nullptr keyword. This change converts all usages of 0 and NULL literal to nullptr. Even though it breaks git history, we need to do it in order to have consistent code as well to ease code reviews (it's very tempting for some people to add unrelated changes to their patches, e.g. converting NULL to nullptr). Test Plan: Compiles. Reviewers: #kwin, davidedmundson, romangg Reviewed By: #kwin, davidedmundson, romangg Subscribers: romangg, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D23618
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, nullptr, &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, nullptr, nullptr, &count);
|
|
|
|
if (success && count > 0) {
|
|
QVector<uint64_t> modifiers(count);
|
|
if (eglQueryDmaBufModifiersEXT(eglDisplay,
|
|
format, count, modifiers.data(),
|
|
nullptr, &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);
|
|
}
|
|
|
|
}
|