glxbackend.cpp 27.6 KB
Newer Older
1
2
3
4
5
/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>
6
Copyright (C) 2012 Martin Gräßlin <mgraesslin@kde.org>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

Based on glcompmgr code by Felix Bellaby.
Using code from Compiz and Beryl.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/

25
26
// own
#include "glxbackend.h"
27
#include "logging.h"
28
#include "glx_context_attribute_builder.h"
29
30
31
// kwin
#include "options.h"
#include "overlaywindow.h"
32
#include "composite.h"
33
#include "platform.h"
34
#include "scene.h"
35
#include "screens.h"
36
#include "xcbutils.h"
37
#include "texture.h"
38
39
// kwin libs
#include <kwinglplatform.h>
40
#include <kwinglutils.h>
41
#include <kwineffectquickview.h>
42
#include <kwinxrenderutils.h>
Martin Flöser's avatar
Martin Flöser committed
43
// Qt
44
#include <QDebug>
45
#include <QOpenGLContext>
46
#include <QX11Info>
47
#include <QtPlatformHeaders/QGLXNativeContext>
Thomas Lübking's avatar
Thomas Lübking committed
48
49
// system
#include <unistd.h>
50

51
52
#include <deque>
#include <algorithm>
53
#if HAVE_DL_LIBRARY
54
#include <dlfcn.h>
55
#endif
56

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#ifndef XCB_GLX_BUFFER_SWAP_COMPLETE
#define XCB_GLX_BUFFER_SWAP_COMPLETE 1
typedef struct xcb_glx_buffer_swap_complete_event_t {
    uint8_t            response_type; /**<  */
    uint8_t            pad0; /**<  */
    uint16_t           sequence; /**<  */
    uint16_t           event_type; /**<  */
    uint8_t            pad1[2]; /**<  */
    xcb_glx_drawable_t drawable; /**<  */
    uint32_t           ust_hi; /**<  */
    uint32_t           ust_lo; /**<  */
    uint32_t           msc_hi; /**<  */
    uint32_t           msc_lo; /**<  */
    uint32_t           sbc; /**<  */
} xcb_glx_buffer_swap_complete_event_t;
#endif

74
#include <tuple>
Martin Flöser's avatar
Martin Flöser committed
75
#include <memory>
76

77
78
namespace KWin
{
79

80
SwapEventFilter::SwapEventFilter(xcb_drawable_t drawable, xcb_glx_drawable_t glxDrawable)
81
    : X11EventFilter(Xcb::Extensions::self()->glxEventBase() + XCB_GLX_BUFFER_SWAP_COMPLETE),
82
83
      m_drawable(drawable),
      m_glxDrawable(glxDrawable)
84
85
86
87
88
89
90
91
{
}

bool SwapEventFilter::event(xcb_generic_event_t *event)
{
    xcb_glx_buffer_swap_complete_event_t *ev =
            reinterpret_cast<xcb_glx_buffer_swap_complete_event_t *>(event);

92
93
94
95
    // The drawable field is the X drawable when the event was synthesized
    // by a WireToEvent handler, and the GLX drawable when the event was
    // received over the wire
    if (ev->drawable == m_drawable || ev->drawable == m_glxDrawable) {
96
97
98
99
100
101
102
103
104
105
106
107
        Compositor::self()->bufferSwapComplete();
        return true;
    }

    return false;
}


// -----------------------------------------------------------------------



108
GlxBackend::GlxBackend(Display *display)
109
    : OpenGLBackend()
110
    , m_overlayWindow(kwinApp()->platform()->createOverlayWindow())
111
    , window(None)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
112
    , fbconfig(nullptr)
113
    , glxWindow(None)
Martin Flöser's avatar
Martin Flöser committed
114
    , ctx(nullptr)
115
    , m_bufferAge(0)
116
    , m_x11Display(display)
117
{
Alexander Volkov's avatar
Alexander Volkov committed
118
119
120
121
     // Force initialization of GLX integration in the Qt's xcb backend
     // to make it call XESetWireToEvent callbacks, which is required
     // by Mesa when using DRI2.
     QOpenGLContext::supportsThreadedOpenGL();
122
123
124
125
}

GlxBackend::~GlxBackend()
{
126
127
128
    if (isFailed()) {
        m_overlayWindow->destroy();
    }
129
130
131
    // TODO: cleanup in error case
    // do cleanup after initBuffer()
    cleanupGL();
132
    doneCurrent();
133
    EffectQuickView::setShareContext(nullptr);
134

135
136
    if (ctx)
        glXDestroyContext(display(), ctx);
137
138
139
140
141
142
143

    if (glxWindow)
        glXDestroyWindow(display(), glxWindow);

    if (window)
        XDestroyWindow(display(), window);

144
145
146
    qDeleteAll(m_fbconfigHash);
    m_fbconfigHash.clear();

147
    overlayWindow()->destroy();
148
    delete m_overlayWindow;
149
150
}

151
152
153
typedef void (*glXFuncPtr)();

static glXFuncPtr getProcAddress(const char* name)
154
{
155
156
157
158
    glXFuncPtr ret = nullptr;
#if HAVE_EPOXY_GLX
    ret = glXGetProcAddress((const GLubyte*) name);
#endif
159
#if HAVE_DL_LIBRARY
160
161
    if (ret == nullptr)
        ret = (glXFuncPtr) dlsym(RTLD_DEFAULT, name);
162
#endif
163
164
165
    return ret;
}
glXSwapIntervalMESA_func glXSwapIntervalMESA;
166

167
168
void GlxBackend::init()
{
169
    // Require at least GLX 1.3
170
    if (!checkVersion()) {
171
        setFailed(QStringLiteral("Requires at least GLX 1.3"));
172
        return;
173
    }
174

175
176
    initExtensions();

177
178
179
180
181
182
183
    // resolve glXSwapIntervalMESA if available
    if (hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"))) {
        glXSwapIntervalMESA = (glXSwapIntervalMESA_func) getProcAddress("glXSwapIntervalMESA");
    } else {
        glXSwapIntervalMESA = nullptr;
    }

184
185
    initVisualDepthHashTable();

186
    if (!initBuffer()) {
187
        setFailed(QStringLiteral("Could not initialize the buffer"));
188
        return;
189
    }
190

191
    if (!initRenderingContext()) {
192
        setFailed(QStringLiteral("Could not initialize rendering context"));
193
194
        return;
    }
195

196
197
    // Initialize OpenGL
    GLPlatform *glPlatform = GLPlatform::instance();
Martin Flöser's avatar
Martin Flöser committed
198
    glPlatform->detect(GlxPlatformInterface);
199
200
201
    options->setGlPreferBufferSwap(options->glPreferBufferSwap()); // resolve autosetting
    if (options->glPreferBufferSwap() == Options::AutoSwapStrategy)
        options->setGlPreferBufferSwap('e'); // for unknown drivers - should not happen
202
    glPlatform->printResults();
203
    initGL(&getProcAddress);
204

205
    // Check whether certain features are supported
206
207
208
    m_haveMESACopySubBuffer = hasExtension(QByteArrayLiteral("GLX_MESA_copy_sub_buffer"));
    m_haveMESASwapControl   = hasExtension(QByteArrayLiteral("GLX_MESA_swap_control"));
    m_haveEXTSwapControl    = hasExtension(QByteArrayLiteral("GLX_EXT_swap_control"));
209

210
    // only enable Intel swap event if env variable is set, see BUG 342582
Roman Gilg's avatar
Roman Gilg committed
211
    if (hasExtension(QByteArrayLiteral("GLX_INTEL_swap_event")) &&
212
            qgetenv("KWIN_USE_INTEL_SWAP_EVENT") == QByteArrayLiteral("1")) {
213
        m_swapEventFilter = std::make_unique<SwapEventFilter>(window, glxWindow);
214
215
        glXSelectEvent(display(), glxWindow, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK);
    }
216

217
218
    setSupportsBufferAge(false);

219
    if (hasExtension(QByteArrayLiteral("GLX_EXT_buffer_age"))) {
220
221
222
223
224
225
        const QByteArray useBufferAge = qgetenv("KWIN_USE_BUFFER_AGE");

        if (useBufferAge != "0")
            setSupportsBufferAge(true);
    }

226
227
228
229
    if (m_haveEXTSwapControl) {
        glXSwapIntervalEXT(display(), glxWindow, 1);
    } else if (m_haveMESASwapControl) {
        glXSwapIntervalMESA(1);
230
    } else {
231
        qCWarning(KWIN_X11STANDALONE) << "NO VSYNC! glSwapInterval is not supported";
232
    }
233

234
235
236
237
    if (glPlatform->isVirtualBox()) {
        // VirtualBox does not support glxQueryDrawable
        // this should actually be in kwinglutils_funcs, but QueryDrawable seems not to be provided by an extension
        // and the GLPlatform has not been initialized at the moment when initGLX() is called.
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
238
        glXQueryDrawable = nullptr;
239
    }
240

241
    setIsDirectRendering(bool(glXIsDirect(display(), ctx)));
242

243
    qCDebug(KWIN_X11STANDALONE) << "Direct rendering:" << isDirectRendering();
244
}
245

246
247
248
249
250
251
252
bool GlxBackend::checkVersion()
{
    int major, minor;
    glXQueryVersion(display(), &major, &minor);
    return kVersionNumber(major, minor) >= kVersionNumber(1, 3);
}

253
254
255
256
257
258
void GlxBackend::initExtensions()
{
    const QByteArray string = (const char *) glXQueryExtensionsString(display(), QX11Info::appScreen());
    setExtensions(string.split(' '));
}

259
bool GlxBackend::initRenderingContext()
260
{
261
    const bool direct = true;
262

263
    // Use glXCreateContextAttribsARB() when it's available
264
265
    if (hasExtension(QByteArrayLiteral("GLX_ARB_create_context"))) {
        const bool have_robustness = hasExtension(QByteArrayLiteral("GLX_ARB_create_context_robustness"));
266
        const bool haveVideoMemoryPurge = hasExtension(QByteArrayLiteral("GLX_NV_robustness_video_memory_purge"));
267

268
        std::vector<GlxContextAttributeBuilder> candidates;
269
        if (options->glCoreProfile()) {
270
            if (have_robustness) {
271
                if (haveVideoMemoryPurge) {
272
273
274
275
276
                    GlxContextAttributeBuilder purgeMemoryCore;
                    purgeMemoryCore.setVersion(3, 1);
                    purgeMemoryCore.setRobust(true);
                    purgeMemoryCore.setResetOnVideoMemoryPurge(true);
                    candidates.emplace_back(std::move(purgeMemoryCore));
277
                }
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
                GlxContextAttributeBuilder robustCore;
                robustCore.setVersion(3, 1);
                robustCore.setRobust(true);
                candidates.emplace_back(std::move(robustCore));
            }
            GlxContextAttributeBuilder core;
            core.setVersion(3, 1);
            candidates.emplace_back(std::move(core));
        } else {
            if (have_robustness) {
                if (haveVideoMemoryPurge) {
                    GlxContextAttributeBuilder purgeMemoryLegacy;
                    purgeMemoryLegacy.setRobust(true);
                    purgeMemoryLegacy.setResetOnVideoMemoryPurge(true);
                    candidates.emplace_back(std::move(purgeMemoryLegacy));
293
                }
294
295
296
                GlxContextAttributeBuilder robustLegacy;
                robustLegacy.setRobust(true);
                candidates.emplace_back(std::move(robustLegacy));
297
            }
298
299
300
            GlxContextAttributeBuilder legacy;
            legacy.setVersion(2, 1);
            candidates.emplace_back(std::move(legacy));
301
        }
302
303
        for (auto it = candidates.begin(); it != candidates.end(); it++) {
            const auto attribs = it->build();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
304
            ctx = glXCreateContextAttribsARB(display(), fbconfig, nullptr, true, attribs.data());
305
306
307
            if (ctx) {
                qCDebug(KWIN_X11STANDALONE) << "Created GLX context with attributes:" << &(*it);
                break;
308
309
            }
        }
310
311
312
    }

    if (!ctx)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
313
        ctx = glXCreateNewContext(display(), fbconfig, GLX_RGBA_TYPE, nullptr, direct);
314
315

    if (!ctx) {
316
        qCDebug(KWIN_X11STANDALONE) << "Failed to create an OpenGL context.";
317
318
319
        return false;
    }

320
    if (!glXMakeCurrent(display(), glxWindow, ctx)) {
321
        qCDebug(KWIN_X11STANDALONE) << "Failed to make the OpenGL context current.";
322
        glXDestroyContext(display(), ctx);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
323
        ctx = nullptr;
324
        return false;
325
    }
326

327
328
329
330
331
332
    auto qtContext = new QOpenGLContext;
    QGLXNativeContext native(ctx, display());
    qtContext->setNativeHandle(QVariant::fromValue(native));
    qtContext->create();
    EffectQuickView::setShareContext(std::unique_ptr<QOpenGLContext>(qtContext));

333
334
    return true;
}
335

336
bool GlxBackend::initBuffer()
337
{
338
    if (!initFbConfig())
339
        return false;
340

341
    if (overlayWindow()->create()) {
342
343
        xcb_connection_t * const c = connection();

344
        // Try to create double-buffered window in the overlay
345
346
347
        xcb_visualid_t visual;
        glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID, (int *) &visual);

348
        if (!visual) {
349
           qCCritical(KWIN_X11STANDALONE) << "The GLXFBConfig does not have an associated X visual";
350
351
           return false;
        }
352
353
354
355
356
357
358
359
360
361
362

        xcb_colormap_t colormap = xcb_generate_id(c);
        xcb_create_colormap(c, false, colormap, rootWindow(), visual);

        const QSize size = screens()->size();

        window = xcb_generate_id(c);
        xcb_create_window(c, visualDepth(visual), window, overlayWindow()->window(),
                          0, 0, size.width(), size.height(), 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
                          visual, XCB_CW_COLORMAP, &colormap);

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
363
        glxWindow = glXCreateWindow(display(), fbconfig, window, nullptr);
364
        overlayWindow()->setup(window);
365
    } else {
366
        qCCritical(KWIN_X11STANDALONE) << "Failed to create overlay window";
367
        return false;
368
    }
369

370
    return true;
371
}
372

373
bool GlxBackend::initFbConfig()
374
{
375
    const int attribs[] = {
Martin Flöser's avatar
Martin Flöser committed
376
        GLX_RENDER_TYPE,    GLX_RGBA_BIT,
377
        GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT,
378
379
380
381
382
383
384
385
386
387
388
        GLX_RED_SIZE,       1,
        GLX_GREEN_SIZE,     1,
        GLX_BLUE_SIZE,      1,
        GLX_ALPHA_SIZE,     0,
        GLX_DEPTH_SIZE,     0,
        GLX_STENCIL_SIZE,   0,
        GLX_CONFIG_CAVEAT,  GLX_NONE,
        GLX_DOUBLEBUFFER,   true,
        0
    };

389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    const int attribs_srgb[] = {
        GLX_RENDER_TYPE,                  GLX_RGBA_BIT,
        GLX_DRAWABLE_TYPE,                GLX_WINDOW_BIT,
        GLX_RED_SIZE,                     1,
        GLX_GREEN_SIZE,                   1,
        GLX_BLUE_SIZE,                    1,
        GLX_ALPHA_SIZE,                   0,
        GLX_DEPTH_SIZE,                   0,
        GLX_STENCIL_SIZE,                 0,
        GLX_CONFIG_CAVEAT,                GLX_NONE,
        GLX_DOUBLEBUFFER,                 true,
        GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, true,
        0
    };

404
405
406
407
408
409
410
411
412
413
    bool llvmpipe = false;

    // Note that we cannot use GLPlatform::driver() here, because it has not been initialized at this point
    if (hasExtension(QByteArrayLiteral("GLX_MESA_query_renderer"))) {
        const QByteArray device = glXQueryRendererStringMESA(display(), DefaultScreen(display()), 0, GLX_RENDERER_DEVICE_ID_MESA);
        if (device.contains(QByteArrayLiteral("llvmpipe"))) {
            llvmpipe = true;
        }
    }

414
    // Try to find a double buffered sRGB capable configuration
415
    int count = 0;
416
417
418
419
420
421
    GLXFBConfig *configs = nullptr;

    // Don't request an sRGB configuration with LLVMpipe when the default depth is 16. See bug #408594.
    if (!llvmpipe || Xcb::defaultDepth() > 16) {
        configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs_srgb, &count);
    }
422
423
424
425
426

    if (count == 0) {
        // Try to find a double buffered non-sRGB capable configuration
        configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);
    }
427

428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
    struct FBConfig {
        GLXFBConfig config;
        int depth;
        int stencil;
    };

    std::deque<FBConfig> candidates;

    for (int i = 0; i < count; i++) {
        int depth, stencil;
        glXGetFBConfigAttrib(display(), configs[i], GLX_DEPTH_SIZE,   &depth);
        glXGetFBConfigAttrib(display(), configs[i], GLX_STENCIL_SIZE, &stencil);

        candidates.emplace_back(FBConfig{configs[i], depth, stencil});
    }

    if (count > 0)
445
        XFree(configs);
446
447
448
449
450
451
452
453
454
455
456
457
458

    std::stable_sort(candidates.begin(), candidates.end(), [](const FBConfig &left, const FBConfig &right) {
        if (left.depth < right.depth)
            return true;

        if (left.stencil < right.stencil)
            return true;

        return false;
    });

    if (candidates.size() > 0) {
        fbconfig = candidates.front().config;
459

460
        int fbconfig_id, visual_id, red, green, blue, alpha, depth, stencil, srgb;
461
462
463
464
465
466
467
468
        glXGetFBConfigAttrib(display(), fbconfig, GLX_FBCONFIG_ID,  &fbconfig_id);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_VISUAL_ID,    &visual_id);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_RED_SIZE,     &red);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_GREEN_SIZE,   &green);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_BLUE_SIZE,    &blue);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_ALPHA_SIZE,   &alpha);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_DEPTH_SIZE,   &depth);
        glXGetFBConfigAttrib(display(), fbconfig, GLX_STENCIL_SIZE, &stencil);
