File indexing completed on 2024-11-10 04:56:34

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "glxcontext.h"
0010 #include "x11_standalone_glx_context_attribute_builder.h"
0011 #include "x11_standalone_logging.h"
0012 
0013 #include <QDebug>
0014 #include <QOpenGLContext>
0015 
0016 namespace KWin
0017 {
0018 
0019 GlxContext::GlxContext(::Display *display, GLXWindow window, GLXContext handle)
0020     : m_display(display)
0021     , m_window(window)
0022     , m_handle(handle)
0023 {
0024     // It is not legal to not have a vertex array object bound in a core context
0025     // to make code handling old and new OpenGL versions easier, bind a dummy vao that's used for everything
0026     if (!isOpenglES() && hasOpenglExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
0027         glGenVertexArrays(1, &m_vao);
0028         glBindVertexArray(m_vao);
0029     }
0030 }
0031 
0032 GlxContext::~GlxContext()
0033 {
0034     if (m_vao) {
0035         makeCurrent();
0036         glDeleteVertexArrays(1, &m_vao);
0037     }
0038     glXDestroyContext(m_display, m_handle);
0039 }
0040 
0041 bool GlxContext::makeCurrent() const
0042 {
0043     if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
0044         // Workaround to tell Qt that no QOpenGLContext is current
0045         context->doneCurrent();
0046     }
0047     return glXMakeCurrent(m_display, m_window, m_handle);
0048 }
0049 
0050 bool GlxContext::doneCurrent() const
0051 {
0052     return glXMakeCurrent(m_display, None, nullptr);
0053 }
0054 
0055 std::unique_ptr<GlxContext> GlxContext::create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow)
0056 {
0057     QOpenGLContext *qtGlobalShareContext = QOpenGLContext::globalShareContext();
0058     GLXContext globalShareContext = nullptr;
0059     if (qtGlobalShareContext) {
0060         qDebug(KWIN_X11STANDALONE) << "Global share context format:" << qtGlobalShareContext->format();
0061         const auto nativeHandle = qtGlobalShareContext->nativeInterface<QNativeInterface::QGLXContext>();
0062         if (nativeHandle) {
0063             globalShareContext = nativeHandle->nativeContext();
0064         } else {
0065             qCDebug(KWIN_X11STANDALONE) << "Invalid QOpenGLContext::globalShareContext()";
0066             return nullptr;
0067         }
0068     }
0069     if (!globalShareContext) {
0070         qCWarning(KWIN_X11STANDALONE) << "QOpenGLContext::globalShareContext() is required";
0071         return nullptr;
0072     }
0073 
0074     GLXContext handle = nullptr;
0075 
0076     // Use glXCreateContextAttribsARB() when it's available
0077     if (backend->hasExtension(QByteArrayLiteral("GLX_ARB_create_context"))) {
0078         const bool have_robustness = backend->hasExtension(QByteArrayLiteral("GLX_ARB_create_context_robustness"));
0079         const bool haveVideoMemoryPurge = backend->hasExtension(QByteArrayLiteral("GLX_NV_robustness_video_memory_purge"));
0080 
0081         std::vector<GlxContextAttributeBuilder> candidates;
0082         // core
0083         if (have_robustness) {
0084             if (haveVideoMemoryPurge) {
0085                 GlxContextAttributeBuilder purgeMemoryCore;
0086                 purgeMemoryCore.setVersion(3, 1);
0087                 purgeMemoryCore.setRobust(true);
0088                 purgeMemoryCore.setResetOnVideoMemoryPurge(true);
0089                 candidates.emplace_back(std::move(purgeMemoryCore));
0090             }
0091             GlxContextAttributeBuilder robustCore;
0092             robustCore.setVersion(3, 1);
0093             robustCore.setRobust(true);
0094             candidates.emplace_back(std::move(robustCore));
0095         }
0096         GlxContextAttributeBuilder core;
0097         core.setVersion(3, 1);
0098         candidates.emplace_back(std::move(core));
0099         // legacy
0100         if (have_robustness) {
0101             if (haveVideoMemoryPurge) {
0102                 GlxContextAttributeBuilder purgeMemoryLegacy;
0103                 purgeMemoryLegacy.setRobust(true);
0104                 purgeMemoryLegacy.setResetOnVideoMemoryPurge(true);
0105                 candidates.emplace_back(std::move(purgeMemoryLegacy));
0106             }
0107             GlxContextAttributeBuilder robustLegacy;
0108             robustLegacy.setRobust(true);
0109             candidates.emplace_back(std::move(robustLegacy));
0110         }
0111         GlxContextAttributeBuilder legacy;
0112         legacy.setVersion(2, 1);
0113         candidates.emplace_back(std::move(legacy));
0114         for (auto it = candidates.begin(); it != candidates.end(); it++) {
0115             const auto attribs = it->build();
0116             handle = glXCreateContextAttribsARB(backend->display(), fbconfig, globalShareContext, true, attribs.data());
0117             if (handle) {
0118                 qCDebug(KWIN_X11STANDALONE) << "Created GLX context with attributes:" << &(*it);
0119                 break;
0120             }
0121         }
0122     }
0123     if (!handle) {
0124         handle = glXCreateNewContext(backend->display(), fbconfig, GLX_RGBA_TYPE, globalShareContext, true);
0125     }
0126     if (!handle) {
0127         qCDebug(KWIN_X11STANDALONE) << "Failed to create an OpenGL context.";
0128         return nullptr;
0129     }
0130     // KWin doesn't support indirect rendering
0131     if (!glXIsDirect(backend->display(), handle)) {
0132         return nullptr;
0133     }
0134     if (!glXMakeCurrent(backend->display(), glxWindow, handle)) {
0135         glXDestroyContext(backend->display(), handle);
0136         return nullptr;
0137     }
0138     auto ret = std::make_unique<GlxContext>(backend->display(), glxWindow, handle);
0139     if (!ret->checkSupported()) {
0140         return nullptr;
0141     }
0142     return ret;
0143 }
0144 
0145 }