backends/drm: add fallback for missing gbm modifier support

It can happen that a gbm implementation does not support modifiers, while
the drm driver does. To prevent that from breaking KWin, fall back to creating
a gbm surface without modifiers when creating one with modifiers fails.

BUG: 453320
This commit is contained in:
Xaver Hugl 2022-05-05 13:28:36 +02:00
parent a264938ff3
commit f02a6fd128
4 changed files with 91 additions and 76 deletions

View file

@ -163,37 +163,35 @@ bool EglGbmLayerSurface::createGbmSurface(const QSize &size, uint32_t format, co
{
static bool modifiersEnvSet = false;
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && m_gpu->addFB2ModifiersSupported()
&& (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && !modifiers.isEmpty();
const auto config = m_eglBackend->config(format);
std::shared_ptr<GbmSurface> gbmSurface;
#if HAVE_GBM_BO_GET_FD_FOR_PLANE
if (!allowModifiers) {
#else
// modifiers have to be disabled with multi-gpu if gbm_bo_get_fd_for_plane is not available
if (!allowModifiers || m_gpu != m_eglBackend->gpu()) {
bool allowModifiers = m_gpu->addFB2ModifiersSupported() && (!modifiersEnvSet || (modifiersEnvSet && modifiersEnv)) && !modifiers.isEmpty();
#if !HAVE_GBM_BO_GET_FD_FOR_PLANE
allowModifiers &= m_gpu == m_eglBackend->gpu();
#endif
int gbmFlags = flags | GBM_BO_USE_RENDERING;
if (m_gpu == m_eglBackend->gpu()) {
gbmFlags |= GBM_BO_USE_SCANOUT;
} else {
gbmFlags |= GBM_BO_USE_LINEAR;
}
gbmSurface = std::make_shared<GbmSurface>(m_eglBackend->gpu(), size, format, gbmFlags, config);
} else {
gbmSurface = std::make_shared<GbmSurface>(m_eglBackend->gpu(), size, format, modifiers, config);
if (!gbmSurface->isValid()) {
// the egl / gbm implementation may reject the modifier list from another gpu
// as a fallback use linear, to at least make CPU copy more efficient
const QVector<uint64_t> linear = {DRM_FORMAT_MOD_LINEAR};
gbmSurface = std::make_shared<GbmSurface>(m_eglBackend->gpu(), size, format, linear, config);
const auto config = m_eglBackend->config(format);
if (!config) {
return false;
}
if (allowModifiers) {
const auto ret = GbmSurface::createSurface(m_eglBackend, size, format, modifiers, config);
if (const auto surface = std::get_if<std::shared_ptr<GbmSurface>>(&ret)) {
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = *surface;
return true;
} else if (std::get<GbmSurface::Error>(ret) != GbmSurface::Error::ModifiersUnsupported) {
return false;
}
}
if (gbmSurface->isValid()) {
int gbmFlags = flags | GBM_BO_USE_RENDERING;
if (m_gpu == m_eglBackend->gpu()) {
gbmFlags |= GBM_BO_USE_SCANOUT;
} else {
gbmFlags |= GBM_BO_USE_LINEAR;
}
const auto ret = GbmSurface::createSurface(m_eglBackend, size, format, gbmFlags, config);
if (const auto surface = std::get_if<std::shared_ptr<GbmSurface>>(&ret)) {
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = gbmSurface;
m_gbmSurface = *surface;
return true;
} else {
return false;

View file

@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2022 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -22,41 +23,16 @@
namespace KWin
{
GbmSurface::GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t flags, EGLConfig config)
: m_surface(gbm_surface_create(gpu->gbmDevice(), size.width(), size.height(), format, flags))
, m_eglBackend(static_cast<EglGbmBackend *>(gpu->platform()->renderBackend()))
, m_size(size)
, m_format(format)
, m_flags(flags)
, m_fbo(new GLFramebuffer(0, size))
{
if (!m_surface) {
qCCritical(KWIN_DRM) << "Could not create gbm surface!" << strerror(errno);
return;
}
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_eglBackend->eglDisplay(), config, m_surface, nullptr);
if (m_eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
}
}
GbmSurface::GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, QVector<uint64_t> modifiers, EGLConfig config)
: m_surface(gbm_surface_create_with_modifiers(gpu->gbmDevice(), size.width(), size.height(), format, modifiers.isEmpty() ? nullptr : modifiers.constData(), modifiers.count()))
, m_eglBackend(static_cast<EglGbmBackend *>(gpu->platform()->renderBackend()))
GbmSurface::GbmSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, uint32_t flags, gbm_surface *surface, EGLSurface eglSurface)
: m_surface(surface)
, m_eglBackend(backend)
, m_eglSurface(eglSurface)
, m_size(size)
, m_format(format)
, m_modifiers(modifiers)
, m_flags(0)
, m_flags(flags)
, m_fbo(new GLFramebuffer(0, size))
{
if (!m_surface) {
qCCritical(KWIN_DRM) << "Could not create gbm surface!" << strerror(errno);
return;
}
m_eglSurface = eglCreatePlatformWindowSurfaceEXT(m_eglBackend->eglDisplay(), config, m_surface, nullptr);
if (m_eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
}
}
GbmSurface::~GbmSurface()
@ -120,11 +96,6 @@ QSize GbmSurface::size() const
return m_size;
}
bool GbmSurface::isValid() const
{
return m_surface != nullptr && m_eglSurface != EGL_NO_SURFACE;
}
uint32_t GbmSurface::format() const
{
return m_format;
@ -153,4 +124,38 @@ uint32_t GbmSurface::flags() const
{
return m_flags;
}
std::variant<std::shared_ptr<GbmSurface>, GbmSurface::Error> GbmSurface::createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, uint32_t flags, EGLConfig config)
{
gbm_surface *surface = gbm_surface_create(backend->gpu()->gbmDevice(), size.width(), size.height(), format, flags);
if (!surface) {
qCWarning(KWIN_DRM) << "Creating gbm surface failed!" << strerror(errno);
return Error::Unknown;
}
EGLSurface eglSurface = eglCreatePlatformWindowSurfaceEXT(backend->eglDisplay(), config, surface, nullptr);
if (eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
return Error::Unknown;
}
return std::make_shared<GbmSurface>(backend, size, format, QVector<uint64_t>{}, flags, surface, eglSurface);
}
std::variant<std::shared_ptr<GbmSurface>, GbmSurface::Error> GbmSurface::createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, QVector<uint64_t> modifiers, EGLConfig config)
{
gbm_surface *surface = gbm_surface_create_with_modifiers(backend->gpu()->gbmDevice(), size.width(), size.height(), format, modifiers.data(), modifiers.size());
if (!surface) {
if (errno == ENOSYS) {
return Error::ModifiersUnsupported;
} else {
qCWarning(KWIN_DRM) << "Creating gbm surface failed!" << strerror(errno);
return Error::Unknown;
}
}
EGLSurface eglSurface = eglCreatePlatformWindowSurfaceEXT(backend->eglDisplay(), config, surface, nullptr);
if (eglSurface == EGL_NO_SURFACE) {
qCCritical(KWIN_DRM) << "Creating EGL surface failed!" << getEglErrorString();
return Error::Unknown;
}
return std::make_shared<GbmSurface>(backend, size, format, modifiers, 0, surface, eglSurface);
}
}

View file

@ -12,7 +12,7 @@
#include <cstdint>
#include <epoxy/egl.h>
#include <memory>
#include <optional>
#include <variant>
#include "drm_buffer_gbm.h"
#include "utils/damagejournal.h"
@ -29,8 +29,7 @@ class EglGbmBackend;
class GbmSurface : public std::enable_shared_from_this<GbmSurface>
{
public:
explicit GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, uint32_t flags, EGLConfig config);
explicit GbmSurface(DrmGpu *gpu, const QSize &size, uint32_t format, QVector<uint64_t> modifiers, EGLConfig config);
explicit GbmSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, const QVector<uint64_t> &modifiers, uint32_t flags, gbm_surface *surface, EGLSurface eglSurface);
~GbmSurface();
bool makeContextCurrent() const;
@ -49,6 +48,14 @@ public:
int bufferAge() const;
QRegion repaintRegion() const;
enum class Error {
ModifiersUnsupported,
EglError,
Unknown
};
static std::variant<std::shared_ptr<GbmSurface>, Error> createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, uint32_t flags, EGLConfig config);
static std::variant<std::shared_ptr<GbmSurface>, Error> createSurface(EglGbmBackend *backend, const QSize &size, uint32_t format, QVector<uint64_t> modifiers, EGLConfig config);
private:
gbm_surface *m_surface;
EglGbmBackend *const m_eglBackend;

