backends/x11/standalone: extract glx context into a separate class

This commit also drops most of the code around checks for direct rendering. If
direct rendering isn't supported, creating the OpenGL context will now simply fail,
achieving the same effect
This commit is contained in:
Xaver Hugl 2023-09-07 03:08:57 +02:00
parent f2dd1b3471
commit 451947b282
13 changed files with 176 additions and 149 deletions

View file

@ -33,7 +33,6 @@ EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend)
, m_backend(drmBackend)
{
drmBackend->setRenderBackend(this);
setIsDirectRendering(true);
connect(m_backend, &DrmBackend::gpuRemoved, this, [this](DrmGpu *gpu) {
m_contexts.erase(gpu->eglDisplay());
});

View file

@ -89,8 +89,6 @@ VirtualEglBackend::VirtualEglBackend(VirtualBackend *b)
, m_backend(b)
, m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice()))
{
// Egl is always direct rendering
setIsDirectRendering(true);
}
VirtualEglBackend::~VirtualEglBackend()

View file

@ -216,9 +216,6 @@ WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
, m_backend(b)
, m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice()))
{
// Egl is always direct rendering
setIsDirectRendering(true);
connect(m_backend, &WaylandBackend::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
m_outputs.erase(output);

View file

@ -27,6 +27,7 @@ endif()
if (HAVE_GLX)
target_sources(KWinX11Platform PRIVATE
glxcontext.cpp
x11_standalone_glx_backend.cpp
x11_standalone_glx_context_attribute_builder.cpp
x11_standalone_glxconvenience.cpp

View file

@ -0,0 +1,131 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "glxcontext.h"
#include "x11_standalone_glx_context_attribute_builder.h"
#include "x11_standalone_logging.h"
#include <QDebug>
#include <QOpenGLContext>
namespace KWin
{
GlxContext::GlxContext(Display *display, GLXWindow window, GLXContext handle)
: m_display(display)
, m_window(window)
, m_handle(handle)
{
}
GlxContext::~GlxContext()
{
glXDestroyContext(m_display, m_handle);
}
bool GlxContext::makeCurrent() const
{
if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
// Workaround to tell Qt that no QOpenGLContext is current
context->doneCurrent();
}
return glXMakeCurrent(m_display, m_window, m_handle);
}
bool GlxContext::doneCurrent() const
{
return glXMakeCurrent(m_display, None, nullptr);
}
std::unique_ptr<GlxContext> GlxContext::create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow)
{
QOpenGLContext *qtGlobalShareContext = QOpenGLContext::globalShareContext();
GLXContext globalShareContext = nullptr;
if (qtGlobalShareContext) {
qDebug(KWIN_X11STANDALONE) << "Global share context format:" << qtGlobalShareContext->format();
const auto nativeHandle = qtGlobalShareContext->nativeInterface<QNativeInterface::QGLXContext>();
if (nativeHandle) {
globalShareContext = nativeHandle->nativeContext();
} else {
qCDebug(KWIN_X11STANDALONE) << "Invalid QOpenGLContext::globalShareContext()";
return nullptr;
}
}
if (!globalShareContext) {
qCWarning(KWIN_X11STANDALONE) << "QOpenGLContext::globalShareContext() is required";
return nullptr;
}
GLXContext handle = nullptr;
// Use glXCreateContextAttribsARB() when it's available
if (backend->hasExtension(QByteArrayLiteral("GLX_ARB_create_context"))) {
const bool have_robustness = backend->hasExtension(QByteArrayLiteral("GLX_ARB_create_context_robustness"));
const bool haveVideoMemoryPurge = backend->hasExtension(QByteArrayLiteral("GLX_NV_robustness_video_memory_purge"));
std::vector<GlxContextAttributeBuilder> candidates;
// core
if (have_robustness) {
if (haveVideoMemoryPurge) {
GlxContextAttributeBuilder purgeMemoryCore;
purgeMemoryCore.setVersion(3, 1);
purgeMemoryCore.setRobust(true);
purgeMemoryCore.setResetOnVideoMemoryPurge(true);
candidates.emplace_back(std::move(purgeMemoryCore));
}
GlxContextAttributeBuilder robustCore;
robustCore.setVersion(3, 1);
robustCore.setRobust(true);
candidates.emplace_back(std::move(robustCore));
}
GlxContextAttributeBuilder core;
core.setVersion(3, 1);
candidates.emplace_back(std::move(core));
// legacy
if (have_robustness) {
if (haveVideoMemoryPurge) {
GlxContextAttributeBuilder purgeMemoryLegacy;
purgeMemoryLegacy.setRobust(true);
purgeMemoryLegacy.setResetOnVideoMemoryPurge(true);
candidates.emplace_back(std::move(purgeMemoryLegacy));
}
GlxContextAttributeBuilder robustLegacy;
robustLegacy.setRobust(true);
candidates.emplace_back(std::move(robustLegacy));
}
GlxContextAttributeBuilder legacy;
legacy.setVersion(2, 1);
candidates.emplace_back(std::move(legacy));
for (auto it = candidates.begin(); it != candidates.end(); it++) {
const auto attribs = it->build();
handle = glXCreateContextAttribsARB(backend->display(), fbconfig, globalShareContext, true, attribs.data());
if (handle) {
qCDebug(KWIN_X11STANDALONE) << "Created GLX context with attributes:" << &(*it);
break;
}
}
}
if (!handle) {
handle = glXCreateNewContext(backend->display(), fbconfig, GLX_RGBA_TYPE, globalShareContext, true);
}
if (!handle) {
qCDebug(KWIN_X11STANDALONE) << "Failed to create an OpenGL context.";
return nullptr;
}
// KWin doesn't support indirect rendering
if (!glXIsDirect(backend->display(), handle)) {
return nullptr;
}
if (!glXMakeCurrent(backend->display(), glxWindow, handle)) {
glXDestroyContext(backend->display(), handle);
return nullptr;
}
return std::make_unique<GlxContext>(backend->display(), glxWindow, handle);
}
}

View file

@ -0,0 +1,35 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "libkwineffects/openglcontext.h"
#include "x11_standalone_glx_backend.h"
#include <epoxy/glx.h>
namespace KWin
{
class GlxContext : public OpenGlContext
{
public:
GlxContext(Display *display, GLXWindow window, GLXContext handle);
~GlxContext() override;
bool makeCurrent() const;
bool doneCurrent() const;
static std::unique_ptr<GlxContext> create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow);
private:
Display *const m_display;
const GLXWindow m_window;
const GLXContext m_handle;
};
}

View file

@ -59,8 +59,6 @@ EglBackend::EglBackend(Display *display, X11StandaloneBackend *backend)
, m_overlayWindow(std::make_unique<OverlayWindowX11>(backend))
, m_layer(std::make_unique<EglLayer>(this))
{
setIsDirectRendering(true);
// There is no any way to determine when a buffer swap completes with EGL. Fallback
// to software vblank events. Could we use the Present extension to get notified when
// the overlay window is actually presented on the screen?

View file

@ -14,6 +14,7 @@
// own
#include "x11_standalone_glx_backend.h"
#include "../common/kwinxrenderutils.h"
#include "glxcontext.h"
#include "utils/softwarevsyncmonitor.h"
#include "x11_standalone_backend.h"
#include "x11_standalone_glx_context_attribute_builder.h"
@ -130,7 +131,6 @@ GlxBackend::GlxBackend(Display *display, X11StandaloneBackend *backend)
, window(None)
, fbconfig(nullptr)
, glxWindow(None)
, ctx(nullptr)
, m_bufferAge(0)
, m_x11Display(display)
, m_backend(backend)
@ -162,9 +162,7 @@ GlxBackend::~GlxBackend()
cleanupGL();
doneCurrent();
if (ctx) {
glXDestroyContext(display(), ctx);
}
m_context.reset();
if (glxWindow) {
glXDestroyWindow(display(), glxWindow);
@ -218,7 +216,8 @@ void GlxBackend::init()
return;
}
if (!initRenderingContext()) {
m_context = GlxContext::create(this, fbconfig, glxWindow);
if (!m_context) {
setFailed(QStringLiteral("Could not initialize rendering context"));
return;
}
@ -323,10 +322,6 @@ void GlxBackend::init()
connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &GlxBackend::vblank);
}
setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
qCDebug(KWIN_X11STANDALONE) << "Direct rendering:" << isDirectRendering();
}
bool GlxBackend::checkVersion()
@ -342,94 +337,6 @@ void GlxBackend::initExtensions()
setExtensions(string.split(' '));
}
bool GlxBackend::initRenderingContext()
{
const bool direct = true;
QOpenGLContext *qtGlobalShareContext = QOpenGLContext::globalShareContext();
GLXContext globalShareContext = nullptr;
if (qtGlobalShareContext) {
qDebug(KWIN_X11STANDALONE) << "Global share context format:" << qtGlobalShareContext->format();
const auto nativeHandle = qtGlobalShareContext->nativeInterface<QNativeInterface::QGLXContext>();
if (nativeHandle) {
globalShareContext = nativeHandle->nativeContext();
} else {
qCDebug(KWIN_X11STANDALONE) << "Invalid QOpenGLContext::globalShareContext()";
return false;
}
}
if (!globalShareContext) {
qCWarning(KWIN_X11STANDALONE) << "QOpenGLContext::globalShareContext() is required";
return false;
}
// Use glXCreateContextAttribsARB() when it's available
if (hasExtension(QByteArrayLiteral("GLX_ARB_create_context"))) {
const bool have_robustness = hasExtension(QByteArrayLiteral("GLX_ARB_create_context_robustness"));
const bool haveVideoMemoryPurge = hasExtension(QByteArrayLiteral("GLX_NV_robustness_video_memory_purge"));
std::vector<GlxContextAttributeBuilder> candidates;
// core
if (have_robustness) {
if (haveVideoMemoryPurge) {
GlxContextAttributeBuilder purgeMemoryCore;
purgeMemoryCore.setVersion(3, 1);
purgeMemoryCore.setRobust(true);
purgeMemoryCore.setResetOnVideoMemoryPurge(true);
candidates.emplace_back(std::move(purgeMemoryCore));
}
GlxContextAttributeBuilder robustCore;
robustCore.setVersion(3, 1);
robustCore.setRobust(true);
candidates.emplace_back(std::move(robustCore));
}
GlxContextAttributeBuilder core;
core.setVersion(3, 1);
candidates.emplace_back(std::move(core));
// legacy
if (have_robustness) {
if (haveVideoMemoryPurge) {
GlxContextAttributeBuilder purgeMemoryLegacy;
purgeMemoryLegacy.setRobust(true);
purgeMemoryLegacy.setResetOnVideoMemoryPurge(true);
candidates.emplace_back(std::move(purgeMemoryLegacy));
}
GlxContextAttributeBuilder robustLegacy;
robustLegacy.setRobust(true);
candidates.emplace_back(std::move(robustLegacy));
}
GlxContextAttributeBuilder legacy;
legacy.setVersion(2, 1);
candidates.emplace_back(std::move(legacy));
for (auto it = candidates.begin(); it != candidates.end(); it++) {
const auto attribs = it->build();
ctx = glXCreateContextAttribsARB(display(), fbconfig, globalShareContext, true, attribs.data());
if (ctx) {
qCDebug(KWIN_X11STANDALONE) << "Created GLX context with attributes:" << &(*it);
break;
}
}
}
if (!ctx) {
ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, globalShareContext, direct);
}
if (!ctx) {
qCDebug(KWIN_X11STANDALONE) << "Failed to create an OpenGL context.";
return false;
}
if (!glXMakeCurrent(display(), glxWindow, ctx)) {
qCDebug(KWIN_X11STANDALONE) << "Failed to make the OpenGL context current.";
glXDestroyContext(display(), ctx);
ctx = nullptr;
return false;
}
return true;
}
bool GlxBackend::initBuffer()
{
if (!initFbConfig()) {
@ -846,17 +753,12 @@ void GlxBackend::vblank(std::chrono::nanoseconds timestamp)
bool GlxBackend::makeCurrent()
{
if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
// Workaround to tell Qt that no QOpenGLContext is current
context->doneCurrent();
}
const bool current = glXMakeCurrent(display(), glxWindow, ctx);
return current;
return m_context->makeCurrent();
}
void GlxBackend::doneCurrent()
{
glXMakeCurrent(display(), None, nullptr);
m_context->doneCurrent();
}
OverlayWindow *GlxBackend::overlayWindow() const

View file

@ -31,6 +31,7 @@ class VsyncMonitor;
class X11StandaloneBackend;
class GlxBackend;
class GLRenderTimeQuery;
class GlxContext;
// GLX_MESA_swap_interval
using glXSwapIntervalMESA_func = int (*)(unsigned int interval);
@ -105,7 +106,6 @@ private:
bool initBuffer();
bool checkVersion();
void initExtensions();
bool initRenderingContext();
bool initFbConfig();
void initVisualDepthHashTable();
void setSwapInterval(int interval);
@ -121,7 +121,7 @@ private:
::Window window;
GLXFBConfig fbconfig;
GLXWindow glxWindow;
GLXContext ctx;
std::unique_ptr<GlxContext> m_context;
QHash<xcb_visualid_t, FBConfigInfo> m_fbconfigHash;
QHash<xcb_visualid_t, int> m_visualDepthHash;
std::unique_ptr<SwapEventFilter> m_swapEventFilter;

View file

@ -190,7 +190,6 @@ X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend)
: m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(backend->gbmDevice()))
, m_backend(backend)
{
setIsDirectRendering(true);
}
X11WindowedEglBackend::~X11WindowedEglBackend()

