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 ®ion) 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