469
        glXGetFBConfigAttrib(display(), fbconfig, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &srgb);
470

471
472
        qCDebug(KWIN_X11STANDALONE, "Choosing GLXFBConfig %#x X visual %#x depth %d RGBA %d:%d:%d:%d ZS %d:%d sRGB: %d",
                fbconfig_id, visual_id, visualDepth(visual_id), red, green, blue, alpha, depth, stencil, srgb);
473
    }
474

475
    if (fbconfig == nullptr) {
476
        qCCritical(KWIN_X11STANDALONE) << "Failed to find a usable framebuffer configuration";
477
        return false;
478
    }
479

480
481
    return true;
}
482

483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
void GlxBackend::initVisualDepthHashTable()
{
    const xcb_setup_t *setup = xcb_get_setup(connection());

    for (auto screen = xcb_setup_roots_iterator(setup); screen.rem; xcb_screen_next(&screen)) {
        for (auto depth = xcb_screen_allowed_depths_iterator(screen.data); depth.rem; xcb_depth_next(&depth)) {
            const int len = xcb_depth_visuals_length(depth.data);
            const xcb_visualtype_t *visuals = xcb_depth_visuals(depth.data);

            for (int i = 0; i < len; i++)
                m_visualDepthHash.insert(visuals[i].visual_id, depth.data->depth);
        }
    }
}

int GlxBackend::visualDepth(xcb_visualid_t visual) const
{
    return m_visualDepthHash.value(visual);
}

503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
static inline int bitCount(uint32_t mask)
{
#if defined(__GNUC__)
    return __builtin_popcount(mask);
#else
    int count = 0;

    while (mask) {
        count += (mask & 1);
        mask >>= 1;
    }

    return count;
#endif
}

519
520
FBConfigInfo *GlxBackend::infoForVisual(xcb_visualid_t visual)
{
521
522
523
524
    auto it = m_fbconfigHash.constFind(visual);
    if (it != m_fbconfigHash.constEnd()) {
        return it.value();
    }
525

526
527
    FBConfigInfo *info = new FBConfigInfo;
    m_fbconfigHash.insert(visual, info);
528
529
530
531
532
533
534
535
536
537
    info->fbconfig            = nullptr;
    info->bind_texture_format = 0;
    info->texture_targets     = 0;
    info->y_inverted          = 0;
    info->mipmap              = 0;

    const xcb_render_pictformat_t format = XRenderUtils::findPictFormat(visual);
    const xcb_render_directformat_t *direct = XRenderUtils::findPictFormatInfo(format);

    if (!direct) {
538
        qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a picture format for visual 0x" << hex << visual;
539
540
541
542
543
544
545
546
        return info;
    }

    const int red_bits   = bitCount(direct->red_mask);
    const int green_bits = bitCount(direct->green_mask);
    const int blue_bits  = bitCount(direct->blue_mask);
    const int alpha_bits = bitCount(direct->alpha_mask);

547
548
    const int depth = visualDepth(visual);

549
550
551
552
553
554
555
556
    const auto rgb_sizes = std::tie(red_bits, green_bits, blue_bits);

    const int attribs[] = {
        GLX_RENDER_TYPE,    GLX_RGBA_BIT,
        GLX_DRAWABLE_TYPE,  GLX_WINDOW_BIT | GLX_PIXMAP_BIT,
        GLX_X_VISUAL_TYPE,  GLX_TRUE_COLOR,
        GLX_X_RENDERABLE,   True,
        GLX_CONFIG_CAVEAT,  int(GLX_DONT_CARE), // The ARGB32 visual is marked non-conformant in Catalyst
557
        GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT,  int(GLX_DONT_CARE), // The ARGB32 visual is marked sRGB capable in mesa/i965
558
559
560
561
562
563
564
565
566
567
568
569
570
571
        GLX_BUFFER_SIZE,    red_bits + green_bits + blue_bits + alpha_bits,
        GLX_RED_SIZE,       red_bits,
        GLX_GREEN_SIZE,     green_bits,
        GLX_BLUE_SIZE,      blue_bits,
        GLX_ALPHA_SIZE,     alpha_bits,
        GLX_STENCIL_SIZE,   0,
        GLX_DEPTH_SIZE,     0,
        0
    };

    int count = 0;
    GLXFBConfig *configs = glXChooseFBConfig(display(), DefaultScreen(display()), attribs, &count);

    if (count < 1) {
572
        qCCritical(KWIN_X11STANDALONE).nospace() << "Could not find a framebuffer configuration for visual 0x" << hex << visual;
573
574
575
        return info;
    }

576
577
578
579
580
581
582
583
584
    struct FBConfig {
        GLXFBConfig config;
        int depth;
        int stencil;
        int format;
    };

    std::deque<FBConfig> candidates;

585
586
587
588
589
590
591
592
593
    for (int i = 0; i < count; i++) {
        int red, green, blue;
        glXGetFBConfigAttrib(display(), configs[i], GLX_RED_SIZE,   &red);
        glXGetFBConfigAttrib(display(), configs[i], GLX_GREEN_SIZE, &green);
        glXGetFBConfigAttrib(display(), configs[i], GLX_BLUE_SIZE,  &blue);

        if (std::tie(red, green, blue) != rgb_sizes)
            continue;

594
595
596
597
598
599
        xcb_visualid_t visual;
        glXGetFBConfigAttrib(display(), configs[i], GLX_VISUAL_ID, (int *) &visual);

        if (visualDepth(visual) != depth)
            continue;

600
601
602
603
604
605
606
        int bind_rgb, bind_rgba;
        glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &bind_rgba);
        glXGetFBConfigAttrib(display(), configs[i], GLX_BIND_TO_TEXTURE_RGB_EXT,  &bind_rgb);

        if (!bind_rgb && !bind_rgba)
            continue;

607
608
609
610
        int depth, stencil;
        glXGetFBConfigAttrib(display(), configs[i], GLX_DEPTH_SIZE,   &depth);
        glXGetFBConfigAttrib(display(), configs[i], GLX_STENCIL_SIZE, &stencil);

611
612
613
614
615
616
        int texture_format;
        if (alpha_bits)
            texture_format = bind_rgba ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT;
        else
            texture_format = bind_rgb ? GLX_TEXTURE_FORMAT_RGB_EXT : GLX_TEXTURE_FORMAT_RGBA_EXT;

617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
        candidates.emplace_back(FBConfig{configs[i], depth, stencil, texture_format});
    }

    if (count > 0)
        XFree(configs);

    std::stable_sort(candidates.begin(), candidates.end(), [](const FBConfig &left, const FBConfig &right) {
        if (left.depth < right.depth)
            return true;

        if (left.stencil < right.stencil)
            return true;

        return false;
    });

    if (candidates.size() > 0) {
        const FBConfig &candidate = candidates.front();

636
        int y_inverted, texture_targets;
637
638
        glXGetFBConfigAttrib(display(), candidate.config, GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_targets);
        glXGetFBConfigAttrib(display(), candidate.config, GLX_Y_INVERTED_EXT, &y_inverted);
639

640
641
        info->fbconfig            = candidate.config;
        info->bind_texture_format = candidate.format;
642
643
644
645
646
647
648
649
650
651
652
653
        info->texture_targets     = texture_targets;
        info->y_inverted          = y_inverted;
        info->mipmap              = 0;
    }

    if (info->fbconfig) {
        int fbc_id = 0;
        int visual_id = 0;

        glXGetFBConfigAttrib(display(), info->fbconfig, GLX_FBCONFIG_ID, &fbc_id);
        glXGetFBConfigAttrib(display(), info->fbconfig, GLX_VISUAL_ID,   &visual_id);

654
        qCDebug(KWIN_X11STANDALONE).nospace() << "Using FBConfig 0x" << hex << fbc_id << " for visual 0x" << hex << visual_id;
655
656
657
658
659
    }

    return info;
}

