From b860439be53196b7270cd5596f942c8c5494ed53 Mon Sep 17 00:00:00 2001 From: Aleix Pol Gonzalez Date: Thu, 26 Oct 2023 18:49:38 +0200 Subject: [PATCH] Allow support dmabuf formats that we have conversions for. If there's a supported mechanism to handle the format, announce them as supported. If there are modifiers supported by the graphics card (even though as external only), offer them as well. --- src/backends/drm/drm_egl_layer_surface.cpp | 10 ++-- .../scenes/opengl/abstract_egl_backend.cpp | 54 +++++++++++++---- .../opengl/basiceglsurfacetexture_wayland.cpp | 34 ++++++----- .../scenes/opengl/egldisplay.cpp | 58 ++++++++++--------- .../scenes/opengl/egldisplay.h | 26 ++++----- src/plugins/qpa/eglplatformcontext.cpp | 2 +- 6 files changed, 113 insertions(+), 71 deletions(-) diff --git a/src/backends/drm/drm_egl_layer_surface.cpp b/src/backends/drm/drm_egl_layer_surface.cpp index fb751a8d38..d5ca335820 100644 --- a/src/backends/drm/drm_egl_layer_surface.cpp +++ b/src/backends/drm/drm_egl_layer_surface.cpp @@ -363,20 +363,22 @@ std::unique_ptr EglGbmLayerSurface::createSurface(c const bool cpuCopy = importMode == MultiGpuImportMode::DumbBuffer || m_bufferTarget == BufferTarget::Dumb; QList renderModifiers; auto ret = std::make_unique(); + const auto drmFormat = m_eglBackend->eglDisplayObject()->allSupportedDrmFormats()[format]; if (importMode == MultiGpuImportMode::Egl) { ret->importContext = m_eglBackend->contextForGpu(m_gpu); if (!ret->importContext || ret->importContext->isSoftwareRenderer()) { return nullptr; } - renderModifiers = filterModifiers(ret->importContext->displayObject()->allSupportedDrmFormats()[format], - m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); + const auto importDrmFormat = ret->importContext->displayObject()->allSupportedDrmFormats()[format]; + renderModifiers = filterModifiers(importDrmFormat.allModifiers, + drmFormat.nonExternalOnlyModifiers); } else if (cpuCopy) { if (!cpuCopyFormats.contains(format)) { return nullptr; } - renderModifiers = m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format); + renderModifiers = drmFormat.nonExternalOnlyModifiers; } else { - renderModifiers = filterModifiers(modifiers, m_eglBackend->eglDisplayObject()->supportedDrmFormats().value(format)); + renderModifiers = filterModifiers(modifiers, drmFormat.nonExternalOnlyModifiers); } if (renderModifiers.empty()) { return nullptr; diff --git a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp index 0f27bce709..3f7beaa061 100644 --- a/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/src/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -149,40 +149,72 @@ void AbstractEglBackend::initWayland() } } - auto filterFormats = [this](std::optional bpc) { - const auto formats = m_display->supportedDrmFormats(); + const auto formats = m_display->allSupportedDrmFormats(); + auto filterFormats = [this, &formats](std::optional bpc, bool withExternalOnlyYUV) { QHash> set; for (auto it = formats.constBegin(); it != formats.constEnd(); it++) { const auto info = formatInfo(it.key()); if (!info || (bpc && bpc != info->bitsPerColor)) { continue; } - const bool duplicate = std::any_of(m_tranches.begin(), m_tranches.end(), [fmt = it.key()](const auto &tranche) { - return tranche.formatTable.contains(fmt); - }); - if (duplicate) { + + const bool externalOnlySupported = withExternalOnlyYUV && info->yuvConversion(); + QList modifiers = externalOnlySupported ? it->allModifiers : it->nonExternalOnlyModifiers; + + if (externalOnlySupported && !modifiers.isEmpty()) { + if (auto yuv = info->yuvConversion()) { + for (auto plane : std::as_const(yuv->plane)) { + const auto planeModifiers = formats.value(plane.format).allModifiers; + modifiers.erase(std::remove_if(modifiers.begin(), modifiers.end(), [&planeModifiers](uint64_t mod) { + return !planeModifiers.contains(mod); + }), + modifiers.end()); + } + } + } + for (const auto &tranche : std::as_const(m_tranches)) { + if (modifiers.isEmpty()) { + break; + } + const auto trancheModifiers = tranche.formatTable.value(it.key()); + for (auto trancheModifier : trancheModifiers) { + modifiers.removeAll(trancheModifier); + } + } + if (modifiers.isEmpty()) { continue; } - set.insert(it.key(), it.value()); + set.insert(it.key(), modifiers); } return set; }; + + auto includeShaderConversions = [](QHash> &&formats) -> QHash> { + for (auto format : s_drmConversions.keys()) { + auto &modifiers = formats[format]; + if (modifiers.isEmpty()) { + modifiers = {DRM_FORMAT_MOD_LINEAR}; + } + } + return formats; + }; + if (prefer10bpc()) { m_tranches.append({ .device = deviceId(), .flags = {}, - .formatTable = filterFormats(10), + .formatTable = filterFormats(10, false), }); } m_tranches.append({ .device = deviceId(), .flags = {}, - .formatTable = filterFormats(8), + .formatTable = filterFormats(8, false), }); m_tranches.append({ .device = deviceId(), .flags = {}, - .formatTable = filterFormats(std::nullopt), + .formatTable = includeShaderConversions(filterFormats({}, true)), }); LinuxDmaBufV1ClientBufferIntegration *dmabuf = waylandServer()->linuxDmabuf(); @@ -323,7 +355,7 @@ bool AbstractEglBackend::testImportBuffer(GraphicsBuffer *buffer) QHash> AbstractEglBackend::supportedFormats() const { - return m_display->supportedDrmFormats(); + return m_display->nonExternalOnlySupportedDrmFormats(); } EGLSurface AbstractEglBackend::surface() const diff --git a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp index bc741b292b..6dac384a2e 100644 --- a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp +++ b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_wayland.cpp @@ -103,19 +103,20 @@ void BasicEGLSurfaceTextureWayland::updateShmTexture(GraphicsBuffer *buffer, con bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer) { - auto createTexture = [this](EGLImageKHR image, const QSize &size) { + auto createTexture = [this](EGLImageKHR image, const QSize &size, bool isExternalOnly) { if (Q_UNLIKELY(image == EGL_NO_IMAGE_KHR)) { qCritical(KWIN_OPENGL) << "Invalid dmabuf-based wl_buffer"; return std::shared_ptr(); } - auto texture = std::make_shared(GL_TEXTURE_2D); + GLint target = isExternalOnly ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; + auto texture = std::make_shared(target); texture->setSize(size); texture->create(); texture->setWrapMode(GL_CLAMP_TO_EDGE); texture->setFilter(GL_LINEAR); texture->bind(); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast(image)); + glEGLImageTargetTexture2DOES(target, static_cast(image)); texture->unbind(); if (m_pixmap->bufferOrigin() == GraphicsBufferOrigin::TopLeft) { texture->setContentTransform(TextureTransform::MirrorY); @@ -123,17 +124,20 @@ bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer) return texture; }; - auto format = formatInfo(buffer->dmabufAttributes()->format); - if (format && format->yuvConversion) { + const auto attribs = buffer->dmabufAttributes(); + auto format = formatInfo(attribs->format); + if (auto itConv = s_drmConversions.find(buffer->dmabufAttributes()->format); itConv != s_drmConversions.end()) { QList> textures; - Q_ASSERT(format->yuvConversion->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); - for (uint plane = 0; plane < format->yuvConversion->plane.count(); ++plane) { - const auto ¤tPlane = format->yuvConversion->plane[plane]; + Q_ASSERT(itConv->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); + + for (uint plane = 0; plane < itConv->plane.count(); ++plane) { + const auto ¤tPlane = itConv->plane[plane]; QSize size = buffer->size(); size.rwidth() /= currentPlane.widthDivisor; size.rheight() /= currentPlane.heightDivisor; - auto t = createTexture(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size), size); + const bool isExternal = backend()->eglDisplayObject()->isExternalOnly(currentPlane.format, attribs->modifier); + auto t = createTexture(backend()->importBufferAsImage(buffer, plane, currentPlane.format, size), size, isExternal); if (!t) { return false; } @@ -141,7 +145,8 @@ bool BasicEGLSurfaceTextureWayland::loadDmabufTexture(GraphicsBuffer *buffer) } m_texture = {textures}; } else { - auto texture = createTexture(backend()->importBufferAsImage(buffer), buffer->size()); + const bool isExternal = backend()->eglDisplayObject()->isExternalOnly(attribs->format, attribs->modifier); + auto texture = createTexture(backend()->importBufferAsImage(buffer), buffer->size(), isExternal); if (!texture) { return false; } @@ -161,11 +166,10 @@ void BasicEGLSurfaceTextureWayland::updateDmabufTexture(GraphicsBuffer *buffer) } const GLint target = GL_TEXTURE_2D; - auto format = formatInfo(buffer->dmabufAttributes()->format); - if (format && format->yuvConversion) { - Q_ASSERT(format->yuvConversion->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); - for (uint plane = 0; plane < format->yuvConversion->plane.count(); ++plane) { - const auto ¤tPlane = format->yuvConversion->plane[plane]; + if (auto itConv = s_drmConversions.find(buffer->dmabufAttributes()->format); itConv != s_drmConversions.end()) { + Q_ASSERT(itConv->plane.count() == uint(buffer->dmabufAttributes()->planeCount)); + for (uint plane = 0; plane < itConv->plane.count(); ++plane) { + const auto ¤tPlane = itConv->plane[plane]; QSize size = buffer->size(); size.rwidth() /= currentPlane.widthDivisor; size.rheight() /= currentPlane.heightDivisor; diff --git a/src/platformsupport/scenes/opengl/egldisplay.cpp b/src/platformsupport/scenes/opengl/egldisplay.cpp index 1f23e0665f..aaa1ab43bd 100644 --- a/src/platformsupport/scenes/opengl/egldisplay.cpp +++ b/src/platformsupport/scenes/opengl/egldisplay.cpp @@ -80,9 +80,7 @@ EglDisplay::EglDisplay(::EGLDisplay display, const QList &extensions , m_owning(owning) , m_supportsBufferAge(extensions.contains(QByteArrayLiteral("EGL_EXT_buffer_age")) && qgetenv("KWIN_USE_BUFFER_AGE") != "0") , m_supportsNativeFence(extensions.contains(QByteArrayLiteral("EGL_ANDROID_native_fence_sync"))) - , m_importFormats(queryImportFormats(Filter::Normal)) - , m_externalOnlyFormats(queryImportFormats(Filter::ExternalOnly)) - , m_allImportFormats(queryImportFormats(Filter::None)) + , m_importFormats(queryImportFormats()) { } @@ -241,26 +239,31 @@ EGLImageKHR EglDisplay::importDmaBufAsImage(const DmaBufAttributes &dmabuf, int return img; } -QHash> EglDisplay::supportedDrmFormats() const +QHash EglDisplay::allSupportedDrmFormats() const { return m_importFormats; } -QHash> EglDisplay::allSupportedDrmFormats() const +QHash> EglDisplay::nonExternalOnlySupportedDrmFormats() const { - return m_allImportFormats; + QHash> ret; + ret.reserve(m_importFormats.size()); + for (auto it = m_importFormats.constBegin(), itEnd = m_importFormats.constEnd(); it != itEnd; ++it) { + ret[it.key()] = it->nonExternalOnlyModifiers; + } + return ret; } bool EglDisplay::isExternalOnly(uint32_t format, uint64_t modifier) const { - if (const auto it = m_externalOnlyFormats.find(format); it != m_externalOnlyFormats.end()) { - return it->contains(modifier); + if (const auto it = m_importFormats.find(format); it != m_importFormats.end()) { + return it->externalOnlyModifiers.contains(modifier); } else { return false; } } -QHash> EglDisplay::queryImportFormats(Filter filter) const +QHash EglDisplay::queryImportFormats() const { if (!hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import")) || !hasExtension(QByteArrayLiteral("EGL_EXT_image_dma_buf_import_modifiers"))) { return {}; @@ -287,37 +290,40 @@ QHash> EglDisplay::queryImportFormats(Filter filter) c qCCritical(KWIN_OPENGL) << "eglQueryDmaBufFormatsEXT with count" << count << "failed!" << getEglErrorString(); return {}; } - QHash> ret; + QHash ret; for (const auto format : std::as_const(formats)) { if (eglQueryDmaBufModifiersEXT != nullptr) { EGLint count = 0; const EGLBoolean success = eglQueryDmaBufModifiersEXT(m_handle, format, 0, nullptr, nullptr, &count); if (success && count > 0) { - QList modifiers(count); + DrmFormatInfo drmFormatInfo; + drmFormatInfo.allModifiers.resize(count); QList externalOnly(count); - if (eglQueryDmaBufModifiersEXT(m_handle, format, count, modifiers.data(), externalOnly.data(), &count)) { - if (filter != Filter::None) { - const bool external = filter == Filter::Normal; - for (int i = modifiers.size() - 1; i >= 0; i--) { - if (externalOnly[i] == external) { - modifiers.remove(i); - externalOnly.remove(i); - } + if (eglQueryDmaBufModifiersEXT(m_handle, format, count, drmFormatInfo.allModifiers.data(), externalOnly.data(), &count)) { + drmFormatInfo.externalOnlyModifiers = drmFormatInfo.allModifiers; + drmFormatInfo.nonExternalOnlyModifiers = drmFormatInfo.allModifiers; + for (int i = drmFormatInfo.allModifiers.size() - 1; i >= 0; i--) { + if (externalOnly[i]) { + drmFormatInfo.nonExternalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]); + } else { + drmFormatInfo.externalOnlyModifiers.removeAll(drmFormatInfo.allModifiers[i]); } } - if (!modifiers.empty()) { - if (filter != Filter::ExternalOnly && !modifiers.contains(DRM_FORMAT_MOD_INVALID)) { - modifiers.push_back(DRM_FORMAT_MOD_INVALID); + if (!drmFormatInfo.allModifiers.empty()) { + if (!drmFormatInfo.allModifiers.contains(DRM_FORMAT_MOD_INVALID)) { + drmFormatInfo.allModifiers.push_back(DRM_FORMAT_MOD_INVALID); + drmFormatInfo.nonExternalOnlyModifiers.push_back(DRM_FORMAT_MOD_INVALID); } - ret.insert(format, modifiers); + ret.insert(format, drmFormatInfo); } continue; } } } - if (filter != Filter::ExternalOnly) { - ret.insert(format, {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}); - } + DrmFormatInfo drmFormat; + drmFormat.allModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}; + drmFormat.nonExternalOnlyModifiers = {DRM_FORMAT_MOD_INVALID, DRM_FORMAT_MOD_LINEAR}; + ret.insert(format, drmFormat); } return ret; } diff --git a/src/platformsupport/scenes/opengl/egldisplay.h b/src/platformsupport/scenes/opengl/egldisplay.h index 6d3fd3d9f5..c55fe9c942 100644 --- a/src/platformsupport/scenes/opengl/egldisplay.h +++ b/src/platformsupport/scenes/opengl/egldisplay.h @@ -25,6 +25,13 @@ class GLTexture; class KWIN_EXPORT EglDisplay { public: + struct DrmFormatInfo + { + QList allModifiers; + QList nonExternalOnlyModifiers; + QList externalOnlyModifiers; + }; + EglDisplay(::EGLDisplay display, const QList &extensions, bool owning = true); ~EglDisplay(); @@ -36,11 +43,9 @@ public: bool supportsBufferAge() const; bool supportsNativeFence() const; - QHash> supportedDrmFormats() const; - /** - * includes external formats - */ - QHash> allSupportedDrmFormats() const; + + QHash> nonExternalOnlySupportedDrmFormats() const; + QHash allSupportedDrmFormats() const; bool isExternalOnly(uint32_t format, uint64_t modifier) const; EGLImageKHR importDmaBufAsImage(const DmaBufAttributes &dmabuf) const; @@ -49,12 +54,7 @@ public: static std::unique_ptr create(::EGLDisplay display, bool owning = true); private: - enum class Filter { - None, - Normal, - ExternalOnly - }; - QHash> queryImportFormats(Filter filter) const; + QHash queryImportFormats() const; const ::EGLDisplay m_handle; const QList m_extensions; @@ -62,9 +62,7 @@ private: const bool m_supportsBufferAge; const bool m_supportsNativeFence; - const QHash> m_importFormats; - const QHash> m_externalOnlyFormats; - const QHash> m_allImportFormats; + const QHash m_importFormats; }; } diff --git a/src/plugins/qpa/eglplatformcontext.cpp b/src/plugins/qpa/eglplatformcontext.cpp index 201f609bf4..418c2d4f7f 100644 --- a/src/plugins/qpa/eglplatformcontext.cpp +++ b/src/plugins/qpa/eglplatformcontext.cpp @@ -75,7 +75,7 @@ bool EGLPlatformContext::makeCurrent(QPlatformSurface *surface) QOpenGLContextPrivate::setCurrentContext(context()); Window *window = static_cast(surface); - Swapchain *swapchain = window->swapchain(m_eglDisplay->supportedDrmFormats()); + Swapchain *swapchain = window->swapchain(m_eglDisplay->nonExternalOnlySupportedDrmFormats()); GraphicsBuffer *buffer = swapchain->acquire(); if (!buffer) {