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) , m_backend(drmBackend)
{ {
drmBackend->setRenderBackend(this); drmBackend->setRenderBackend(this);
setIsDirectRendering(true);
connect(m_backend, &DrmBackend::gpuRemoved, this, [this](DrmGpu *gpu) { connect(m_backend, &DrmBackend::gpuRemoved, this, [this](DrmGpu *gpu) {
m_contexts.erase(gpu->eglDisplay()); m_contexts.erase(gpu->eglDisplay());
}); });

View file

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

View file

@ -216,9 +216,6 @@ WaylandEglBackend::WaylandEglBackend(WaylandBackend *b)
, m_backend(b) , m_backend(b)
, m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(b->gbmDevice())) , 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::outputAdded, this, &WaylandEglBackend::createEglWaylandOutput);
connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) { connect(m_backend, &WaylandBackend::outputRemoved, this, [this](Output *output) {
m_outputs.erase(output); m_outputs.erase(output);

View file

@ -27,6 +27,7 @@ endif()
if (HAVE_GLX) if (HAVE_GLX)
target_sources(KWinX11Platform PRIVATE target_sources(KWinX11Platform PRIVATE
glxcontext.cpp
x11_standalone_glx_backend.cpp x11_standalone_glx_backend.cpp
x11_standalone_glx_context_attribute_builder.cpp x11_standalone_glx_context_attribute_builder.cpp
x11_standalone_glxconvenience.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_overlayWindow(std::make_unique<OverlayWindowX11>(backend))
, m_layer(std::make_unique<EglLayer>(this)) , m_layer(std::make_unique<EglLayer>(this))
{ {
setIsDirectRendering(true);
// There is no any way to determine when a buffer swap completes with EGL. Fallback // 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 // to software vblank events. Could we use the Present extension to get notified when
// the overlay window is actually presented on the screen? // the overlay window is actually presented on the screen?

View file

@ -14,6 +14,7 @@
// own // own
#include "x11_standalone_glx_backend.h" #include "x11_standalone_glx_backend.h"
#include "../common/kwinxrenderutils.h" #include "../common/kwinxrenderutils.h"
#include "glxcontext.h"
#include "utils/softwarevsyncmonitor.h" #include "utils/softwarevsyncmonitor.h"
#include "x11_standalone_backend.h" #include "x11_standalone_backend.h"
#include "x11_standalone_glx_context_attribute_builder.h" #include "x11_standalone_glx_context_attribute_builder.h"
@ -130,7 +131,6 @@ GlxBackend::GlxBackend(Display *display, X11StandaloneBackend *backend)
, window(None) , window(None)
, fbconfig(nullptr) , fbconfig(nullptr)
, glxWindow(None) , glxWindow(None)
, ctx(nullptr)
, m_bufferAge(0) , m_bufferAge(0)
, m_x11Display(display) , m_x11Display(display)
, m_backend(backend) , m_backend(backend)
@ -162,9 +162,7 @@ GlxBackend::~GlxBackend()
cleanupGL(); cleanupGL();
doneCurrent(); doneCurrent();
if (ctx) { m_context.reset();
glXDestroyContext(display(), ctx);
}
if (glxWindow) { if (glxWindow) {
glXDestroyWindow(display(), glxWindow); glXDestroyWindow(display(), glxWindow);
@ -218,7 +216,8 @@ void GlxBackend::init()
return; return;
} }
if (!initRenderingContext()) { m_context = GlxContext::create(this, fbconfig, glxWindow);
if (!m_context) {
setFailed(QStringLiteral("Could not initialize rendering context")); setFailed(QStringLiteral("Could not initialize rendering context"));
return; return;
} }
@ -323,10 +322,6 @@ void GlxBackend::init()
connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &GlxBackend::vblank); connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &GlxBackend::vblank);
} }
setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
qCDebug(KWIN_X11STANDALONE) << "Direct rendering:" << isDirectRendering();
} }
bool GlxBackend::checkVersion() bool GlxBackend::checkVersion()
@ -342,94 +337,6 @@ void GlxBackend::initExtensions()
setExtensions(string.split(' ')); 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() bool GlxBackend::initBuffer()
{ {
if (!initFbConfig()) { if (!initFbConfig()) {
@ -846,17 +753,12 @@ void GlxBackend::vblank(std::chrono::nanoseconds timestamp)
bool GlxBackend::makeCurrent() bool GlxBackend::makeCurrent()
{ {
if (QOpenGLContext *context = QOpenGLContext::currentContext()) { return m_context->makeCurrent();
// Workaround to tell Qt that no QOpenGLContext is current
context->doneCurrent();
}
const bool current = glXMakeCurrent(display(), glxWindow, ctx);
return current;
} }
void GlxBackend::doneCurrent() void GlxBackend::doneCurrent()
{ {
glXMakeCurrent(display(), None, nullptr); m_context->doneCurrent();
} }
OverlayWindow *GlxBackend::overlayWindow() const OverlayWindow *GlxBackend::overlayWindow() const

View file

@ -31,6 +31,7 @@ class VsyncMonitor;
class X11StandaloneBackend; class X11StandaloneBackend;
class GlxBackend; class GlxBackend;
class GLRenderTimeQuery; class GLRenderTimeQuery;
class GlxContext;
// GLX_MESA_swap_interval // GLX_MESA_swap_interval
using glXSwapIntervalMESA_func = int (*)(unsigned int interval); using glXSwapIntervalMESA_func = int (*)(unsigned int interval);
@ -105,7 +106,6 @@ private:
bool initBuffer(); bool initBuffer();
bool checkVersion(); bool checkVersion();
void initExtensions(); void initExtensions();
bool initRenderingContext();
bool initFbConfig(); bool initFbConfig();
void initVisualDepthHashTable(); void initVisualDepthHashTable();
void setSwapInterval(int interval); void setSwapInterval(int interval);
@ -121,7 +121,7 @@ private:
::Window window; ::Window window;
GLXFBConfig fbconfig; GLXFBConfig fbconfig;
GLXWindow glxWindow; GLXWindow glxWindow;
GLXContext ctx; std::unique_ptr<GlxContext> m_context;
QHash<xcb_visualid_t, FBConfigInfo> m_fbconfigHash; QHash<xcb_visualid_t, FBConfigInfo> m_fbconfigHash;
QHash<xcb_visualid_t, int> m_visualDepthHash; QHash<xcb_visualid_t, int> m_visualDepthHash;
std::unique_ptr<SwapEventFilter> m_swapEventFilter; 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_allocator(std::make_unique<GbmGraphicsBufferAllocator>(backend->gbmDevice()))
, m_backend(backend) , m_backend(backend)
{ {
setIsDirectRendering(true);
} }
X11WindowedEglBackend::~X11WindowedEglBackend() X11WindowedEglBackend::~X11WindowedEglBackend()

View file

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

View file

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

View file

@ -61,18 +61,6 @@ public:
{ {
return m_failed; 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 bool supportsBufferAge() const
{ {
@ -119,19 +107,6 @@ protected:
* @param reason The reason why the initialization failed. * @param reason The reason why the initialization failed.
*/ */
void setFailed(const QString &reason); 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) void setSupportsBufferAge(bool value)
{ {
@ -154,10 +129,6 @@ protected:
} }
private: 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. * @brief Whether the backend supports GLX_EXT_buffer_age / EGL_EXT_buffer_age.
*/ */