View file

@ -97,27 +97,32 @@ bool VirtualEglGbmLayer::createGbmSurface()
{
static bool modifiersEnvSet = false;
static const bool modifiersEnv = qEnvironmentVariableIntValue("KWIN_DRM_USE_MODIFIERS", &modifiersEnvSet) != 0;
const bool allowModifiers = ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv));
const auto tranches = m_eglBackend->dmabuf()->tranches();
for (const auto &tranche : tranches) {
for (auto it = tranche.formatTable.constBegin(); it != tranche.formatTable.constEnd(); it++) {
const auto size = m_output->pixelSize();
const auto config = m_eglBackend->config(it.key());
const auto format = it.key();
const auto modifiers = it.value();
const bool allowModifiers = m_eglBackend->gpu()->addFB2ModifiersSupported() && ((m_eglBackend->gpu()->isNVidia() && !modifiersEnvSet) || (modifiersEnvSet && modifiersEnv));
std::shared_ptr<GbmSurface> gbmSurface;
if (!allowModifiers) {
gbmSurface = std::make_shared<GbmSurface>(m_eglBackend->gpu(), size, it.key(), GBM_BO_USE_RENDERING, config);
} else {
gbmSurface = std::make_shared<GbmSurface>(m_eglBackend->gpu(), size, it.key(), it.value(), config);
if (allowModifiers && !modifiers.isEmpty()) {
const auto ret = GbmSurface::createSurface(m_eglBackend, size, format, modifiers, config);
if (const auto surface = std::get_if<std::shared_ptr<GbmSurface>>(&ret)) {
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = *surface;
return true;
} else if (std::get<GbmSurface::Error>(ret) != GbmSurface::Error::ModifiersUnsupported) {
continue;
}
}
if (!gbmSurface->isValid()) {
continue;
const auto ret = GbmSurface::createSurface(m_eglBackend, size, format, GBM_BO_USE_RENDERING, config);
if (const auto surface = std::get_if<std::shared_ptr<GbmSurface>>(&ret)) {
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = *surface;
return true;
}
m_oldGbmSurface = m_gbmSurface;
m_gbmSurface = gbmSurface;
return true;
}
}
return false;