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:
parent
a264938ff3
commit
f02a6fd128
4 changed files with 91 additions and 76 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue