File indexing completed on 2025-04-27 11:33:00

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0006     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0007 
0008     Based on glcompmgr code by Felix Bellaby.
0009     Using code from Compiz and Beryl.
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 */
0013 
0014 // own
0015 #include "x11_standalone_glx_backend.h"
0016 #include "../common/kwinxrenderutils.h"
0017 #include "softwarevsyncmonitor.h"
0018 #include "x11_standalone_backend.h"
0019 #include "x11_standalone_glx_context_attribute_builder.h"
0020 #include "x11_standalone_glxconvenience.h"
0021 #include "x11_standalone_logging.h"
0022 #include "x11_standalone_omlsynccontrolvsyncmonitor.h"
0023 #include "x11_standalone_overlaywindow.h"
0024 #include "x11_standalone_sgivideosyncvsyncmonitor.h"
0025 // kwin
0026 #include "composite.h"
0027 #include "core/outputbackend.h"
0028 #include "core/overlaywindow.h"
0029 #include "core/renderloop_p.h"
0030 #include "options.h"
0031 #include "scene/surfaceitem_x11.h"
0032 #include "scene/workspacescene.h"
0033 #include "utils/xcbutils.h"
0034 #include "workspace.h"
0035 // kwin libs
0036 #include <kwinglplatform.h>
0037 #include <kwinglutils.h>
0038 #include <kwinoffscreenquickview.h>
0039 // Qt
0040 #include <QDebug>
0041 #include <QOpenGLContext>
0042 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0043 #include <private/qtx11extras_p.h>
0044 #else
0045 #include <QX11Info>
0046 #endif
0047 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0048 #include <QtPlatformHeaders/QGLXNativeContext>
0049 #endif
0050 // system
0051 #include <unistd.h>
0052 
0053 #include <algorithm>
0054 #include <deque>
0055 #if HAVE_DL_LIBRARY
0056 #include <dlfcn.h>
0057 #endif
0058 
0059 #ifndef XCB_GLX_BUFFER_SWAP_COMPLETE
0060 #define XCB_GLX_BUFFER_SWAP_COMPLETE 1
0061 typedef struct xcb_glx_buffer_swap_complete_event_t
0062 {
0063     uint8_t response_type; /**<  */
0064     uint8_t pad0; /**<  */
0065     uint16_t sequence; /**<  */
0066     uint16_t event_type; /**<  */
0067     uint8_t pad1[2]; /**<  */
0068     xcb_glx_drawable_t drawable; /**<  */
0069     uint32_t ust_hi; /**<  */
0070     uint32_t ust_lo; /**<  */
0071     uint32_t msc_hi; /**<  */
0072     uint32_t msc_lo; /**<  */
0073     uint32_t sbc; /**<  */
0074 } xcb_glx_buffer_swap_complete_event_t;
0075 #endif
0076 
0077 #include <tuple>
0078 
0079 namespace KWin
0080 {
0081 
0082 SwapEventFilter::SwapEventFilter(xcb_drawable_t drawable, xcb_glx_drawable_t glxDrawable)
0083     : X11EventFilter(Xcb::Extensions::self()->glxEventBase() + XCB_GLX_BUFFER_SWAP_COMPLETE)
0084     , m_drawable(drawable)
0085     , m_glxDrawable(glxDrawable)
0086 {
0087 }
0088 
0089 bool SwapEventFilter::event(xcb_generic_event_t *event)
0090 {
0091     const xcb_glx_buffer_swap_complete_event_t *swapEvent =
0092         reinterpret_cast<xcb_glx_buffer_swap_complete_event_t *>(event);
0093     if (swapEvent->drawable != m_drawable && swapEvent->drawable != m_glxDrawable) {
0094         return false;
0095     }
0096 
0097     // The clock for the UST timestamp is left unspecified in the spec, however, usually,
0098     // it's CLOCK_MONOTONIC, so no special conversions are needed.
0099     const std::chrono::microseconds timestamp((uint64_t(swapEvent->ust_hi) << 32) | swapEvent->ust_lo);
0100 
0101     const auto platform = static_cast<X11StandaloneBackend *>(kwinApp()->outputBackend());
0102     RenderLoopPrivate::get(platform->renderLoop())->notifyFrameCompleted(timestamp);
0103 
0104     return true;
0105 }
0106 
0107 GlxLayer::GlxLayer(GlxBackend *backend)
0108     : m_backend(backend)
0109 {
0110 }
0111 
0112 std::optional<OutputLayerBeginFrameInfo> GlxLayer::beginFrame()
0113 {
0114     return m_backend->beginFrame();
0115 }
0116 
0117 bool GlxLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0118 {
0119     m_backend->endFrame(renderedRegion, damagedRegion);
0120     return true;
0121 }
0122 
0123 GlxBackend::GlxBackend(Display *display, X11StandaloneBackend *backend)
0124     : OpenGLBackend()
0125     , m_overlayWindow(std::make_unique<OverlayWindowX11>())
0126     , window(None)
0127     , fbconfig(nullptr)
0128     , glxWindow(None)
0129     , ctx(nullptr)
0130     , m_bufferAge(0)
0131     , m_x11Display(display)
0132     , m_backend(backend)
0133     , m_layer(std::make_unique<GlxLayer>(this))
0134 {
0135     // Force initialization of GLX integration in the Qt's xcb backend
0136     // to make it call XESetWireToEvent callbacks, which is required
0137     // by Mesa when using DRI2.
0138     QOpenGLContext::supportsThreadedOpenGL();
0139 
0140     connect(workspace(), &Workspace::geometryChanged, this, &GlxBackend::screenGeometryChanged);
0141 }
0142 
0143 GlxBackend::~GlxBackend()
0144 {
0145     m_vsyncMonitor.reset();
0146     // No completion events will be received for in-flight frames, this may lock the
0147     // render loop. We need to ensure that the render loop is back to its initial state
0148     // if the render backend is about to be destroyed.
0149     RenderLoopPrivate::get(m_backend->renderLoop())->invalidate();
0150 
0151     if (isFailed()) {
0152         m_overlayWindow->destroy();
0153     }
0154     // TODO: cleanup in error case
0155     // do cleanup after initBuffer()
0156     cleanupGL();
0157     doneCurrent();
0158 
0159     if (ctx) {
0160         glXDestroyContext(display(), ctx);
0161     }
0162 
0163     if (glxWindow) {
0164         glXDestroyWindow(display(), glxWindow);
0165     }
0166 
0167     if (window) {
0168         XDestroyWindow(display(), window);
0169     }
0170 
0171     m_overlayWindow->destroy();
0172 }
0173 
0174 typedef void (*glXFuncPtr)();
0175 
0176 static glXFuncPtr getProcAddress(const char *name)
0177 {
0178     glXFuncPtr ret = nullptr;
0179 #if HAVE_EPOXY_GLX
0180     ret = glXGetProcAddress((const GLubyte *)name);
0181 #endif
0182 #if HAVE_DL_LIBRARY
0183     if (ret == nullptr) {
0184         ret = (glXFuncPtr)dlsym(RTLD_DEFAULT, name);
0185     }
0186 #endif
0187     return ret;
0188 }
0189 glXSwapIntervalMESA_func glXSwapIntervalMESA;
0190 
0191 void GlxBackend::init()
0192 {
0193     // Require at least GLX 1.3
0194     if (!checkVersion()) {
0195         setFailed(QStringLiteral("Requires at least GLX 1.3"));
0196         return;
0197     }
0198 
0199     initExtensions();
0200 
0201     // resolve glXSwapIntervalMESA if available
0202     if (hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"))) {
0203         glXSwapIntervalMESA = (glXSwapIntervalMESA_func)getProcAddress("glXSwapIntervalMESA");
0204     } else {
0205         glXSwapIntervalMESA = nullptr;
0206     }
0207 
0208     initVisualDepthHashTable();
0209 
0210     if (!initBuffer()) {
0211         setFailed(QStringLiteral("Could not initialize the buffer"));
0212         return;
0213     }
0214 
0215     if (!initRenderingContext()) {
0216         setFailed(QStringLiteral("Could not initialize rendering context"));
0217         return;
0218     }
0219 
0220     // Initialize OpenGL
0221     GLPlatform *glPlatform = GLPlatform::instance();
0222     glPlatform->detect(GlxPlatformInterface);
0223     options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting
0224     if (options->glPreferBufferSwap() == Options::AutoSwapStrategy) {
0225         options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen
0226     }
0227     glPlatform->printResults();
0228     initGL(&getProcAddress);
0229 
0230     m_fbo = std::make_unique<GLFramebuffer>(0, workspace()->geometry().size());
0231 
0232     bool supportsSwapEvent = false;
0233 
0234     if (hasExtension(QByteArrayLiteral("GLX_INTEL_swap_event"))) {
0235         if (qEnvironmentVariableIsSet("KWIN_USE_INTEL_SWAP_EVENT")) {
0236             supportsSwapEvent = qEnvironmentVariable("KWIN_USE_INTEL_SWAP_EVENT") != QLatin1String("0");
0237         } else {
0238             // Don't use swap events on Intel. The issue with Intel GPUs is that they are not as
0239             // powerful as discrete GPUs. Therefore, it's better to push frames as often as vblank
0240             // notifications are received. This, however, may increase latency. If the swap events
0241             // are enabled explicitly by setting the environment variable, honor that choice.
0242             supportsSwapEvent = !glPlatform->isIntel();
0243         }
0244     }
0245 
0246     // Check whether certain features are supported
0247     m_haveMESACopySubBuffer = hasExtension(QByteArrayLiteral("GLX_MESA_copy_sub_buffer"));
0248     m_haveMESASwapControl = hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"));
0249     m_haveEXTSwapControl = hasExtension(QByteArrayLiteral("GLX_EXT_swap_control"));
0250     m_haveSGISwapControl = hasExtension(QByteArrayLiteral("GLX_SGI_swap_control"));
0251 
0252     bool haveSwapInterval = m_haveMESASwapControl || m_haveEXTSwapControl || m_haveSGISwapControl;
0253 
0254     setSupportsBufferAge(false);
0255 
0256     if (hasExtension(QByteArrayLiteral("GLX_EXT_buffer_age"))) {
0257         const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
0258 
0259         if (useBufferAge != "0") {
0260             setSupportsBufferAge(true);
0261         }
0262     }
0263 
0264     // If the buffer age extension is unsupported, glXSwapBuffers() is not guaranteed to
0265     // be called. Therefore, there is no point for creating the swap event filter.
0266     if (!supportsBufferAge()) {
0267         supportsSwapEvent = false;
0268     }
0269 
0270     static bool syncToVblankDisabled = qEnvironmentVariableIsSet("KWIN_X11_NO_SYNC_TO_VBLANK");
0271     if (!syncToVblankDisabled) {
0272         if (haveSwapInterval) {
0273             setSwapInterval(1);
0274         } else {
0275             qCWarning(KWIN_X11STANDALONE) << "glSwapInterval is unsupported";
0276         }
0277     } else {
0278         setSwapInterval(0); // disable vsync if possible
0279     }
0280 
0281     if (glPlatform->isVirtualBox()) {
0282         // VirtualBox does not support glxQueryDrawable
0283         // this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
0284         // and the GLPlatform has not been initialized at the moment when initGLX() is called.
0285         glXQueryDrawable = nullptr;
0286     }
0287 
0288     static bool forceSoftwareVsync = qEnvironmentVariableIntValue("KWIN_X11_FORCE_SOFTWARE_VSYNC");
0289     if (supportsSwapEvent && !forceSoftwareVsync) {
0290         // Nice, the GLX_INTEL_swap_event extension is available. We are going to receive
0291         // the presentation timestamp (UST) after glXSwapBuffers() via the X command stream.
0292         m_swapEventFilter = std::make_unique<SwapEventFilter>(window, glxWindow);
0293         glXSelectEvent(display(), glxWindow, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
0294     } else {
0295         // If the GLX_INTEL_swap_event extension is unavailble, we are going to wait for
0296         // the next vblank event after swapping buffers. This is a bit racy solution, e.g.
0297         // the vblank may occur right in between querying video sync counter and the act
0298         // of swapping buffers, but on the other hand, there is no any better alternative
0299         // option. NVIDIA doesn't provide any extension such as GLX_INTEL_swap_event.
0300         if (!forceSoftwareVsync) {
0301             if (!m_vsyncMonitor) {
0302                 m_vsyncMonitor = SGIVideoSyncVsyncMonitor::create();
0303             }
0304             if (!m_vsyncMonitor) {
0305                 m_vsyncMonitor = OMLSyncControlVsyncMonitor::create();
0306             }
0307         }
0308         if (!m_vsyncMonitor) {
0309             std::unique_ptr<SoftwareVsyncMonitor> monitor = SoftwareVsyncMonitor::create();
0310             RenderLoop *renderLoop = m_backend->renderLoop();
0311             monitor->setRefreshRate(renderLoop->refreshRate());
0312             connect(renderLoop, &RenderLoop::refreshRateChanged, this, [this, m = monitor.get()]() {
0313                 m->setRefreshRate(m_backend->renderLoop()->refreshRate());
0314             });
0315             m_vsyncMonitor = std::move(monitor);
0316         }
0317 
0318         connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &GlxBackend::vblank);
0319     }
0320 
0321     setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
0322 
0323     qCDebug(KWIN_X11STANDALONE) << "Direct rendering:" << isDirectRendering();
0324 }
0325 
0326 bool GlxBackend::checkVersion()
0327 {
0328     int major, minor;
0329     glXQueryVersion(display(), &major, &minor);
0330     return kVersionNumber(major, minor) >= kVersionNumber(1, 3);
0331 }
0332 
0333 void GlxBackend::initExtensions()
0334 {
0335     const QByteArray string = (const char *)glXQueryExtensionsString(display(), QX11Info::appScreen());
0336     setExtensions(string.split(' '));
0337 }
0338 
0339 bool GlxBackend::initRenderingContext()
0340 {
0341     const bool direct = true;
0342 
0343     QOpenGLContext *qtGlobalShareContext = QOpenGLContext::globalShareContext();
0344     GLXContext globalShareContext = nullptr;
0345     if (qtGlobalShareContext) {
0346         qDebug(KWIN_X11STANDALONE) << "Global share context format:" << qtGlobalShareContext->format();
0347 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0348         const QVariant nativeHandle = qtGlobalShareContext->nativeHandle();
0349         if (!nativeHandle.canConvert<QGLXNativeContext>()) {
0350             qCDebug(KWIN_X11STANDALONE) << "Invalid QOpenGLContext::globalShareContext()";
0351             return false;
0352         } else {
0353             QGLXNativeContext handle = qvariant_cast<QGLXNativeContext>(nativeHandle);
0354             globalShareContext = handle.context();
0355         }
0356 #else
0357         const auto nativeHandle = qtGlobalShareContext->nativeInterface<QNativeInterface::QGLXContext>();
0358         if (nativeHandle) {
0359             globalShareContext = nativeHandle->nativeContext();
0360         } else {
0361             qCDebug(KWIN_X11STANDALONE) << "Invalid QOpenGLContext::globalShareContext()";
0362             return false;
0363         }
0364 #endif
0365     }
0366     if (!globalShareContext) {
0367         qCWarning(KWIN_X11STANDALONE) << "QOpenGLContext::globalShareContext() is required";
0368         return false;
0369     }
0370 
0371     // Use glXCreateContextAttribsARB() when it's available
0372     if (hasExtension(QByteArrayLiteral("GLX_ARB_create_context"))) {
0373         const bool have_robustness = hasExtension(QByteArrayLiteral("GLX_ARB_create_context_robustness"));
0374         const bool haveVideoMemoryPurge = hasExtension(QByteArrayLiteral("GLX_NV_robustness_video_memory_purge"));
0375 
0376         std::vector<GlxContextAttributeBuilder> candidates;
0377         // core
0378         if (have_robustness) {
0379             if (haveVideoMemoryPurge) {
0380                 GlxContextAttributeBuilder purgeMemoryCore;
0381                 purgeMemoryCore.setVersion(3, 1);
0382                 purgeMemoryCore.setRobust(true);
0383                 purgeMemoryCore.setResetOnVideoMemoryPurge(true);
0384                 candidates.emplace_back(std::move(purgeMemoryCore));
0385             }
0386             GlxContextAttributeBuilder robustCore;
0387             robustCore.setVersion(3, 1);
0388             robustCore.setRobust(true);
0389             candidates.emplace_back(std::move(robustCore));
0390         }
0391         GlxContextAttributeBuilder core;
0392         core.setVersion(3, 1);
0393         candidates.emplace_back(std::move(core));
0394         // legacy
0395         if (have_robustness) {
0396             if (haveVideoMemoryPurge) {
0397                 GlxContextAttributeBuilder purgeMemoryLegacy;
0398                 purgeMemoryLegacy.setRobust(true);
0399                 purgeMemoryLegacy.setResetOnVideoMemoryPurge(true);
0400                 candidates.emplace_back(std::move(purgeMemoryLegacy));
0401             }
0402             GlxContextAttributeBuilder robustLegacy;
0403             robustLegacy.setRobust(true);
0404             candidates.emplace_back(std::move(robustLegacy));
0405         }
0406         GlxContextAttributeBuilder legacy;
0407         legacy.setVersion(2, 1);
0408         candidates.emplace_back(std::move(legacy));
0409         for (auto it = candidates.begin(); it != candidates.end(); it++) {
0410             const auto attribs = it->build();
0411             ctx = glXCreateContextAttribsARB(display(), fbconfig, globalShareContext, true, attribs.data());
0412             if (ctx) {
0413                 qCDebug(KWIN_X11STANDALONE) << "Created GLX context with attributes:" << &(*it);
0414                 break;
0415             }
0416         }
0417     }
0418 
0419     if (!ctx) {
0420         ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, globalShareContext, direct);
0421     }
0422 
0423     if (!ctx) {
0424         qCDebug(KWIN_X11STANDALONE) << "Failed to create an OpenGL context.";
0425         return false;
0426     }
0427 
0428     if (!glXMakeCurrent(display(), glxWindow, ctx)) {
0429         qCDebug(KWIN_X11STANDALONE) << "Failed to make the OpenGL context current.";
0430         glXDestroyContext(display(), ctx);
0431         ctx = nullptr;
0432         return false;
0433     }
0434 
0435     return true;
0436 }
0437 
0438 bool GlxBackend::initBuffer()
0439 {
0440     if (!initFbConfig()) {
0441         return false;
0442     }
0443 
0444     if (overlayWindow()->create()) {
0445         xcb_connection_t *const c = connection();
0446 
0447         // Try to create double-buffered window in the overlay
0448         xcb_visualid_t visual;
0449         glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, (int *)&visual);
0450 
0451         if (!visual) {
0452             qCCritical(KWIN_X11STANDALONE) << "The GLXFBConfig does not have an associated X visual";
0453             return false;
0454         }
0455 
0456         xcb_colormap_t colormap = xcb_generate_id(c);
0457         xcb_create_colormap(c, false, colormap, rootWindow(), visual);
0458 
0459         const QSize size = workspace()->geometry().size();
0460 
0461         window = xcb_generate_id(c);
0462         xcb_create_window(c, visualDepth(visual), window, overlayWindow()->window(),
0463                           0, 0, size.width(), size.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
0464                           visual, XCB_CW_COLORMAP, &colormap);
0465 
0466         glxWindow = glXCreateWindow(display(), fbconfig, window, nullptr);
0467         overlayWindow()->setup(window);
0468     } else {
0469         qCCritical(KWIN_X11STANDALONE) << "Failed to create overlay window";
0470         return false;
0471     }
0472 
0473     return true;
0474 }
0475 
0476 bool GlxBackend::initFbConfig()
0477 {
0478     const int attribs[] = {
0479         GLX_RENDER_TYPE, GLX_RGBA_BIT,
0480         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0481         GLX_RED_SIZE, 1,
0482         GLX_GREEN_SIZE, 1,
0483         GLX_BLUE_SIZE, 1,
0484         GLX_ALPHA_SIZE, 0,
0485         GLX_DEPTH_SIZE, 0,
0486         GLX_STENCIL_SIZE, 0,
0487         GLX_CONFIG_CAVEAT, GLX_NONE,
0488         GLX_DOUBLEBUFFER, true,
0489         0};
0490 
0491     const int attribs_srgb[] = {
0492         GLX_RENDER_TYPE, GLX_RGBA_BIT,
0493         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
0494         GLX_RED_SIZE, 1,
0495         GLX_GREEN_SIZE, 1,
0496         GLX_BLUE_SIZE, 1,
0497         GLX_ALPHA_SIZE, 0,
0498         GLX_DEPTH_SIZE, 0,
0499         GLX_STENCIL_SIZE, 0,
0500         GLX_CONFIG_CAVEAT, GLX_NONE,
0501         GLX_DOUBLEBUFFER, true,
0502         GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, true,
0503         0};
0504 
0505     // Only request sRGB configurations with default depth 24 as it can cause problems with other default depths. See bugs #408594 and #423014.
0506     if (Xcb::defaultDepth() == 24) {
0507         fbconfig = chooseGlxFbConfig(display(), attribs_srgb);
0508     }
0509     if (!fbconfig) {
0510         fbconfig = chooseGlxFbConfig(display(), attribs);
0511     }
0512 
0513     if (fbconfig == nullptr) {
0514         qCCritical(KWIN_X11STANDALONE) << "Failed to find a usable framebuffer configuration";
0515         return false;
0516     }
0517 
0518     int fbconfig_id, visual_id, red, green, blue, alpha, depth, stencil, srgb;
0519     glXGetFBConfigAttrib(display(), fbconfig, GLX_FBCONFIG_ID, &fbconfig_id);
0520     glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, &visual_id);
0521     glXGetFBConfigAttrib(display(), fbconfig, GLX_RED_SIZE, &red);
0522     glXGetFBConfigAttrib(display(), fbconfig, GLX_GREEN_SIZE, &green);
0523     glXGetFBConfigAttrib(display(), fbconfig, GLX_BLUE_SIZE, &blue);
0524     glXGetFBConfigAttrib(display(), fbconfig, GLX_ALPHA_SIZE, &alpha);
0525     glXGetFBConfigAttrib(display(), fbconfig, GLX_DEPTH_SIZE, &depth);
0526     glXGetFBConfigAttrib(display(), fbconfig, GLX_STENCIL_SIZE, &stencil);
0527     glXGetFBConfigAttrib(display(), fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgb);
0528 
0529     qCDebug(KWIN_X11STANDALONE, "Choosing GLXFBConfig %#x X visual %#x depth %d RGBA %d:%d:%d:%d ZS %d:%d sRGB: %d",
0530             fbconfig_id, visual_id, visualDepth(visual_id), red, green, blue, alpha, depth, stencil, srgb);
0531 
0532     return true;
0533 }
0534 
0535 void GlxBackend::initVisualDepthHashTable()
0536 {
0537     const xcb_setup_t *setup = xcb_get_setup(connection());
0538 
0539     for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) {
0540         for (auto depth = xcb_screen_allowed_depths_iterator(screen.data); depth.rem; xcb_depth_next(&depth)) {
0541             const int len = xcb_depth_visuals_length(depth.data);
0542             const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
0543 
0544             for (int i = 0; i < len; i++) {
0545                 m_visualDepthHash.insert(visuals[i].visual_id, depth.data->depth);
0546             }
0547         }
0548     }
0549 }
0550 
0551 int GlxBackend::visualDepth(xcb_visualid_t visual) const
0552 {
0553     return m_visualDepthHash.value(visual);
0554 }
0555 
0556 static inline int bitCount(uint32_t mask)
0557 {
0558 #if defined(__GNUC__)
0559     return __builtin_popcount(mask);
0560 #else
0561     int count = 0;
0562 
0563     while (mask) {
0564         count += (mask & 1);
0565         mask >>= 1;
0566     }
0567 
0568     return count;
0569 #endif
0570 }
0571 
0572 const FBConfigInfo &GlxBackend::infoForVisual(xcb_visualid_t visual)
0573 {
0574     auto it = m_fbconfigHash.constFind(visual);
0575     if (it != m_fbconfigHash.constEnd()) {
0576         return *it;
0577     }
0578     m_fbconfigHash[visual] = FBConfigInfo{
0579         .fbconfig = nullptr,
0580         .bind_texture_format = 0,
0581         .texture_targets = 0,
0582         .y_inverted = 0,
0583         .mipmap = 0,
0584     };
0585     FBConfigInfo &info = m_fbconfigHash[visual];
0586 
0587     const xcb_render_pictformat_t format = XRenderUtils::findPictFormat(visual);
0588     const xcb_render_directformat_t *direct = XRenderUtils::findPictFormatInfo(format);
0589 
0590     if (!direct) {
0591         qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a picture format for visual 0x" << Qt::hex << visual;
0592         return info;
0593     }
0594 
0595     const int red_bits = bitCount(direct->red_mask);
0596     const int green_bits = bitCount(direct->green_mask);
0597     const int blue_bits = bitCount(direct->blue_mask);
0598     const int alpha_bits = bitCount(direct->alpha_mask);
0599 
0600     const int depth = visualDepth(visual);
0601 
0602     const auto rgb_sizes = std::tie(red_bits, green_bits, blue_bits);
0603 
0604     const int attribs[] = {
0605         GLX_RENDER_TYPE, GLX_RGBA_BIT,
0606         GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
0607         GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
0608         GLX_X_RENDERABLE, true,
0609         GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), // The ARGB32 visual is marked non-conformant in Catalyst
0610         GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, int(GLX_DONT_CARE), // The ARGB32 visual is marked sRGB capable in mesa/i965
0611         GLX_BUFFER_SIZE, red_bits + green_bits + blue_bits + alpha_bits,
0612         GLX_RED_SIZE, red_bits,
0613         GLX_GREEN_SIZE, green_bits,
0614         GLX_BLUE_SIZE, blue_bits,
0615         GLX_ALPHA_SIZE, alpha_bits,
0616         GLX_STENCIL_SIZE, 0,
0617         GLX_DEPTH_SIZE, 0,
0618         0};
0619 
0620     int count = 0;
0621     GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
0622 
0623     if (count < 1) {
0624         qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a framebuffer configuration for visual 0x" << Qt::hex << visual;
0625         return info;
0626     }
0627 
0628     struct FBConfig
0629     {
0630         GLXFBConfig config;
0631         int depth;
0632         int stencil;
0633         int format;
0634     };
0635 
0636     std::deque<FBConfig> candidates;
0637 
0638     for (int i = 0; i < count; i++) {
0639         int red, green, blue;
0640         glXGetFBConfigAttrib(display(), configs[i], GLX_RED_SIZE, &red);
0641         glXGetFBConfigAttrib(display(), configs[i], GLX_GREEN_SIZE, &green);
0642         glXGetFBConfigAttrib(display(), configs[i], GLX_BLUE_SIZE, &blue);
0643 
0644         if (std::tie(red, green, blue) != rgb_sizes) {
0645             continue;
0646         }
0647 
0648         xcb_visualid_t visual;
0649         glXGetFBConfigAttrib(display(), configs[i], GLX_VISUAL_ID, (int *)&visual);
0650 
0651         if (visualDepth(visual) != depth) {
0652             continue;
0653         }
0654 
0655         int bind_rgb, bind_rgba;
0656         glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
0657         glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
0658 
0659         if (!bind_rgb && !bind_rgba) {
0660             continue;
0661         }
0662 
0663         int depth, stencil;
0664         glXGetFBConfigAttrib(display(), configs[i], GLX_DEPTH_SIZE, &depth);
0665         glXGetFBConfigAttrib(display(), configs[i], GLX_STENCIL_SIZE, &stencil);
0666 
0667         int texture_format;
0668         if (alpha_bits) {
0669             texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
0670         } else {
0671             texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
0672         }
0673 
0674         candidates.emplace_back(FBConfig{configs[i], depth, stencil, texture_format});
0675     }
0676 
0677     if (count > 0) {
0678         XFree(configs);
0679     }
0680 
0681     std::stable_sort(candidates.begin(), candidates.end(), [](const FBConfig &left, const FBConfig &right) {
0682         if (left.depth < right.depth) {
0683             return true;
0684         }
0685 
0686         if (left.stencil < right.stencil) {
0687             return true;
0688         }
0689 
0690         return false;
0691     });
0692 
0693     if (candidates.size() > 0) {
0694         const FBConfig &candidate = candidates.front();
0695 
0696         int y_inverted, texture_targets;
0697         glXGetFBConfigAttrib(display(), candidate.config, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
0698         glXGetFBConfigAttrib(display(), candidate.config, GLX_Y_INVERTED_EXT, &y_inverted);
0699 
0700         info = FBConfigInfo{
0701             .fbconfig = candidate.config,
0702             .bind_texture_format = candidate.format,
0703             .texture_targets = texture_targets,
0704             .y_inverted = y_inverted,
0705             .mipmap = 0,
0706         };
0707     }
0708 
0709     if (info.fbconfig) {
0710         int fbc_id = 0;
0711         int visual_id = 0;
0712 
0713         glXGetFBConfigAttrib(display(), info.fbconfig, GLX_FBCONFIG_ID, &fbc_id);
0714         glXGetFBConfigAttrib(display(), info.fbconfig, GLX_VISUAL_ID, &visual_id);
0715 
0716         qCDebug(KWIN_X11STANDALONE).nospace() << "Using FBConfig 0x" << Qt::hex << fbc_id << " for visual 0x" << Qt::hex << visual_id;
0717     }
0718 
0719     return info;
0720 }
0721 
0722 void GlxBackend::setSwapInterval(int interval)
0723 {
0724     if (m_haveEXTSwapControl) {
0725         glXSwapIntervalEXT(display(), glxWindow, interval);
0726     } else if (m_haveMESASwapControl) {
0727         glXSwapIntervalMESA(interval);
0728     } else if (m_haveSGISwapControl) {
0729         glXSwapIntervalSGI(interval);
0730     }
0731 }
0732 
0733 void GlxBackend::present(const QRegion &damage)
0734 {
0735     const QSize &screenSize = workspace()->geometry().size();
0736     const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
0737     const bool fullRepaint = supportsBufferAge() || (damage == displayRegion);
0738 
0739     if (fullRepaint) {
0740         glXSwapBuffers(display(), glxWindow);
0741         if (supportsBufferAge()) {
0742             glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *)&m_bufferAge);
0743         }
0744     } else if (m_haveMESACopySubBuffer) {
0745         for (const QRect &r : damage) {
0746             // convert to OpenGL coordinates
0747             int y = screenSize.height() - r.y() - r.height();
0748             glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
0749         }
0750     } else { // Copy Pixels (horribly slow on Mesa)
0751         glDrawBuffer(GL_FRONT);
0752         copyPixels(damage, screenSize);
0753         glDrawBuffer(GL_BACK);
0754     }
0755 
0756     if (!supportsBufferAge()) {
0757         glXWaitGL();
0758         XFlush(display());
0759     }
0760 }
0761 
0762 void GlxBackend::screenGeometryChanged()
0763 {
0764     const QSize size = workspace()->geometry().size();
0765     doneCurrent();
0766 
0767     XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
0768     overlayWindow()->setup(window);
0769     Xcb::sync();
0770 
0771     // The back buffer contents are now undefined
0772     m_bufferAge = 0;
0773     m_fbo = std::make_unique<GLFramebuffer>(0, size);
0774 }
0775 
0776 std::unique_ptr<SurfaceTexture> GlxBackend::createSurfaceTextureX11(SurfacePixmapX11 *pixmap)
0777 {
0778     return std::make_unique<GlxSurfaceTextureX11>(this, pixmap);
0779 }
0780 
0781 OutputLayerBeginFrameInfo GlxBackend::beginFrame()
0782 {
0783     QRegion repaint;
0784     makeCurrent();
0785 
0786     if (supportsBufferAge()) {
0787         repaint = m_damageJournal.accumulate(m_bufferAge, infiniteRegion());
0788     }
0789 
0790     glXWaitX();
0791 
0792     return OutputLayerBeginFrameInfo{
0793         .renderTarget = RenderTarget(m_fbo.get()),
0794         .repaint = repaint,
0795     };
0796 }
0797 
0798 void GlxBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
0799 {
0800     // Save the damaged region to history
0801     if (supportsBufferAge()) {
0802         m_damageJournal.add(damagedRegion);
0803     }
0804     m_lastRenderedRegion = renderedRegion;
0805 }
0806 
0807 void GlxBackend::present(Output *output)
0808 {
0809     // If the GLX_INTEL_swap_event extension is not used for getting presentation feedback,
0810     // assume that the frame will be presented at the next vblank event, this is racy.
0811     if (m_vsyncMonitor) {
0812         m_vsyncMonitor->arm();
0813     }
0814 
0815     const QRect displayRect = workspace()->geometry();
0816 
0817     QRegion effectiveRenderedRegion = m_lastRenderedRegion;
0818     if (!supportsBufferAge() && options->glPreferBufferSwap() == Options::CopyFrontBuffer && m_lastRenderedRegion != displayRect) {
0819         glReadBuffer(GL_FRONT);
0820         copyPixels(QRegion(displayRect) - m_lastRenderedRegion, displayRect.size());
0821         glReadBuffer(GL_BACK);
0822         effectiveRenderedRegion = displayRect;
0823     }
0824 
0825     present(effectiveRenderedRegion);
0826 
0827     if (overlayWindow()->window()) { // show the window only after the first pass,
0828         overlayWindow()->show(); // since that pass may take long
0829     }
0830 }
0831 
0832 void GlxBackend::vblank(std::chrono::nanoseconds timestamp)
0833 {
0834     RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_backend->renderLoop());
0835     renderLoopPrivate->notifyFrameCompleted(timestamp);
0836 }
0837 
0838 bool GlxBackend::makeCurrent()
0839 {
0840     if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
0841         // Workaround to tell Qt that no QOpenGLContext is current
0842         context->doneCurrent();
0843     }
0844     const bool current = glXMakeCurrent(display(), glxWindow, ctx);
0845     return current;
0846 }
0847 
0848 void GlxBackend::doneCurrent()
0849 {
0850     glXMakeCurrent(display(), None, nullptr);
0851 }
0852 
0853 OverlayWindow *GlxBackend::overlayWindow() const
0854 {
0855     return m_overlayWindow.get();
0856 }
0857 
0858 OutputLayer *GlxBackend::primaryLayer(Output *output)
0859 {
0860     return m_layer.get();
0861 }
0862 
0863 GlxSurfaceTextureX11::GlxSurfaceTextureX11(GlxBackend *backend, SurfacePixmapX11 *texture)
0864     : OpenGLSurfaceTextureX11(backend, texture)
0865 {
0866 }
0867 
0868 bool GlxSurfaceTextureX11::create()
0869 {
0870     auto texture = std::make_unique<GlxPixmapTexture>(static_cast<GlxBackend *>(m_backend));
0871     if (texture->create(m_pixmap)) {
0872         m_texture = std::move(texture);
0873         return true;
0874     } else {
0875         return false;
0876     }
0877 }
0878 
0879 void GlxSurfaceTextureX11::update(const QRegion &region)
0880 {
0881     // mipmaps need to be updated
0882     m_texture->setDirty();
0883 }
0884 
0885 GlxPixmapTexture::GlxPixmapTexture(GlxBackend *backend)
0886     : GLTexture(*new GlxPixmapTexturePrivate(this, backend))
0887 {
0888 }
0889 
0890 bool GlxPixmapTexture::create(SurfacePixmapX11 *texture)
0891 {
0892     Q_D(GlxPixmapTexture);
0893     return d->create(texture);
0894 }
0895 
0896 GlxPixmapTexturePrivate::GlxPixmapTexturePrivate(GlxPixmapTexture *texture, GlxBackend *backend)
0897     : m_backend(backend)
0898     , q(texture)
0899     , m_glxPixmap(None)
0900 {
0901 }
0902 
0903 GlxPixmapTexturePrivate::~GlxPixmapTexturePrivate()
0904 {
0905     if (m_glxPixmap != None) {
0906         if (!options->isGlStrictBinding()) {
0907             glXReleaseTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
0908         }
0909         glXDestroyPixmap(m_backend->display(), m_glxPixmap);
0910         m_glxPixmap = None;
0911     }
0912 }
0913 
0914 void GlxPixmapTexturePrivate::onDamage()
0915 {
0916     if (options->isGlStrictBinding() && m_glxPixmap) {
0917         glXReleaseTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
0918         glXBindTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT, nullptr);
0919     }
0920     GLTexturePrivate::onDamage();
0921 }
0922 
0923 bool GlxPixmapTexturePrivate::create(SurfacePixmapX11 *texture)
0924 {
0925     if (texture->pixmap() == XCB_NONE || texture->size().isEmpty() || texture->visual() == XCB_NONE) {
0926         return false;
0927     }
0928 
0929     const FBConfigInfo &info = m_backend->infoForVisual(texture->visual());
0930     if (info.fbconfig == nullptr) {
0931         return false;
0932     }
0933 
0934     if (info.texture_targets & GLX_TEXTURE_2D_BIT_EXT) {
0935         m_target = GL_TEXTURE_2D;
0936         m_scale.setWidth(1.0f / m_size.width());
0937         m_scale.setHeight(1.0f / m_size.height());
0938     } else {
0939         Q_ASSERT(info.texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT);
0940 
0941         m_target = GL_TEXTURE_RECTANGLE;
0942         m_scale.setWidth(1.0f);
0943         m_scale.setHeight(1.0f);
0944     }
0945 
0946     const int attrs[] = {
0947         GLX_TEXTURE_FORMAT_EXT, info.bind_texture_format,
0948         GLX_MIPMAP_TEXTURE_EXT, false,
0949         GLX_TEXTURE_TARGET_EXT, m_target == GL_TEXTURE_2D ? GLX_TEXTURE_2D_EXT : GLX_TEXTURE_RECTANGLE_EXT,
0950         0};
0951 
0952     m_glxPixmap = glXCreatePixmap(m_backend->display(), info.fbconfig, texture->pixmap(), attrs);
0953     m_size = texture->size();
0954     m_yInverted = info.y_inverted ? true : false;
0955     m_canUseMipmaps = false;
0956 
0957     glGenTextures(1, &m_texture);
0958 
0959     q->setDirty();
0960     q->setFilter(GL_NEAREST);
0961 
0962     glBindTexture(m_target, m_texture);
0963     glXBindTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT, nullptr);
0964 
0965     updateMatrix();
0966     return true;
0967 }
0968 
0969 } // namespace