660
void GlxBackend::present()
661
{
Thomas Lübking's avatar
Thomas Lübking committed
662
663
664
    if (lastDamage().isEmpty())
        return;

665
666
    const QSize &screenSize = screens()->size();
    const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
667
    const bool fullRepaint = supportsBufferAge() || (lastDamage() == displayRegion);
668

669
670
    if (fullRepaint) {
        if (hasSwapEvent()) {
671
            Compositor::self()->aboutToSwapBuffers();
Roman Gilg's avatar
Roman Gilg committed
672
        }
673

674
675
        glXSwapBuffers(display(), glxWindow);

676
677
678
        if (supportsBufferAge()) {
            glXQueryDrawable(display(), glxWindow, GLX_BACK_BUFFER_AGE_EXT, (GLuint *) &m_bufferAge);
        }
679
    } else if (m_haveMESACopySubBuffer) {
680
        for (const QRect &r : lastDamage()) {
681
            // convert to OpenGL coordinates
682
            int y = screenSize.height() - r.y() - r.height();
683
            glXCopySubBufferMESA(display(), glxWindow, r.x(), y, r.width(), r.height());
684
        }
685
    } else { // Copy Pixels (horribly slow on Mesa)
686
        glDrawBuffer(GL_FRONT);
687
        copyPixels(lastDamage());
688
        glDrawBuffer(GL_BACK);
689
    }
Ralf Jung's avatar
Ralf Jung committed
690
691

    setLastDamage(QRegion());
692
693
694
695
    if (!supportsBufferAge()) {
        glXWaitGL();
        XFlush(display());
    }
696
}
697

698
void GlxBackend::screenGeometryChanged(const QSize &size)
699
{
700
    doneCurrent();
701
702
703

    XMoveResizeWindow(display(), window, 0, 0, size.width(), size.height());
    overlayWindow()->setup(window);
704
    Xcb::sync();
705

706
    makeCurrent();
707
    glViewport(0, 0, size.width(), size.height());
708
709
710

    // The back buffer contents are now undefined
    m_bufferAge = 0;
711
712
}

713
SceneOpenGLTexturePrivate *GlxBackend::createBackendTexture(SceneOpenGLTexture *texture)
714
715
716
{
    return new GlxTexture(texture, this);
}
717

718
QRegion GlxBackend::prepareRenderingFrame()
719
{
720
721
722
723
724
    QRegion repaint;

    if (supportsBufferAge())
        repaint = accumulatedDamageHistory(m_bufferAge);

Thomas Lübking's avatar
Thomas Lübking committed
725
    startRenderTimer();
726

727
    return repaint;
728
}
729

730
void GlxBackend::endRenderingFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
731
{
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
    if (damagedRegion.isEmpty()) {
        setLastDamage(QRegion());

        // If the damaged region of a window is fully occluded, the only
        // rendering done, if any, will have been to repair a reused back
        // buffer, making it identical to the front buffer.
        //
        // In this case we won't post the back buffer. Instead we'll just
        // set the buffer age to 1, so the repaired regions won't be
        // rendered again in the next frame.
        if (!renderedRegion.isEmpty())
            glFlush();

        m_bufferAge = 1;
        return;
    }

749
    setLastDamage(renderedRegion);
750
    present();
751
752
753

    if (overlayWindow()->window())  // show the window only after the first pass,
        overlayWindow()->show();   // since that pass may take long
754
755
756
757

    // Save the damaged region to history
    if (supportsBufferAge())
        addToDamageHistory(damagedRegion);
758
759
}

