From 451947b2823bd444bfa5cf9f3ee0e4ed0b3dc77b Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Thu, 7 Sep 2023 03:08:57 +0200 Subject: [PATCH] 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 --- src/backends/drm/drm_egl_backend.cpp | 1 - src/backends/virtual/virtual_egl_backend.cpp | 2 - src/backends/wayland/wayland_egl_backend.cpp | 3 - src/backends/x11/standalone/CMakeLists.txt | 1 + src/backends/x11/standalone/glxcontext.cpp | 131 ++++++++++++++++++ src/backends/x11/standalone/glxcontext.h | 35 +++++ .../standalone/x11_standalone_egl_backend.cpp | 2 - .../standalone/x11_standalone_glx_backend.cpp | 110 +-------------- .../standalone/x11_standalone_glx_backend.h | 4 +- .../x11/windowed/x11_windowed_egl_backend.cpp | 1 - src/composite.cpp | 3 - .../scenes/opengl/openglbackend.cpp | 3 +- .../scenes/opengl/openglbackend.h | 29 ---- 13 files changed, 176 insertions(+), 149 deletions(-) create mode 100644 src/backends/x11/standalone/glxcontext.cpp create mode 100644 src/backends/x11/standalone/glxcontext.h diff --git a/src/backends/drm/drm_egl_backend.cpp b/src/backends/drm/drm_egl_backend.cpp index 3f75765711..041a13a857 100644 --- a/src/backends/drm/drm_egl_backend.cpp +++ b/src/backends/drm/drm_egl_backend.cpp @@ -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()); }); diff --git a/src/backends/virtual/virtual_egl_backend.cpp b/src/backends/virtual/virtual_egl_backend.cpp index 88ce395cf2..4d03f6b5af 100644 --- a/src/backends/virtual/virtual_egl_backend.cpp +++ b/src/backends/virtual/virtual_egl_backend.cpp @@ -89,8 +89,6 @@ VirtualEglBackend::VirtualEglBackend(VirtualBackend *b) , m_backend(b) , m_allocator(std::make_unique(b->gbmDevice())) { - // Egl is always direct rendering - setIsDirectRendering(true); } VirtualEglBackend::~VirtualEglBackend() diff --git a/src/backends/wayland/wayland_egl_backend.cpp b/src/backends/wayland/wayland_egl_backend.cpp index c6d0db3897..ab2c5eaf48 100644 --- a/src/backends/wayland/wayland_egl_backend.cpp +++ b/src/backends/wayland/wayland_egl_backend.cpp @@ -216,9 +216,6 @@ WaylandEglBackend::WaylandEglBackend(WaylandBackend *b) , m_backend(b) , m_allocator(std::make_unique(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); diff --git a/src/backends/x11/standalone/CMakeLists.txt b/src/backends/x11/standalone/CMakeLists.txt index 65b0b8bc96..97fa5e44bc 100644 --- a/src/backends/x11/standalone/CMakeLists.txt +++ b/src/backends/x11/standalone/CMakeLists.txt @@ -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 diff --git a/src/backends/x11/standalone/glxcontext.cpp b/src/backends/x11/standalone/glxcontext.cpp new file mode 100644 index 0000000000..c3cce456c8 --- /dev/null +++ b/src/backends/x11/standalone/glxcontext.cpp @@ -0,0 +1,131 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2023 Xaver Hugl + + 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 +#include + +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::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(); + 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 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(backend->display(), glxWindow, handle); +} + +} diff --git a/src/backends/x11/standalone/glxcontext.h b/src/backends/x11/standalone/glxcontext.h new file mode 100644 index 0000000000..d1b4da31cd --- /dev/null +++ b/src/backends/x11/standalone/glxcontext.h @@ -0,0 +1,35 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2023 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once +#include "libkwineffects/openglcontext.h" +#include "x11_standalone_glx_backend.h" + +#include + +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 create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow); + +private: + Display *const m_display; + const GLXWindow m_window; + const GLXContext m_handle; +}; + +} diff --git a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp index 1c33cce931..22d32e01d5 100644 --- a/src/backends/x11/standalone/x11_standalone_egl_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_egl_backend.cpp @@ -59,8 +59,6 @@ EglBackend::EglBackend(Display *display, X11StandaloneBackend *backend) , m_overlayWindow(std::make_unique(backend)) , m_layer(std::make_unique(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? diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp index dbc778aeec..36609d6ad9 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.cpp +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.cpp @@ -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(); - 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 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 diff --git a/src/backends/x11/standalone/x11_standalone_glx_backend.h b/src/backends/x11/standalone/x11_standalone_glx_backend.h index b8a58e7c77..6283e261f4 100644 --- a/src/backends/x11/standalone/x11_standalone_glx_backend.h +++ b/src/backends/x11/standalone/x11_standalone_glx_backend.h @@ -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 m_context; QHash m_fbconfigHash; QHash m_visualDepthHash; std::unique_ptr m_swapEventFilter; diff --git a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp index 04a2b5c40b..0d1b16dde3 100644 --- a/src/backends/x11/windowed/x11_windowed_egl_backend.cpp +++ b/src/backends/x11/windowed/x11_windowed_egl_backend.cpp @@ -190,7 +190,6 @@ X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend) : m_allocator(std::make_unique(backend->gbmDevice())) , m_backend(backend) { - setIsDirectRendering(true); } X11WindowedEglBackend::~X11WindowedEglBackend() diff --git a/src/composite.cpp b/src/composite.cpp index ac781c6405..d69f90381c 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -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; diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp index ed3617d40f..f305c2d1db 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.cpp +++ b/src/platformsupport/scenes/opengl/openglbackend.cpp @@ -20,8 +20,7 @@ namespace KWin { OpenGLBackend::OpenGLBackend() - : m_directRendering(false) - , m_haveBufferAge(false) + : m_haveBufferAge(false) , m_failed(false) { } diff --git a/src/platformsupport/scenes/opengl/openglbackend.h b/src/platformsupport/scenes/opengl/openglbackend.h index a582a3d886..3318f4da18 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.h +++ b/src/platformsupport/scenes/opengl/openglbackend.h @@ -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. */