diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp index dda5d2c89..98f717791 100644 --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -1,632 +1,623 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 "abstract_egl_backend.h" #include "egl_dmabuf.h" +#include "kwineglext.h" #include "texture.h" #include "composite.h" #include "egl_context_attribute_builder.h" #include "options.h" #include "platform.h" #include "scene.h" #include "wayland_server.h" #include #include #include // kwin libs #include #include #include // Qt #include #include #include namespace KWin { typedef GLboolean(*eglBindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display); typedef GLboolean(*eglUnbindWaylandDisplayWL_func)(EGLDisplay dpy, wl_display *display); typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); eglBindWaylandDisplayWL_func eglBindWaylandDisplayWL = nullptr; eglUnbindWaylandDisplayWL_func eglUnbindWaylandDisplayWL = nullptr; eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr; -#ifndef EGL_WAYLAND_BUFFER_WL -#define EGL_WAYLAND_BUFFER_WL 0x31D5 -#endif -#ifndef EGL_WAYLAND_PLANE_WL -#define EGL_WAYLAND_PLANE_WL 0x31D6 -#endif -#ifndef EGL_WAYLAND_Y_INVERTED_WL -#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB -#endif - AbstractEglBackend::AbstractEglBackend() : QObject(nullptr) , OpenGLBackend() { connect(Compositor::self(), &Compositor::aboutToDestroy, this, &AbstractEglBackend::unbindWaylandDisplay); } AbstractEglBackend::~AbstractEglBackend() { delete m_dmaBuf; } void AbstractEglBackend::unbindWaylandDisplay() { if (eglUnbindWaylandDisplayWL && m_display != EGL_NO_DISPLAY) { eglUnbindWaylandDisplayWL(m_display, *(WaylandServer::self()->display())); } } void AbstractEglBackend::cleanup() { cleanupGL(); doneCurrent(); eglDestroyContext(m_display, m_context); cleanupSurfaces(); eglReleaseThread(); kwinApp()->platform()->setSceneEglContext(EGL_NO_CONTEXT); kwinApp()->platform()->setSceneEglSurface(EGL_NO_SURFACE); kwinApp()->platform()->setSceneEglConfig(nullptr); } void AbstractEglBackend::cleanupSurfaces() { if (m_surface != EGL_NO_SURFACE) { eglDestroySurface(m_display, m_surface); } } bool AbstractEglBackend::initEglAPI() { EGLint major, minor; if (eglInitialize(m_display, &major, &minor) == EGL_FALSE) { qCWarning(KWIN_OPENGL) << "eglInitialize failed"; EGLint error = eglGetError(); if (error != EGL_SUCCESS) { qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error; } return false; } EGLint error = eglGetError(); if (error != EGL_SUCCESS) { qCWarning(KWIN_OPENGL) << "Error during eglInitialize " << error; return false; } qCDebug(KWIN_OPENGL) << "Egl Initialize succeeded"; if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) { qCCritical(KWIN_OPENGL) << "bind OpenGL API failed"; return false; } qCDebug(KWIN_OPENGL) << "EGL version: " << major << "." << minor; const QByteArray eglExtensions = eglQueryString(m_display, EGL_EXTENSIONS); setExtensions(eglExtensions.split(' ')); return true; } typedef void (*eglFuncPtr)(); static eglFuncPtr getProcAddress(const char* name) { return eglGetProcAddress(name); } void AbstractEglBackend::initKWinGL() { GLPlatform *glPlatform = GLPlatform::instance(); glPlatform->detect(EglPlatformInterface); options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting if (options->glPreferBufferSwap() == Options::AutoSwapStrategy) options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen glPlatform->printResults(); initGL(&getProcAddress); } void AbstractEglBackend::initBufferAge() { setSupportsBufferAge(false); if (hasExtension(QByteArrayLiteral("EGL_EXT_buffer_age"))) { const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE"); if (useBufferAge != "0") setSupportsBufferAge(true); } } void AbstractEglBackend::initWayland() { if (!WaylandServer::self()) { return; } if (hasExtension(QByteArrayLiteral("EGL_WL_bind_wayland_display"))) { eglBindWaylandDisplayWL = (eglBindWaylandDisplayWL_func)eglGetProcAddress("eglBindWaylandDisplayWL"); eglUnbindWaylandDisplayWL = (eglUnbindWaylandDisplayWL_func)eglGetProcAddress("eglUnbindWaylandDisplayWL"); eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL"); // only bind if not already done if (waylandServer()->display()->eglDisplay() != eglDisplay()) { if (!eglBindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display()))) { eglUnbindWaylandDisplayWL = nullptr; eglQueryWaylandBufferWL = nullptr; } else { waylandServer()->display()->setEglDisplay(eglDisplay()); } } } Q_ASSERT(!m_dmaBuf); m_dmaBuf = EglDmabuf::factory(this); } void AbstractEglBackend::initClientExtensions() { // Get the list of client extensions const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString)); if (clientExtensionsString.isEmpty()) { // If eglQueryString() returned NULL, the implementation doesn't support // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error. (void) eglGetError(); } m_clientExtensions = clientExtensionsString.split(' '); } bool AbstractEglBackend::hasClientExtension(const QByteArray &ext) const { return m_clientExtensions.contains(ext); } bool AbstractEglBackend::makeCurrent() { if (QOpenGLContext *context = QOpenGLContext::currentContext()) { // Workaround to tell Qt that no QOpenGLContext is current context->doneCurrent(); } const bool current = eglMakeCurrent(m_display, m_surface, m_surface, m_context); return current; } void AbstractEglBackend::doneCurrent() { eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } bool AbstractEglBackend::isOpenGLES() const { if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { return true; } return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; } bool AbstractEglBackend::createContext() { const bool haveRobustness = hasExtension(QByteArrayLiteral("EGL_EXT_create_context_robustness")); const bool haveCreateContext = hasExtension(QByteArrayLiteral("EGL_KHR_create_context")); const bool haveContextPriority = hasExtension(QByteArrayLiteral("EGL_IMG_context_priority")); std::vector> candidates; if (isOpenGLES()) { if (haveCreateContext && haveRobustness && haveContextPriority) { auto glesRobustPriority = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesRobustPriority->setVersion(2); glesRobustPriority->setRobust(true); glesRobustPriority->setHighPriority(true); candidates.push_back(std::move(glesRobustPriority)); } if (haveCreateContext && haveRobustness) { auto glesRobust = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesRobust->setVersion(2); glesRobust->setRobust(true); candidates.push_back(std::move(glesRobust)); } if (haveContextPriority) { auto glesPriority = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesPriority->setVersion(2); glesPriority->setHighPriority(true); candidates.push_back(std::move(glesPriority)); } auto gles = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); gles->setVersion(2); candidates.push_back(std::move(gles)); } else { if (options->glCoreProfile() && haveCreateContext) { if (haveRobustness && haveContextPriority) { auto robustCorePriority = std::unique_ptr(new EglContextAttributeBuilder); robustCorePriority->setVersion(3, 1); robustCorePriority->setRobust(true); robustCorePriority->setHighPriority(true); candidates.push_back(std::move(robustCorePriority)); } if (haveRobustness) { auto robustCore = std::unique_ptr(new EglContextAttributeBuilder); robustCore->setVersion(3, 1); robustCore->setRobust(true); candidates.push_back(std::move(robustCore)); } if (haveContextPriority) { auto corePriority = std::unique_ptr(new EglContextAttributeBuilder); corePriority->setVersion(3, 1); corePriority->setHighPriority(true); candidates.push_back(std::move(corePriority)); } auto core = std::unique_ptr(new EglContextAttributeBuilder); core->setVersion(3, 1); candidates.push_back(std::move(core)); } if (haveRobustness && haveCreateContext && haveContextPriority) { auto robustPriority = std::unique_ptr(new EglContextAttributeBuilder); robustPriority->setRobust(true); robustPriority->setHighPriority(true); candidates.push_back(std::move(robustPriority)); } if (haveRobustness && haveCreateContext) { auto robust = std::unique_ptr(new EglContextAttributeBuilder); robust->setRobust(true); candidates.push_back(std::move(robust)); } candidates.emplace_back(new EglContextAttributeBuilder); } EGLContext ctx = EGL_NO_CONTEXT; for (auto it = candidates.begin(); it != candidates.end(); it++) { const auto attribs = (*it)->build(); ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs.data()); if (ctx != EGL_NO_CONTEXT) { qCDebug(KWIN_OPENGL) << "Created EGL context with attributes:" << (*it).get(); break; } } if (ctx == EGL_NO_CONTEXT) { qCCritical(KWIN_OPENGL) << "Create Context failed"; return false; } m_context = ctx; kwinApp()->platform()->setSceneEglContext(m_context); return true; } void AbstractEglBackend::setEglDisplay(const EGLDisplay &display) { m_display = display; kwinApp()->platform()->setSceneEglDisplay(display); } void AbstractEglBackend::setConfig(const EGLConfig &config) { m_config = config; kwinApp()->platform()->setSceneEglConfig(config); } void AbstractEglBackend::setSurface(const EGLSurface &surface) { m_surface = surface; kwinApp()->platform()->setSceneEglSurface(surface); } AbstractEglTexture::AbstractEglTexture(SceneOpenGLTexture *texture, AbstractEglBackend *backend) : SceneOpenGLTexturePrivate() , q(texture) , m_backend(backend) , m_image(EGL_NO_IMAGE_KHR) { m_target = GL_TEXTURE_2D; } AbstractEglTexture::~AbstractEglTexture() { if (m_image != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(m_backend->eglDisplay(), m_image); } } OpenGLBackend *AbstractEglTexture::backend() { return m_backend; } bool AbstractEglTexture::loadTexture(WindowPixmap *pixmap) { // FIXME: Refactor this method. const auto &buffer = pixmap->buffer(); if (buffer.isNull()) { if (updateFromFBO(pixmap->fbo())) { return true; } if (loadInternalImageObject(pixmap)) { return true; } return false; } // try Wayland loading if (auto s = pixmap->surface()) { s->resetTrackedDamage(); } if (buffer->linuxDmabufBuffer()) { return loadDmabufTexture(buffer); } else if (buffer->shmBuffer()) { return loadShmTexture(buffer); } return loadEglTexture(buffer); } void AbstractEglTexture::updateTexture(WindowPixmap *pixmap) { // FIXME: Refactor this method. const auto &buffer = pixmap->buffer(); if (buffer.isNull()) { if (updateFromFBO(pixmap->fbo())) { return; } if (updateFromInternalImageObject(pixmap)) { return; } return; } auto s = pixmap->surface(); if (EglDmabufBuffer *dmabuf = static_cast(buffer->linuxDmabufBuffer())) { q->bind(); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) dmabuf->images()[0]); //TODO q->unbind(); if (m_image != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(m_backend->eglDisplay(), m_image); } m_image = EGL_NO_IMAGE_KHR; // The wl_buffer has ownership of the image // The origin in a dmabuf-buffer is at the upper-left corner, so the meaning // of Y-inverted is the inverse of OpenGL. const bool yInverted = !(dmabuf->flags() & KWaylandServer::LinuxDmabufUnstableV1Interface::YInverted); if (m_size != dmabuf->size() || yInverted != q->isYInverted()) { m_size = dmabuf->size(); q->setYInverted(yInverted); } if (s) { s->resetTrackedDamage(); } return; } if (!buffer->shmBuffer()) { q->bind(); EGLImageKHR image = attach(buffer); q->unbind(); if (image != EGL_NO_IMAGE_KHR) { if (m_image != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(m_backend->eglDisplay(), m_image); } m_image = image; } if (s) { s->resetTrackedDamage(); } return; } // shm fallback const QImage &image = buffer->data(); if (image.isNull() || !s) { return; } Q_ASSERT(image.size() == m_size); const QRegion damage = s->trackedDamage(); s->resetTrackedDamage(); // TODO: this should be shared with GLTexture::update createTextureSubImage(s->scale(), image, damage); } bool AbstractEglTexture::createTextureImage(const QImage &image) { if (image.isNull()) { return false; } glGenTextures(1, &m_texture); q->setFilter(GL_LINEAR); q->setWrapMode(GL_CLAMP_TO_EDGE); const QSize &size = image.size(); q->bind(); GLenum format = 0; switch (image.format()) { case QImage::Format_ARGB32: case QImage::Format_ARGB32_Premultiplied: format = GL_RGBA8; break; case QImage::Format_RGB32: format = GL_RGB8; break; default: return false; } if (GLPlatform::instance()->isGLES()) { if (s_supportsARGB32 && format == GL_RGBA8) { const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); glTexImage2D(m_target, 0, GL_BGRA_EXT, im.width(), im.height(), 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.bits()); } else { const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); glTexImage2D(m_target, 0, GL_RGBA, im.width(), im.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, im.bits()); } } else { glTexImage2D(m_target, 0, format, size.width(), size.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.bits()); } q->unbind(); q->setYInverted(true); m_size = size; updateMatrix(); return true; } void AbstractEglTexture::createTextureSubImage(int scale, const QImage &image, const QRegion &damage) { q->bind(); if (GLPlatform::instance()->isGLES()) { if (s_supportsARGB32 && (image.format() == QImage::Format_ARGB32 || image.format() == QImage::Format_ARGB32_Premultiplied)) { const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); for (const QRect &rect : damage) { auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); } } else { const QImage im = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied); for (const QRect &rect : damage) { auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), GL_RGBA, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); } } } else { const QImage im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); for (const QRect &rect : damage) { auto scaledRect = QRect(rect.x() * scale, rect.y() * scale, rect.width() * scale, rect.height() * scale); glTexSubImage2D(m_target, 0, scaledRect.x(), scaledRect.y(), scaledRect.width(), scaledRect.height(), GL_BGRA, GL_UNSIGNED_BYTE, im.copy(scaledRect).bits()); } } q->unbind(); } bool AbstractEglTexture::loadShmTexture(const QPointer< KWaylandServer::BufferInterface > &buffer) { return createTextureImage(buffer->data()); } bool AbstractEglTexture::loadEglTexture(const QPointer< KWaylandServer::BufferInterface > &buffer) { if (!eglQueryWaylandBufferWL) { return false; } if (!buffer->resource()) { return false; } glGenTextures(1, &m_texture); q->setWrapMode(GL_CLAMP_TO_EDGE); q->setFilter(GL_LINEAR); q->bind(); m_image = attach(buffer); q->unbind(); if (EGL_NO_IMAGE_KHR == m_image) { qCDebug(KWIN_OPENGL) << "failed to create egl image"; q->discard(); return false; } return true; } bool AbstractEglTexture::loadDmabufTexture(const QPointer< KWaylandServer::BufferInterface > &buffer) { 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(); return false; } Q_ASSERT(m_image == EGL_NO_IMAGE_KHR); glGenTextures(1, &m_texture); q->setWrapMode(GL_CLAMP_TO_EDGE); q->setFilter(GL_NEAREST); q->bind(); glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES) dmabuf->images()[0]); q->unbind(); m_size = dmabuf->size(); q->setYInverted(!(dmabuf->flags() & KWaylandServer::LinuxDmabufUnstableV1Interface::YInverted)); return true; } bool AbstractEglTexture::loadInternalImageObject(WindowPixmap *pixmap) { return createTextureImage(pixmap->internalImage()); } EGLImageKHR AbstractEglTexture::attach(const QPointer< KWaylandServer::BufferInterface > &buffer) { EGLint format, yInverted; eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_TEXTURE_FORMAT, &format); if (format != EGL_TEXTURE_RGB && format != EGL_TEXTURE_RGBA) { qCDebug(KWIN_OPENGL) << "Unsupported texture format: " << format; return EGL_NO_IMAGE_KHR; } if (!eglQueryWaylandBufferWL(m_backend->eglDisplay(), buffer->resource(), EGL_WAYLAND_Y_INVERTED_WL, &yInverted)) { // if EGL_WAYLAND_Y_INVERTED_WL is not supported wl_buffer should be treated as if value were EGL_TRUE yInverted = EGL_TRUE; } const EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, 0, EGL_NONE }; EGLImageKHR image = eglCreateImageKHR(m_backend->eglDisplay(), EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, (EGLClientBuffer)buffer->resource(), attribs); if (image != EGL_NO_IMAGE_KHR) { glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); m_size = buffer->size(); updateMatrix(); q->setYInverted(yInverted); } return image; } bool AbstractEglTexture::updateFromFBO(const QSharedPointer &fbo) { if (fbo.isNull()) { return false; } m_texture = fbo->texture(); m_size = fbo->size(); q->setWrapMode(GL_CLAMP_TO_EDGE); q->setFilter(GL_LINEAR); q->setYInverted(false); updateMatrix(); return true; } bool AbstractEglTexture::updateFromInternalImageObject(WindowPixmap *pixmap) { const QImage image = pixmap->internalImage(); if (image.isNull()) { return false; } if (m_size != image.size()) { glDeleteTextures(1, &m_texture); return loadInternalImageObject(pixmap); } createTextureSubImage(image.devicePixelRatio(), image, pixmap->toplevel()->damage()); return true; } } diff --git a/platformsupport/scenes/opengl/egl_dmabuf.cpp b/platformsupport/scenes/opengl/egl_dmabuf.cpp index ecba33a9d..f9b9c8cd0 100644 --- a/platformsupport/scenes/opengl/egl_dmabuf.cpp +++ b/platformsupport/scenes/opengl/egl_dmabuf.cpp @@ -1,493 +1,454 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright © 2019 Roman Gilg Copyright © 2018 Fredrik Höglund 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 "egl_dmabuf.h" #include "drm_fourcc.h" +#include "kwineglext.h" #include "../../../wayland_server.h" #include -#include 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 } } } }; 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); } 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; } EglDmabufBuffer::~EglDmabufBuffer() { removeImages(); } void EglDmabufBuffer::setInterfaceImplementation(EglDmabuf *interfaceImpl) { m_interfaceImpl = interfaceImpl; } void EglDmabufBuffer::addImage(EGLImage image) { m_images << image; } void EglDmabufBuffer::removeImages() { for (auto image : m_images) { eglDestroyImageKHR(m_interfaceImpl->m_backend->eglDisplay(), image); } m_images.clear(); } using Plane = KWaylandServer::LinuxDmabufUnstableV1Interface::Plane; using Flags = KWaylandServer::LinuxDmabufUnstableV1Interface::Flags; EGLImage EglDmabuf::createImage(const QVector &planes, uint32_t format, const QSize &size) { const bool hasModifiers = eglQueryDmaBufModifiersEXT != nullptr && planes[0].modifier != DRM_FORMAT_MOD_INVALID; QVector 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; } KWaylandServer::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 EglDmabufBuffer(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; } KWaylandServer::LinuxDmabufUnstableV1Buffer* EglDmabuf::yuvImport(const QVector &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 EglDmabufBuffer(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(1, plane), planeFormat, planeSize); if (!image) { delete buf; return nullptr; } buf->addImage(image); } // TODO: add buf import properties return buf; } 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) : LinuxDmabuf() , m_backend(backend) { 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(); } EglDmabuf::~EglDmabuf() { auto curBuffers = waylandServer()->linuxDmabufBuffers(); for (auto *buffer : curBuffers) { auto *buf = static_cast(buffer); buf->removeImages(); } } 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 &formats) { QVector::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 EglDmabuf::setSupportedFormatsAndModifiers() { const EGLDisplay eglDisplay = m_backend->eglDisplay(); EGLint count = 0; EGLBoolean success = eglQueryDmaBufFormatsEXT(eglDisplay, 0, nullptr, &count); if (!success || count == 0) { return; } QVector formats(count); if (!eglQueryDmaBufFormatsEXT(eglDisplay, count, (EGLint *) formats.data(), &count)) { return; } filterFormatsWithMultiplePlanes(formats); QHash > set; for (auto format : qAsConst(formats)) { if (eglQueryDmaBufModifiersEXT != nullptr) { count = 0; success = eglQueryDmaBufModifiersEXT(eglDisplay, format, 0, nullptr, nullptr, &count); if (success && count > 0) { QVector modifiers(count); if (eglQueryDmaBufModifiersEXT(eglDisplay, format, count, modifiers.data(), nullptr, &count)) { QSet modifiersSet; for (auto mod : qAsConst(modifiers)) { modifiersSet.insert(mod); } set.insert(format, modifiersSet); continue; } } } set.insert(format, QSet()); } LinuxDmabuf::setSupportedFormatsAndModifiers(set); } } diff --git a/platformsupport/scenes/opengl/kwineglext.h b/platformsupport/scenes/opengl/kwineglext.h new file mode 100644 index 000000000..834701b89 --- /dev/null +++ b/platformsupport/scenes/opengl/kwineglext.h @@ -0,0 +1,76 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2018 Fredrik Höglund +Copyright (C) 2020 Vlad Zahorodnii + +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 . +*********************************************************************/ + +#ifndef KWINEGLEXT_H +#define KWINEGLEXT_H + +#include + +#ifndef EGL_WL_bind_wayland_display +#define EGL_WAYLAND_BUFFER_WL 0x31D5 +#define EGL_WAYLAND_PLANE_WL 0x31D6 +#define EGL_TEXTURE_Y_U_V_WL 0x31D7 +#define EGL_TEXTURE_Y_UV_WL 0x31D8 +#define EGL_TEXTURE_Y_XUXV_WL 0x31D9 +#define EGL_TEXTURE_EXTERNAL_WL 0x31DA +#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB +#endif // EGL_WL_bind_wayland_display + +#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 + +#endif // KWINEGLEXT_H