760
761
762
763
764
765
766
767
768
769
770
771
772
773
bool GlxBackend::makeCurrent()
{
    if (QOpenGLContext *context = QOpenGLContext::currentContext()) {
        // Workaround to tell Qt that no QOpenGLContext is current
        context->doneCurrent();
    }
    const bool current = glXMakeCurrent(display(), glxWindow, ctx);
    return current;
}

void GlxBackend::doneCurrent()
{
    glXMakeCurrent(display(), None, nullptr);
}
774

775
OverlayWindow* GlxBackend::overlayWindow() const
776
777
778
779
780
781
782
783
784
{
    return m_overlayWindow;
}

bool GlxBackend::usesOverlayWindow() const
{
    return true;
}

785
786
bool GlxBackend::hasSwapEvent() const
{
787
    return m_swapEventFilter != nullptr;
788
789
}

790
791
792
/********************************************************
 * GlxTexture
 *******************************************************/
793
794
GlxTexture::GlxTexture(SceneOpenGLTexture *texture, GlxBackend *backend)
    : SceneOpenGLTexturePrivate()
795
796
797
798
799
800
801
    , q(texture)
    , m_backend(backend)
    , m_glxpixmap(None)
{
}

GlxTexture::~GlxTexture()
802
803
{
    if (m_glxpixmap != None) {
804
        if (!options->isGlStrictBinding()) {
805
            glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
806
        }
807
        glXDestroyPixmap(display(), m_glxpixmap);
808
        m_glxpixmap = None;
809
    }
810
}
811

812
813
814
815
void GlxTexture::onDamage()
{
    if (options->isGlStrictBinding() && m_glxpixmap) {
        glXReleaseTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
816
        glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, nullptr);
817
818
819
820
    }
    GLTexturePrivate::onDamage();
}

821
822
823
824
825
826
827
828
829
bool GlxTexture::loadTexture(xcb_pixmap_t pixmap, const QSize &size, xcb_visualid_t visual)
{
    if (pixmap == XCB_NONE || size.isEmpty() || visual == XCB_NONE)
        return false;

    const FBConfigInfo *info = m_backend->infoForVisual(visual);
    if (!info || info->fbconfig == nullptr)
        return false;

830
    if (info->texture_targets & GLX_TEXTURE_2D_BIT_EXT) {
831
832
833
834
        m_target = GL_TEXTURE_2D;
        m_scale.setWidth(1.0f / m_size.width());
        m_scale.setHeight(1.0f / m_size.height());
    } else {
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
835
        Q_ASSERT(info->texture_targets & GLX_TEXTURE_RECTANGLE_BIT_EXT);
836
837
838
839
840
841
842
843

        m_target = GL_TEXTURE_RECTANGLE;
        m_scale.setWidth(1.0f);
        m_scale.setHeight(1.0f);
    }

    const int attrs[] = {
        GLX_TEXTURE_FORMAT_EXT, info->bind_texture_format,
844
        GLX_MIPMAP_TEXTURE_EXT, false,
845
846
847
848
849
850
851
        GLX_TEXTURE_TARGET_EXT, m_target == GL_TEXTURE_2D ? GLX_TEXTURE_2D_EXT : GLX_TEXTURE_RECTANGLE_EXT,
        0
    };

    m_glxpixmap     = glXCreatePixmap(display(), info->fbconfig, pixmap, attrs);
    m_size          = size;
    m_yInverted     = info->y_inverted ? true : false;
852
    m_canUseMipmaps = false;
853
854
855
856

    glGenTextures(1, &m_texture);

    q->setDirty();
857
    q->setFilter(GL_NEAREST);
858
859
860
861
862
863
864
865

    glBindTexture(m_target, m_texture);
    glXBindTexImageEXT(display(), m_glxpixmap, GLX_FRONT_LEFT_EXT, nullptr);

    updateMatrix();
    return true;
}

866
867
868
bool GlxTexture::loadTexture(WindowPixmap *pixmap)
{
    Toplevel *t = pixmap->toplevel();
869
    return loadTexture(pixmap->pixmap(), t->bufferGeometry().size(), t->visual());
870
871
}

872
OpenGLBackend *GlxTexture::backend()
873
{
874
    return m_backend;
875
}
876
877

} // namespace