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:
parent
f2dd1b3471
commit
451947b282
13 changed files with 176 additions and 149 deletions
|
@ -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());
|
||||
});
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
131
src/backends/x11/standalone/glxcontext.cpp
Normal file
131
src/backends/x11/standalone/glxcontext.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
35
src/backends/x11/standalone/glxcontext.h
Normal file
35
src/backends/x11/standalone/glxcontext.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -190,7 +190,6 @@ X11WindowedEglBackend::X11WindowedEglBackend(X11WindowedBackend *backend)
|
|||
: m_allocator(std::make_unique<GbmGraphicsBufferAllocator>(backend->gbmDevice()))
|
||||
, m_backend(backend)
|
||||
{
|
||||
setIsDirectRendering(true);
|
||||
}
|
||||
|
||||
X11WindowedEglBackend::~X11WindowedEglBackend()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -20,8 +20,7 @@ namespace KWin
|
|||
{
|
||||
|
||||
OpenGLBackend::OpenGLBackend()
|
||||
: m_directRendering(false)
|
||||
, m_haveBufferAge(false)
|
||||
: m_haveBufferAge(false)
|
||||
, m_failed(false)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue