KWin
Loading...
Searching...
No Matches
x11_standalone_glx_backend.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
7
8 Based on glcompmgr code by Felix Bellaby.
9 Using code from Compiz and Beryl.
10
11 SPDX-License-Identifier: GPL-2.0-or-later
12*/
13
14// own
17#include "glxcontext.h"
26// kwin
27#include "compositor.h"
28#include "core/outputbackend.h"
29#include "core/overlaywindow.h"
30#include "core/renderloop_p.h"
32#include "options.h"
34#include "utils/xcbutils.h"
35#include "workspace.h"
36// kwin libs
38#include "opengl/glplatform.h"
39#include "opengl/glutils.h"
40// Qt
41#include <QDebug>
42#include <QOpenGLContext>
43#include <private/qtx11extras_p.h>
44// system
45#include <unistd.h>
46
47#include <algorithm>
48#include <deque>
49#if HAVE_DL_LIBRARY
50#include <dlfcn.h>
51#endif
52
53#ifndef XCB_GLX_BUFFER_SWAP_COMPLETE
54#define XCB_GLX_BUFFER_SWAP_COMPLETE 1
56{
57 uint8_t response_type;
58 uint8_t pad0;
59 uint16_t sequence;
60 uint16_t event_type;
61 uint8_t pad1[2];
62 xcb_glx_drawable_t drawable;
63 uint32_t ust_hi;
64 uint32_t ust_lo;
65 uint32_t msc_hi;
66 uint32_t msc_lo;
67 uint32_t sbc;
69#endif
70
71#include <drm_fourcc.h>
72#include <tuple>
73
74namespace KWin
75{
76
77SwapEventFilter::SwapEventFilter(xcb_drawable_t drawable, xcb_glx_drawable_t glxDrawable)
78 : X11EventFilter(Xcb::Extensions::self()->glxEventBase() + XCB_GLX_BUFFER_SWAP_COMPLETE)
79 , m_drawable(drawable)
80 , m_glxDrawable(glxDrawable)
81{
82}
83
84bool SwapEventFilter::event(xcb_generic_event_t *event)
85{
88 if (swapEvent->drawable != m_drawable && swapEvent->drawable != m_glxDrawable) {
89 return false;
90 }
91
92 // The clock for the UST timestamp is left unspecified in the spec, however, usually,
93 // it's CLOCK_MONOTONIC, so no special conversions are needed.
94 const std::chrono::microseconds timestamp((uint64_t(swapEvent->ust_hi) << 32) | swapEvent->ust_lo);
95
96 const auto platform = static_cast<X11StandaloneBackend *>(kwinApp()->outputBackend());
97 RenderLoopPrivate::get(platform->renderLoop())->notifyFrameCompleted(timestamp, Compositor::self()->backend()->primaryLayer(nullptr)->queryRenderTime());
98
99 return true;
100}
101
103 : m_backend(backend)
104{
105}
106
107std::optional<OutputLayerBeginFrameInfo> GlxLayer::beginFrame()
108{
109 return m_backend->beginFrame();
110}
111
112bool GlxLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
113{
114 m_backend->endFrame(renderedRegion, damagedRegion);
115 return true;
116}
117
118std::chrono::nanoseconds GlxLayer::queryRenderTime() const
119{
120 return m_backend->queryRenderTime();
121}
122
124 : OpenGLBackend()
125 , m_overlayWindow(std::make_unique<OverlayWindowX11>(backend))
126 , window(None)
127 , fbconfig(nullptr)
128 , glxWindow(None)
129 , m_bufferAge(0)
130 , m_x11Display(display)
131 , m_backend(backend)
132 , m_layer(std::make_unique<GlxLayer>(this))
133{
134 // Force initialization of GLX integration in the Qt's xcb backend
135 // to make it call XESetWireToEvent callbacks, which is required
136 // by Mesa when using DRI2.
137 QOpenGLContext::supportsThreadedOpenGL();
138
139 Q_ASSERT(workspace());
140 connect(workspace(), &Workspace::geometryChanged, this, &GlxBackend::screenGeometryChanged);
141 overlayWindow()->resize(workspace()->geometry().size());
142}
143
145{
146 m_vsyncMonitor.reset();
147 // No completion events will be received for in-flight frames, this may lock the
148 // render loop. We need to ensure that the render loop is back to its initial state
149 // if the render backend is about to be destroyed.
151
152 m_query.reset();
153
154 if (isFailed()) {
155 m_overlayWindow->destroy();
156 }
157 // TODO: cleanup in error case
158 // do cleanup after initBuffer()
159 cleanupGL();
160 doneCurrent();
161
162 m_context.reset();
163
164 if (glxWindow) {
165 glXDestroyWindow(display(), glxWindow);
166 }
167
168 if (window) {
169 XDestroyWindow(display(), window);
170 }
171
172 m_overlayWindow->destroy();
173}
174
175typedef void (*glXFuncPtr)();
176
177static glXFuncPtr getProcAddress(const char *name)
178{
179 glXFuncPtr ret = nullptr;
180#if HAVE_GLX
181 ret = glXGetProcAddress((const GLubyte *)name);
182#endif
183#if HAVE_DL_LIBRARY
184 if (ret == nullptr) {
185 ret = (glXFuncPtr)dlsym(RTLD_DEFAULT, name);
186 }
187#endif
188 return ret;
189}
191
193{
194 // Require at least GLX 1.3
195 if (!checkVersion()) {
196 setFailed(QStringLiteral("Requires at least GLX 1.3"));
197 return;
198 }
199
200 initExtensions();
201
202 // resolve glXSwapIntervalMESA if available
203 if (hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"))) {
204 glXSwapIntervalMESA = (glXSwapIntervalMESA_func)getProcAddress("glXSwapIntervalMESA");
205 } else {
206 glXSwapIntervalMESA = nullptr;
207 }
208
209 initVisualDepthHashTable();
210
211 if (!initBuffer()) {
212 setFailed(QStringLiteral("Could not initialize the buffer"));
213 return;
214 }
215
216 m_context = GlxContext::create(this, fbconfig, glxWindow);
217 if (!m_context) {
218 setFailed(QStringLiteral("Could not initialize rendering context"));
219 return;
220 }
221
222 // Initialize OpenGL
223 GLPlatform *glPlatform = GLPlatform::instance();
224 glPlatform->detect(GlxPlatformInterface);
225
226 m_swapStrategy = options->glPreferBufferSwap();
227 if (m_swapStrategy == Options::AutoSwapStrategy) {
228 // buffer copying is very fast with the nvidia blob
229 // but due to restrictions in DRI2 *incredibly* slow for all MESA drivers
230 // see https://www.x.org/releases/X11R7.7/doc/dri2proto/dri2proto.txt, item 2.5
231 if (GLPlatform::instance()->driver() == Driver_NVidia) {
232 m_swapStrategy = Options::CopyFrontBuffer;
233 } else if (GLPlatform::instance()->driver() != Driver_Unknown) { // undetected, finally resolved when context is initialized
234 m_swapStrategy = Options::ExtendDamage;
235 }
236 }
237
238 glPlatform->printResults();
239 initGL(&getProcAddress);
240
241 m_fbo = std::make_unique<GLFramebuffer>(0, workspace()->geometry().size());
242
243 bool supportsSwapEvent = false;
244
245 if (hasExtension(QByteArrayLiteral("GLX_INTEL_swap_event"))) {
246 if (qEnvironmentVariableIsSet("KWIN_USE_INTEL_SWAP_EVENT")) {
247 supportsSwapEvent = qEnvironmentVariable("KWIN_USE_INTEL_SWAP_EVENT") != QLatin1String("0");
248 } else {
249 // Don't use swap events on Intel. The issue with Intel GPUs is that they are not as
250 // powerful as discrete GPUs. Therefore, it's better to push frames as often as vblank
251 // notifications are received. This, however, may increase latency. If the swap events
252 // are enabled explicitly by setting the environment variable, honor that choice.
253 supportsSwapEvent = !glPlatform->isIntel();
254 }
255 }
256
257 // Check whether certain features are supported
258 m_haveMESACopySubBuffer = hasExtension(QByteArrayLiteral("GLX_MESA_copy_sub_buffer"));
259 m_haveMESASwapControl = hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"));
260 m_haveEXTSwapControl = hasExtension(QByteArrayLiteral("GLX_EXT_swap_control"));
261 m_haveSGISwapControl = hasExtension(QByteArrayLiteral("GLX_SGI_swap_control"));
262
263 bool haveSwapInterval = m_haveMESASwapControl || m_haveEXTSwapControl || m_haveSGISwapControl;
264
266
267 if (hasExtension(QByteArrayLiteral("GLX_EXT_buffer_age"))) {
268 const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");
269
270 if (useBufferAge != "0") {
272 }
273 }
274
275 // If the buffer age extension is unsupported, glXSwapBuffers() is not guaranteed to
276 // be called. Therefore, there is no point for creating the swap event filter.
277 if (!supportsBufferAge()) {
278 supportsSwapEvent = false;
279 }
280
281 static bool syncToVblankDisabled = qEnvironmentVariableIsSet("KWIN_X11_NO_SYNC_TO_VBLANK");
282 if (!syncToVblankDisabled) {
283 if (haveSwapInterval) {
284 setSwapInterval(1);
285 } else {
286 qCWarning(KWIN_X11STANDALONE) << "glSwapInterval is unsupported";
287 }
288 } else {
289 setSwapInterval(0); // disable vsync if possible
290 }
291
292 if (glPlatform->isVirtualBox()) {
293 // VirtualBox does not support glxQueryDrawable
294 // this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
295 // and the GLPlatform has not been initialized at the moment when initGLX() is called.
296 glXQueryDrawable = nullptr;
297 }
298
299 static bool forceSoftwareVsync = qEnvironmentVariableIntValue("KWIN_X11_FORCE_SOFTWARE_VSYNC");
300 if (supportsSwapEvent && !forceSoftwareVsync) {
301 // Nice, the GLX_INTEL_swap_event extension is available. We are going to receive
302 // the presentation timestamp (UST) after glXSwapBuffers() via the X command stream.
303 m_swapEventFilter = std::make_unique<SwapEventFilter>(window, glxWindow);
304 glXSelectEvent(display(), glxWindow, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
305 } else {
306 // If the GLX_INTEL_swap_event extension is unavailble, we are going to wait for
307 // the next vblank event after swapping buffers. This is a bit racy solution, e.g.
308 // the vblank may occur right in between querying video sync counter and the act
309 // of swapping buffers, but on the other hand, there is no any better alternative
310 // option. NVIDIA doesn't provide any extension such as GLX_INTEL_swap_event.
311 if (!forceSoftwareVsync) {
312 if (!m_vsyncMonitor) {
313 m_vsyncMonitor = SGIVideoSyncVsyncMonitor::create();
314 }
315 if (!m_vsyncMonitor) {
316 m_vsyncMonitor = OMLSyncControlVsyncMonitor::create();
317 }
318 }
319 if (!m_vsyncMonitor) {
320 std::unique_ptr<SoftwareVsyncMonitor> monitor = SoftwareVsyncMonitor::create();
321 RenderLoop *renderLoop = m_backend->renderLoop();
322 monitor->setRefreshRate(renderLoop->refreshRate());
323 connect(renderLoop, &RenderLoop::refreshRateChanged, this, [this, m = monitor.get()]() {
324 m->setRefreshRate(m_backend->renderLoop()->refreshRate());
325 });
326 m_vsyncMonitor = std::move(monitor);
327 }
328
329 connect(m_vsyncMonitor.get(), &VsyncMonitor::vblankOccurred, this, &GlxBackend::vblank);
330 }
331}
332
333bool GlxBackend::checkVersion()
334{
335 int major, minor;
336 glXQueryVersion(display(), &major, &minor);
337 return Version(major, minor) >= Version(1, 3);
338}
339
340void GlxBackend::initExtensions()
341{
342 const QByteArray string = (const char *)glXQueryExtensionsString(display(), QX11Info::appScreen());
343 setExtensions(string.split(' '));
344}
345
346bool GlxBackend::initBuffer()
347{
348 if (!initFbConfig()) {
349 return false;
350 }
351
352 if (overlayWindow()->create()) {
353 xcb_connection_t *const c = connection();
354
355 // Try to create double-buffered window in the overlay
356 xcb_visualid_t visual;
357 glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, (int *)&visual);
358
359 if (!visual) {
360 qCCritical(KWIN_X11STANDALONE) << "The GLXFBConfig does not have an associated X visual";
361 return false;
362 }
363
364 xcb_colormap_t colormap = xcb_generate_id(c);
365 xcb_create_colormap(c, false, colormap, rootWindow(), visual);
366
367 const QSize size = workspace()->geometry().size();
368
369 window = xcb_generate_id(c);
370 xcb_create_window(c, visualDepth(visual), window, overlayWindow()->window(),
371 0, 0, size.width(), size.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
372 visual, XCB_CW_COLORMAP, &colormap);
373
374 glxWindow = glXCreateWindow(display(), fbconfig, window, nullptr);
375 overlayWindow()->setup(window);
376 } else {
377 qCCritical(KWIN_X11STANDALONE) << "Failed to create overlay window";
378 return false;
379 }
380
381 return true;
382}
383
384bool GlxBackend::initFbConfig()
385{
386 const int attribs[] = {
387 GLX_RENDER_TYPE, GLX_RGBA_BIT,
388 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
389 GLX_RED_SIZE, 1,
390 GLX_GREEN_SIZE, 1,
391 GLX_BLUE_SIZE, 1,
392 GLX_ALPHA_SIZE, 0,
393 GLX_DEPTH_SIZE, 0,
394 GLX_STENCIL_SIZE, 0,
395 GLX_CONFIG_CAVEAT, GLX_NONE,
396 GLX_DOUBLEBUFFER, true,
397 0};
398
399 const int attribs_srgb[] = {
400 GLX_RENDER_TYPE, GLX_RGBA_BIT,
401 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
402 GLX_RED_SIZE, 1,
403 GLX_GREEN_SIZE, 1,
404 GLX_BLUE_SIZE, 1,
405 GLX_ALPHA_SIZE, 0,
406 GLX_DEPTH_SIZE, 0,
407 GLX_STENCIL_SIZE, 0,
408 GLX_CONFIG_CAVEAT, GLX_NONE,
409 GLX_DOUBLEBUFFER, true,
410 GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, true,
411 0};
412
413 // Only request sRGB configurations with default depth 24 as it can cause problems with other default depths. See bugs #408594 and #423014.
414 if (Xcb::defaultDepth() == 24) {
415 fbconfig = chooseGlxFbConfig(display(), attribs_srgb);
416 }
417 if (!fbconfig) {
418 fbconfig = chooseGlxFbConfig(display(), attribs);
419 }
420
421 if (fbconfig == nullptr) {
422 qCCritical(KWIN_X11STANDALONE) << "Failed to find a usable framebuffer configuration";
423 return false;
424 }
425
426 int fbconfig_id, visual_id, red, green, blue, alpha, depth, stencil, srgb;
427 glXGetFBConfigAttrib(display(), fbconfig, GLX_FBCONFIG_ID, &fbconfig_id);
428 glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, &visual_id);
429 glXGetFBConfigAttrib(display(), fbconfig, GLX_RED_SIZE, &red);
430 glXGetFBConfigAttrib(display(), fbconfig, GLX_GREEN_SIZE, &green);
431 glXGetFBConfigAttrib(display(), fbconfig, GLX_BLUE_SIZE, &blue);
432 glXGetFBConfigAttrib(display(), fbconfig, GLX_ALPHA_SIZE, &alpha);
433 glXGetFBConfigAttrib(display(), fbconfig, GLX_DEPTH_SIZE, &depth);
434 glXGetFBConfigAttrib(display(), fbconfig, GLX_STENCIL_SIZE, &stencil);
435 glXGetFBConfigAttrib(display(), fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgb);
436
437 qCDebug(KWIN_X11STANDALONE, "Choosing GLXFBConfig %#x X visual %#x depth %d RGBA %d:%d:%d:%d ZS %d:%d sRGB: %d",
438 fbconfig_id, visual_id, visualDepth(visual_id), red, green, blue, alpha, depth, stencil, srgb);
439
440 return true;
441}
442
443void GlxBackend::initVisualDepthHashTable()
444{
445 const xcb_setup_t *setup = xcb_get_setup(connection());
446
447 for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) {
448 for (auto depth = xcb_screen_allowed_depths_iterator(screen.data); depth.rem; xcb_depth_next(&depth)) {
449 const int len = xcb_depth_visuals_length(depth.data);
450 const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);
451
452 for (int i = 0; i < len; i++) {
453 m_visualDepthHash.insert(visuals[i].visual_id, depth.data->depth);
454 }
455 }
456 }
457}
458
459int GlxBackend::visualDepth(xcb_visualid_t visual) const
460{
461 return m_visualDepthHash.value(visual);
462}
463
464static inline int bitCount(uint32_t mask)
465{
466#if defined(__GNUC__)
467 return __builtin_popcount(mask);
468#else
469 int count = 0;
470
471 while (mask) {
472 count += (mask & 1);
473 mask >>= 1;
474 }
475
476 return count;
477#endif
478}
479
480const FBConfigInfo &GlxBackend::infoForVisual(xcb_visualid_t visual)
481{
482 auto it = m_fbconfigHash.constFind(visual);
483 if (it != m_fbconfigHash.constEnd()) {
484 return *it;
485 }
486 m_fbconfigHash[visual] = FBConfigInfo{
487 .fbconfig = nullptr,
488 .bind_texture_format = 0,
489 .texture_targets = 0,
490 .y_inverted = 0,
491 .mipmap = 0,
492 };
493 FBConfigInfo &info = m_fbconfigHash[visual];
494
495 const xcb_render_pictformat_t format = XRenderUtils::findPictFormat(visual);
496 const xcb_render_directformat_t *direct = XRenderUtils::findPictFormatInfo(format);
497
498 if (!direct) {
499 qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a picture format for visual 0x" << Qt::hex << visual;
500 return info;
501 }
502
503 const int red_bits = bitCount(direct->red_mask);
504 const int green_bits = bitCount(direct->green_mask);
505 const int blue_bits = bitCount(direct->blue_mask);
506 const int alpha_bits = bitCount(direct->alpha_mask);
507
508 const int depth = visualDepth(visual);
509
510 const auto rgb_sizes = std::tie(red_bits, green_bits, blue_bits);
511
512 const int attribs[] = {
513 GLX_RENDER_TYPE, GLX_RGBA_BIT,
514 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
515 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
516 GLX_X_RENDERABLE, true,
517 GLX_CONFIG_CAVEAT, int(GLX_DONT_CARE), // The ARGB32 visual is marked non-conformant in Catalyst
518 GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, int(GLX_DONT_CARE), // The ARGB32 visual is marked sRGB capable in mesa/i965
519 GLX_BUFFER_SIZE, red_bits + green_bits + blue_bits + alpha_bits,
520 GLX_RED_SIZE, red_bits,
521 GLX_GREEN_SIZE, green_bits,
522 GLX_BLUE_SIZE, blue_bits,
523 GLX_ALPHA_SIZE, alpha_bits,
524 GLX_STENCIL_SIZE, 0,
525 GLX_DEPTH_SIZE, 0,
526 0};
527
528 int count = 0;
529 GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
530
531 if (count < 1) {
532 qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a framebuffer configuration for visual 0x" << Qt::hex << visual;
533 return info;
534 }
535
536 struct FBConfig
537 {
538 GLXFBConfig config;
539 int depth;
540 int stencil;
541 int format;
542 };
543
544 std::deque<FBConfig> candidates;
545
546 for (int i = 0; i < count; i++) {
547 int red, green, blue;
548 glXGetFBConfigAttrib(display(), configs[i], GLX_RED_SIZE, &red);
549 glXGetFBConfigAttrib(display(), configs[i], GLX_GREEN_SIZE, &green);
550 glXGetFBConfigAttrib(display(), configs[i], GLX_BLUE_SIZE, &blue);
551
552 if (std::tie(red, green, blue) != rgb_sizes) {
553 continue;
554 }
555
556 xcb_visualid_t visual;
557 glXGetFBConfigAttrib(display(), configs[i], GLX_VISUAL_ID, (int *)&visual);
558
559 if (visualDepth(visual) != depth) {
560 continue;
561 }
562
563 int bind_rgb, bind_rgba;
564 glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
565 glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &bind_rgb);
566
567 if (!bind_rgb && !bind_rgba) {
568 continue;
569 }
570
571 int depth, stencil;
572 glXGetFBConfigAttrib(display(), configs[i], GLX_DEPTH_SIZE, &depth);
573 glXGetFBConfigAttrib(display(), configs[i], GLX_STENCIL_SIZE, &stencil);
574
575 int texture_format;
576 if (alpha_bits) {
577 texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
578 } else {
579 texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;
580 }
581
582 candidates.emplace_back(FBConfig{configs[i], depth, stencil, texture_format});
583 }
584
585 if (count > 0) {
586 XFree(configs);
587 }
588
589 std::stable_sort(candidates.begin(), candidates.end(), [](const FBConfig &left, const FBConfig &right) {
590 if (left.depth < right.depth) {
591 return true;
592 }
593
594 if (left.stencil < right.stencil) {
595 return true;
596 }
597
598 return false;
599 });
600
601 if (candidates.size() > 0) {
602 const FBConfig &candidate = candidates.front();
603
604 int y_inverted, texture_targets;
605 glXGetFBConfigAttrib(display(), candidate.config, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
606 glXGetFBConfigAttrib(display(), candidate.config, GLX_Y_INVERTED_EXT, &y_inverted);
607
608 info = FBConfigInfo{
609 .fbconfig = candidate.config,
610 .bind_texture_format = candidate.format,
611 .texture_targets = texture_targets,
612 .y_inverted = y_inverted,
613 .mipmap = 0,
614 };
615 }
616
617 if (info.fbconfig) {
618 int fbc_id = 0;
619 int visual_id = 0;
620
621 glXGetFBConfigAttrib(display(), info.fbconfig, GLX_FBCONFIG_ID, &fbc_id);
622 glXGetFBConfigAttrib(display(), info.fbconfig, GLX_VISUAL_ID, &visual_id);
623
624 qCDebug(KWIN_X11STANDALONE).nospace() << "Using FBConfig 0x" << Qt::hex << fbc_id << " for visual 0x" << Qt::hex << visual_id;
625 }
626
627 return info;
628}
629
630void GlxBackend::setSwapInterval(int interval)
631{
632 if (m_haveEXTSwapControl) {
633 glXSwapIntervalEXT(display(), glxWindow, interval);
634 } else if (m_haveMESASwapControl) {
635 glXSwapIntervalMESA(interval);
636 } else if (m_haveSGISwapControl) {
637 glXSwapIntervalSGI(interval);
638 }
639}
640
641void GlxBackend::present(const QRegion &damage)
642{
643 const QSize &screenSize = workspace()->geometry().size();
644 const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
645 const bool fullRepaint = supportsBufferAge() || (damage == displayRegion);
646
647 if (fullRepaint) {
648 glXSwapBuffers(display(), glxWindow);
649 if (supportsBufferAge()) {
650 glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *)&m_bufferAge);
651 }
652 } else if (m_haveMESACopySubBuffer) {
653 for (const QRect &r : damage) {
654 // convert to OpenGL coordinates
655 int y = screenSize.height() - r.y() - r.height();
656 glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
657 }
658 } else { // Copy Pixels (horribly slow on Mesa)
659 glDrawBuffer(GL_FRONT);
660 copyPixels(damage, screenSize);
661 glDrawBuffer(GL_BACK);
662 }
663
664 if (!supportsBufferAge()) {
665 glXWaitGL();
666 XFlush(display());
667 }
668}
669
670void GlxBackend::screenGeometryChanged()
671{
672 const QSize size = workspace()->geometry().size();
673 doneCurrent();
674
675 XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
676 overlayWindow()->resize(size);
677 Xcb::sync();
678
679 // The back buffer contents are now undefined
680 m_bufferAge = 0;
681 m_fbo = std::make_unique<GLFramebuffer>(0, size);
682}
683
684std::unique_ptr<SurfaceTexture> GlxBackend::createSurfaceTextureX11(SurfacePixmapX11 *pixmap)
685{
686 return std::make_unique<GlxSurfaceTextureX11>(this, pixmap);
687}
688
689OutputLayerBeginFrameInfo GlxBackend::beginFrame()
690{
691 QRegion repaint;
692 makeCurrent();
693 if (!m_query) {
694 m_query = std::make_unique<GLRenderTimeQuery>();
695 }
696
697 if (supportsBufferAge()) {
698 repaint = m_damageJournal.accumulate(m_bufferAge, infiniteRegion());
699 }
700
701 glXWaitX();
702
703 if (!m_query) {
704 m_query = std::make_unique<GLRenderTimeQuery>();
705 }
706 m_query->begin();
708 .renderTarget = RenderTarget(m_fbo.get()),
709 .repaint = repaint,
710 };
711}
712
713void GlxBackend::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
714{
715 m_query->end();
716 // Save the damaged region to history
717 if (supportsBufferAge()) {
718 m_damageJournal.add(damagedRegion);
719 }
720 m_lastRenderedRegion = renderedRegion;
721}
722
723std::chrono::nanoseconds GlxBackend::queryRenderTime()
724{
725 makeCurrent();
726 return m_query->result();
727}
728
729void GlxBackend::present(Output *output, const std::shared_ptr<OutputFrame> &frame)
730{
731 m_frame = frame;
732 // If the GLX_INTEL_swap_event extension is not used for getting presentation feedback,
733 // assume that the frame will be presented at the next vblank event, this is racy.
734 if (m_vsyncMonitor) {
735 m_vsyncMonitor->arm();
736 }
737
738 const QRect displayRect = workspace()->geometry();
739
740 QRegion effectiveRenderedRegion = m_lastRenderedRegion;
741 if (!supportsBufferAge() && m_swapStrategy == Options::CopyFrontBuffer && m_lastRenderedRegion != displayRect) {
742 glReadBuffer(GL_FRONT);
743 copyPixels(QRegion(displayRect) - m_lastRenderedRegion, displayRect.size());
744 glReadBuffer(GL_BACK);
745 effectiveRenderedRegion = displayRect;
746 }
747
748 present(effectiveRenderedRegion);
749
750 if (overlayWindow()->window()) { // show the window only after the first pass,
751 overlayWindow()->show(); // since that pass may take long
752 }
753}
754
755void GlxBackend::vblank(std::chrono::nanoseconds timestamp)
756{
757 m_frame->presented(std::chrono::nanoseconds::zero(), timestamp, queryRenderTime(), PresentationMode::VSync);
758 m_frame.reset();
759}
760
761bool GlxBackend::makeCurrent()
762{
763 return m_context->makeCurrent();
764}
765
766void GlxBackend::doneCurrent()
767{
768 m_context->doneCurrent();
769}
770
771OverlayWindow *GlxBackend::overlayWindow() const
772{
773 return m_overlayWindow.get();
774}
775
776OutputLayer *GlxBackend::primaryLayer(Output *output)
777{
778 return m_layer.get();
779}
780
781GlxSurfaceTextureX11::GlxSurfaceTextureX11(GlxBackend *backend, SurfacePixmapX11 *texture)
782 : OpenGLSurfaceTextureX11(backend, texture)
783{
784}
785
787{
788 auto texture = std::make_shared<GlxPixmapTexture>(static_cast<GlxBackend *>(m_backend));
789 if (texture->create(m_pixmap)) {
790 m_texture = {texture};
791 return true;
792 } else {
793 return false;
794 }
795}
796
797void GlxSurfaceTextureX11::update(const QRegion &region)
798{
799 // mipmaps need to be updated
801}
802
804 : GLTexture(GL_TEXTURE_2D)
805 , m_backend(backend)
806 , m_glxPixmap(None)
807{
808}
809
811{
812 if (m_glxPixmap != None) {
813 if (!options->isGlStrictBinding()) {
814 glXReleaseTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
815 }
816 glXDestroyPixmap(m_backend->display(), m_glxPixmap);
817 m_glxPixmap = None;
818 }
819}
820
822{
823 if (texture->pixmap() == XCB_NONE || texture->size().isEmpty() || texture->visual() == XCB_NONE) {
824 return false;
825 }
826
827 const FBConfigInfo &info = m_backend->infoForVisual(texture->visual());
828 if (info.fbconfig == nullptr) {
829 return false;
830 }
831
832 if (info.texture_targets & GLX_TEXTURE_2D_BIT_EXT) {
833 d->m_target = GL_TEXTURE_2D;
834 d->m_scale.setWidth(1.0f / d->m_size.width());
835 d->m_scale.setHeight(1.0f / d->m_size.height());
836 } else {
837 Q_ASSERT(info.texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT);
838
839 d->m_target = GL_TEXTURE_RECTANGLE;
840 d->m_scale.setWidth(1.0f);
841 d->m_scale.setHeight(1.0f);
842 }
843
844 const int attrs[] = {
845 GLX_TEXTURE_FORMAT_EXT, info.bind_texture_format,
846 GLX_MIPMAP_TEXTURE_EXT, false,
847 GLX_TEXTURE_TARGET_EXT, d->m_target == GL_TEXTURE_2D ? GLX_TEXTURE_2D_EXT : GLX_TEXTURE_RECTANGLE_EXT,
848 0};
849
850 m_glxPixmap = glXCreatePixmap(m_backend->display(), info.fbconfig, texture->pixmap(), attrs);
851 d->m_size = texture->size();
853 d->m_canUseMipmaps = false;
854
855 glGenTextures(1, &d->m_texture);
856
857 setDirty();
858 setFilter(GL_LINEAR);
859 setWrapMode(GL_CLAMP_TO_EDGE);
860
861 glBindTexture(d->m_target, d->m_texture);
862 glXBindTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT, nullptr);
863
864 d->updateMatrix();
865 return true;
866}
867
868void GlxPixmapTexture::onDamage()
869{
870 if (options->isGlStrictBinding() && m_glxPixmap) {
871 glXReleaseTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT);
872 glXBindTexImageEXT(m_backend->display(), m_glxPixmap, GLX_FRONT_LEFT_EXT, nullptr);
873 }
874}
875
876} // namespace
877
878#include "moc_x11_standalone_glx_backend.cpp"
static Compositor * self()
void printResults() const
void detect(OpenGLPlatformInterface platformInterface)
static GLPlatform * instance()
Definition glplatform.h:394
bool isVirtualBox() const
bool isIntel() const
const std::unique_ptr< GLTexturePrivate > d
Definition gltexture.h:143
void setContentTransform(OutputTransform transform)
GLuint texture() const
void setFilter(GLenum filter)
void setWrapMode(GLenum mode)
OpenGL Backend using GLX over an X overlay window.
OutputLayerBeginFrameInfo beginFrame()
GlxBackend(::Display *display, X11StandaloneBackend *backend)
std::chrono::nanoseconds queryRenderTime()
OverlayWindow * overlayWindow() const override
::Display * display() const
void endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
static std::unique_ptr< GlxContext > create(GlxBackend *backend, GLXFBConfig fbconfig, GLXWindow glxWindow)
std::optional< OutputLayerBeginFrameInfo > beginFrame() override
std::chrono::nanoseconds queryRenderTime() const override
bool endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion) override
GlxLayer(GlxBackend *backend)
void update(const QRegion &region) override
static std::unique_ptr< OMLSyncControlVsyncMonitor > create()
The OpenGLBackend creates and holds the OpenGL context and is responsible for Texture from Pixmap.
void setSupportsBufferAge(bool value)
bool isFailed() const
Whether the creation of the Backend failed.
void setFailed(const QString &reason)
Sets the backend initialization to failed.
void setExtensions(const QList< QByteArray > &extensions)
bool hasExtension(const QByteArray &extension) const
bool supportsBufferAge() const
OpenGLSurfaceContents m_texture
OpenGLSurfaceContents texture() const
GlSwapStrategy glPreferBufferSwap
Definition options.h:197
bool isGlStrictBinding() const
Definition options.h:657
virtual void resize(const QSize &size)=0
virtual void setup(xcb_window_t window)=0
Init overlay and the destination window in it.
int refreshRate() const
void refreshRateChanged()
void setRefreshRate(int refreshRate)
static RenderLoopPrivate * get(RenderLoop *loop)
void notifyFrameCompleted(std::chrono::nanoseconds timestamp, std::chrono::nanoseconds renderTime, PresentationMode mode=PresentationMode::VSync)
static std::unique_ptr< SGIVideoSyncVsyncMonitor > create()
static std::unique_ptr< SoftwareVsyncMonitor > create()
bool event(xcb_generic_event_t *event) override
SwapEventFilter(xcb_drawable_t drawable, xcb_glx_drawable_t glxDrawable)
void vblankOccurred(std::chrono::nanoseconds timestamp)
qreal width
Definition window.h:99
QRect geometry() const
void geometryChanged()
const xcb_render_directformat_t * findPictFormatInfo(xcb_render_pictformat_t format)
xcb_render_pictformat_t findPictFormat(xcb_visualid_t visual)
KWIN_EXPORT QRect infiniteRegion()
Definition globals.h:234
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
@ Driver_NVidia
Definition glplatform.h:54
@ Driver_Unknown
Definition glplatform.h:68
@ None
Definition options.h:37
GLXFBConfig chooseGlxFbConfig(::Display *display, const int attributes[])
void KWIN_EXPORT cleanupGL()
Definition glutils.cpp:122
void initGL(const std::function< resolveFuncPtr(const char *)> &resolveFunction)
Definition glutils.cpp:97
glXSwapIntervalMESA_func glXSwapIntervalMESA
GLenum format
Definition gltexture.cpp:49
Workspace * workspace()
Definition workspace.h:830
@ GlxPlatformInterface
Definition globals.h:44
KWIN_EXPORT xcb_connection_t * connection()
Definition xcb.h:19
Options * options
Definition main.cpp:73
int(*)(unsigned int interval) glXSwapIntervalMESA_func
struct _XDisplay Display
#define XCB_GLX_BUFFER_SWAP_COMPLETE
struct xcb_glx_buffer_swap_complete_event_t xcb_glx_buffer_swap_complete_event_t