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