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

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