View file

@ -189,9 +189,6 @@ bool Compositor::attemptOpenGLCompositing()
return false;
}
} else {
if (!backend->isDirectRendering()) {
return false;
}
if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
return false;

View file

@ -20,8 +20,7 @@ namespace KWin
{
OpenGLBackend::OpenGLBackend()
: m_directRendering(false)
, m_haveBufferAge(false)
: m_haveBufferAge(false)
, m_failed(false)
{
}

View file

@ -61,18 +61,6 @@ public:
{
return m_failed;
}
/**
* @brief Whether the backend uses direct rendering.
*
* Some OpenGLScene modes require direct rendering. E.g. the OpenGL 2 should not be used
* if direct rendering is not supported by the Scene.
*
* @return bool @c true if the GL context is direct, @c false if indirect
*/
bool isDirectRendering() const
{
return m_directRendering;
}
bool supportsBufferAge() const
{
@ -119,19 +107,6 @@ protected:
* @param reason The reason why the initialization failed.
*/
void setFailed(const QString &reason);
/**
* @brief Sets whether the OpenGL context is direct.
*
* Should be called by the concrete subclass once it is determined whether the OpenGL context is
* direct or indirect.
* If the subclass does not call this method, the backend defaults to @c false.
*
* @param direct @c true if the OpenGL context is direct, @c false if indirect
*/
void setIsDirectRendering(bool direct)
{
m_directRendering = direct;
}
void setSupportsBufferAge(bool value)
{
@ -154,10 +129,6 @@ protected:
}
private:
/**
* @brief Whether direct rendering is used, defaults to @c false.
*/
bool m_directRendering;
/**
* @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age.
*/