scene_opengl.cpp 92.5 KB
Newer Older
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
1
2
3
/*
    KWin - the KDE window manager
    This file is part of the KDE project.
4

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
5
6
7
    SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
    SPDX-FileCopyrightText: 2009, 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
    SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
9
10
    Based on glcompmgr code by Felix Bellaby.
    Using code from Compiz and Beryl.
11

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
12
13
    Explicit command stream synchronization based on the sample
    implementation by James Jones <jajones@nvidia.com>,
14

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
15
    SPDX-FileCopyrightText: 2011 NVIDIA Corporation
16

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
17
18
    SPDX-License-Identifier: GPL-2.0-or-later
*/
19
#include "scene_opengl.h"
20
#include "texture.h"
21

22
#include "platform.h"
23
24
#include "wayland_server.h"

25
#include <kwinglplatform.h>
26
#include <kwineffectquickview.h>
27

28
#include "utils.h"
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
29
#include "x11client.h"
30
#include "composite.h"
31
32
#include "deleted.h"
#include "effects.h"
33
#include "lanczosfilter.h"
34
#include "main.h"
35
#include "overlaywindow.h"
36
#include "renderloop.h"
37
#include "screens.h"
38
#include "cursor.h"
39
#include "decorations/decoratedclient.h"
40
#include <logging.h>
41

42
43
44
#include <KWaylandServer/buffer_interface.h>
#include <KWaylandServer/subcompositor_interface.h>
#include <KWaylandServer/surface_interface.h>
45

46
#include <array>
Thomas Lübking's avatar
Thomas Lübking committed
47
#include <cmath>
48
#include <cstddef>
49
#include <unistd.h>
50

51
52
53
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
54
#include <QGraphicsScale>
55
#include <QPainter>
56
#include <QStringList>
57
58
#include <QVector2D>
#include <QVector4D>
59
#include <QMatrix4x4>
60

61
62
#include <KLocalizedString>
#include <KNotification>
63
64
#include <KProcess>

65
66
67
68
69
70
71
72
73
74
75
// HACK: workaround for libepoxy < 1.3
#ifndef GL_GUILTY_CONTEXT_RESET
#define GL_GUILTY_CONTEXT_RESET 0x8253
#endif
#ifndef GL_INNOCENT_CONTEXT_RESET
#define GL_INNOCENT_CONTEXT_RESET 0x8254
#endif
#ifndef GL_UNKNOWN_CONTEXT_RESET
#define GL_UNKNOWN_CONTEXT_RESET 0x8255
#endif

76
77
78
namespace KWin
{

79
80
81
/**
 * SyncObject represents a fence used to synchronize operations in
 * the kwin command stream with operations in the X command stream.
82
 */
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class SyncObject
{
public:
    enum State { Ready, TriggerSent, Waiting, Done, Resetting };

    SyncObject();
    ~SyncObject();

    State state() const { return m_state; }

    void trigger();
    void wait();
    bool finish();
    void reset();
    void finishResetting();

private:
    State m_state;
    GLsync m_sync;
    xcb_sync_fence_t m_fence;
    xcb_get_input_focus_cookie_t m_reset_cookie;
};

SyncObject::SyncObject()
{
    m_state = Ready;

    xcb_connection_t * const c = connection();

    m_fence = xcb_generate_id(c);
    xcb_sync_create_fence(c, rootWindow(), m_fence, false);
    xcb_flush(c);

    m_sync = glImportSyncEXT(GL_SYNC_X11_FENCE_EXT, m_fence, 0);
}

SyncObject::~SyncObject()
{
121
122
123
124
125
126
127
128
129
130
131
    // If glDeleteSync is called before the xcb fence is signalled
    // the nvidia driver (the only one to implement GL_SYNC_X11_FENCE_EXT)
    // deadlocks waiting for the fence to be signalled.
    // To avoid this, make sure the fence is signalled before
    // deleting the sync.
    if (m_state == Resetting || m_state == Ready){
        trigger();
        // The flush is necessary!
        // The trigger command needs to be sent to the X server.
        xcb_flush(connection());
    }
132
133
134
135
136
137
138
139
140
    xcb_sync_destroy_fence(connection(), m_fence);
    glDeleteSync(m_sync);

    if (m_state == Resetting)
        xcb_discard_reply(connection(), m_reset_cookie.sequence);
}

void SyncObject::trigger()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
141
    Q_ASSERT(m_state == Ready || m_state == Resetting);
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167

    // Finish resetting the fence if necessary
    if (m_state == Resetting)
        finishResetting();

    xcb_sync_trigger_fence(connection(), m_fence);
    m_state = TriggerSent;
}

void SyncObject::wait()
{
    if (m_state != TriggerSent)
        return;

    glWaitSync(m_sync, 0, GL_TIMEOUT_IGNORED);
    m_state = Waiting;
}

bool SyncObject::finish()
{
    if (m_state == Done)
        return true;

    // Note: It is possible that we never inserted a wait for the fence.
    //       This can happen if we ended up not rendering the damaged
    //       window because it is fully occluded.
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
168
    Q_ASSERT(m_state == TriggerSent || m_state == Waiting);
169
170
171
172
173
174

    // Check if the fence is signaled
    GLint value;
    glGetSynciv(m_sync, GL_SYNC_STATUS, 1, nullptr, &value);

    if (value != GL_SIGNALED) {
175
        qCDebug(KWIN_OPENGL) << "Waiting for X fence to finish";
176
177
178
179
180
181

        // Wait for the fence to become signaled with a one second timeout
        const GLenum result = glClientWaitSync(m_sync, 0, 1000000000);

        switch (result) {
        case GL_TIMEOUT_EXPIRED:
182
            qCWarning(KWIN_OPENGL) << "Timeout while waiting for X fence";
183
184
185
            return false;

        case GL_WAIT_FAILED:
186
            qCWarning(KWIN_OPENGL) << "glClientWaitSync() failed";
187
188
189
190
191
192
193
194
195
196
            return false;
        }
    }

    m_state = Done;
    return true;
}

void SyncObject::reset()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
197
    Q_ASSERT(m_state == Done);
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

    xcb_connection_t * const c = connection();

    // Send the reset request along with a sync request.
    // We use the cookie to ensure that the server has processed the reset
    // request before we trigger the fence and call glWaitSync().
    // Otherwise there is a race condition between the reset finishing and
    // the glWaitSync() call.
    xcb_sync_reset_fence(c, m_fence);
    m_reset_cookie = xcb_get_input_focus(c);
    xcb_flush(c);

    m_state = Resetting;
}

void SyncObject::finishResetting()
{
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
215
    Q_ASSERT(m_state == Resetting);
216
217
218
219
220
221
222
223
224
225
226
227
228
    free(xcb_get_input_focus_reply(connection(), m_reset_cookie, nullptr));
    m_state = Ready;
}



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



/**
 * SyncManager manages a set of fences used for explicit synchronization
 * with the X command stream.
229
 */
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
class SyncManager
{
public:
    enum { MaxFences = 4 };

    SyncManager();
    ~SyncManager();

    SyncObject *nextFence();
    bool updateFences();

private:
    std::array<SyncObject, MaxFences> m_fences;
    int m_next;
};

SyncManager::SyncManager()
    : m_next(0)
{
}

SyncManager::~SyncManager()
{
}

SyncObject *SyncManager::nextFence()
{
    SyncObject *fence = &m_fences[m_next];
    m_next = (m_next + 1) % MaxFences;
    return fence;
}

bool SyncManager::updateFences()
{
    for (int i = 0; i < qMin(2, MaxFences - 1); i++) {
        const int index = (m_next + i) % MaxFences;
        SyncObject &fence = m_fences[index];

        switch (fence.state()) {
        case SyncObject::Ready:
            break;

        case SyncObject::TriggerSent:
        case SyncObject::Waiting:
            if (!fence.finish())
                return false;
            fence.reset();
            break;

        // Should not happen in practice since we always reset the fence
        // after finishing it
        case SyncObject::Done:
            fence.reset();
            break;

        case SyncObject::Resetting:
            fence.finishResetting();
            break;
        }
    }

    return true;
}


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

297
298
299
300
/************************************************
 * SceneOpenGL
 ***********************************************/

301
302
SceneOpenGL::SceneOpenGL(OpenGLBackend *backend, QObject *parent)
    : Scene(parent)
303
    , init_ok(true)
304
    , m_backend(backend)
305
306
    , m_syncManager(nullptr)
    , m_currentFence(nullptr)
307
308
{
    if (m_backend->isFailed()) {
309
        init_ok = false;
310
311
        return;
    }
312
    if (!viewportLimitsMatched(screens()->size()))
313
        return;
314
315
316

    // perform Scene specific checks
    GLPlatform *glPlatform = GLPlatform::instance();
317
    if (!glPlatform->isGLES() && !hasGLExtension(QByteArrayLiteral("GL_ARB_texture_non_power_of_two"))
318
            && !hasGLExtension(QByteArrayLiteral("GL_ARB_texture_rectangle"))) {
319
        qCCritical(KWIN_OPENGL) << "GL_ARB_texture_non_power_of_two and GL_ARB_texture_rectangle missing";
320
        init_ok = false;
321
322
        return; // error
    }
323
    if (glPlatform->isMesaDriver() && glPlatform->mesaVersion() < kVersionNumber(10, 0)) {
324
        qCCritical(KWIN_OPENGL) << "KWin requires at least Mesa 10.0 for OpenGL compositing.";
325
        init_ok = false;
326
327
328
        return;
    }

329
    m_debug = qstrcmp(qgetenv("KWIN_GL_DEBUG"), "1") == 0;
Fredrik Höglund's avatar
Fredrik Höglund committed
330
    initDebugOutput();
331
332
333
334
335

    // set strict binding
    if (options->isGlStrictBindingFollowsDriver()) {
        options->setGlStrictBinding(!glPlatform->supports(LooseBinding));
    }
336
337
338
339
340

    bool haveSyncObjects = glPlatform->isGLES()
        ? hasGLVersion(3, 0)
        : hasGLVersion(3, 2) || hasGLExtension("GL_ARB_sync");

341
    if (hasGLExtension("GL_EXT_x11_sync_object") && haveSyncObjects && kwinApp()->operationMode() == Application::OperationModeX11) {
342
343
344
        const QByteArray useExplicitSync = qgetenv("KWIN_EXPLICIT_SYNC");

        if (useExplicitSync != "0") {
345
            qCDebug(KWIN_OPENGL) << "Initializing fences for synchronization with the X command stream";
346
347
            m_syncManager = new SyncManager;
        } else {
348
            qCDebug(KWIN_OPENGL) << "Explicit synchronization with the X command stream disabled by environment variable";
349
350
        }
    }
351
352
353
354
}

SceneOpenGL::~SceneOpenGL()
{
355
356
    if (init_ok) {
        makeOpenGLContextCurrent();
357
    }
358
359
360
361
362
363
    SceneOpenGL::EffectFrame::cleanup();

    delete m_syncManager;

    // backend might be still needed for a different scene
    delete m_backend;
364
365
}

366

Fredrik Höglund's avatar
Fredrik Höglund committed
367
368
369
void SceneOpenGL::initDebugOutput()
{
    const bool have_KHR_debug = hasGLExtension(QByteArrayLiteral("GL_KHR_debug"));
370
371
    const bool have_ARB_debug = hasGLExtension(QByteArrayLiteral("GL_ARB_debug_output"));
    if (!have_KHR_debug && !have_ARB_debug)
Fredrik Höglund's avatar
Fredrik Höglund committed
372
373
        return;

374
375
376
377
378
379
380
381
    if (!have_ARB_debug) {
        // if we don't have ARB debug, but only KHR debug we need to verify whether the context is a debug context
        // it should work without as well, but empirical tests show: no it doesn't
        if (GLPlatform::instance()->isGLES()) {
            if (!hasGLVersion(3, 2)) {
                // empirical data shows extension doesn't work
                return;
            }
382
383
        } else if (!hasGLVersion(3, 0)) {
            return;
384
        }
385
        // can only be queried with either OpenGL >= 3.0 or OpenGL ES of at least 3.1
386
387
388
389
390
391
392
        GLint value = 0;
        glGetIntegerv(GL_CONTEXT_FLAGS, &value);
        if (!(value & GL_CONTEXT_FLAG_DEBUG_BIT)) {
            return;
        }
    }

Fredrik Höglund's avatar
Fredrik Höglund committed
393
394
395
396
397
398
399
400
    // Set the callback function
    auto callback = [](GLenum source, GLenum type, GLuint id,
                       GLenum severity, GLsizei length,
                       const GLchar *message,
                       const GLvoid *userParam) {
        Q_UNUSED(source)
        Q_UNUSED(severity)
        Q_UNUSED(userParam)
401
        while (length && std::isspace(message[length - 1])) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
402
            --length;
403
        }
Fredrik Höglund's avatar
Fredrik Höglund committed
404
405
406
407

        switch (type) {
        case GL_DEBUG_TYPE_ERROR:
        case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
408
            qCWarning(KWIN_OPENGL, "%#x: %.*s", id, length, message);
Fredrik Höglund's avatar
Fredrik Höglund committed
409
410
            break;

411
        case GL_DEBUG_TYPE_OTHER:
Fredrik Höglund's avatar
Fredrik Höglund committed
412
413
414
415
        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        case GL_DEBUG_TYPE_PORTABILITY:
        case GL_DEBUG_TYPE_PERFORMANCE:
        default:
416
            qCDebug(KWIN_OPENGL, "%#x: %.*s", id, length, message);
Fredrik Höglund's avatar
Fredrik Höglund committed
417
418
419
420
421
422
423
424
425
426
            break;
        }
    };

    glDebugMessageCallback(callback, nullptr);

    // This state exists only in GL_KHR_debug
    if (have_KHR_debug)
        glEnable(GL_DEBUG_OUTPUT);

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
427
#if !defined(QT_NO_DEBUG)
Fredrik Höglund's avatar
Fredrik Höglund committed
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    // Enable all debug messages
    glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE);
#else
    // Enable error messages
    glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_ERROR, GL_DONT_CARE, 0, nullptr, GL_TRUE);
    glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR, GL_DONT_CARE, 0, nullptr, GL_TRUE);
#endif

    // Insert a test message
    const QByteArray message = QByteArrayLiteral("OpenGL debug output initialized");
    glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_OTHER, 0,
                         GL_DEBUG_SEVERITY_LOW, message.length(), message.constData());
}

442
SceneOpenGL *SceneOpenGL::createScene(QObject *parent)
443
{
444
    OpenGLBackend *backend = kwinApp()->platform()->createOpenGLBackend();
445
446
447
448
449
450
451
    if (!backend) {
        return nullptr;
    }
    if (!backend->isFailed()) {
        backend->init();
    }
    if (backend->isFailed()) {
452
        delete backend;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
453
        return nullptr;
454
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
455
    SceneOpenGL *scene = nullptr;
456
457
    // first let's try an OpenGL 2 scene
    if (SceneOpenGL2::supported(backend)) {
458
        scene = new SceneOpenGL2(backend, parent);
459
460
        if (scene->initFailed()) {
            delete scene;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
461
            scene = nullptr;
462
463
464
465
466
        } else {
            return scene;
        }
    }
    if (!scene) {
467
        if (GLPlatform::instance()->recommendedCompositor() == XRenderCompositing) {
468
469
            qCCritical(KWIN_OPENGL) << "OpenGL driver recommends XRender based compositing. Falling back to XRender.";
            qCCritical(KWIN_OPENGL) << "To overwrite the detection use the environment variable KWIN_COMPOSE";
Volker Krause's avatar
Volker Krause committed
470
            qCCritical(KWIN_OPENGL) << "For more information see https://community.kde.org/KWin/Environment_Variables#KWIN_COMPOSE";
471
        }
472
473
474
475
476
477
        delete backend;
    }

    return scene;
}

478
OverlayWindow *SceneOpenGL::overlayWindow() const
479
480
481
482
{
    return m_backend->overlayWindow();
}

483
bool SceneOpenGL::initFailed() const
484
{
485
    return !init_ok;
486
}
487

488
489
490
void SceneOpenGL::handleGraphicsReset(GLenum status)
{
    switch (status) {
491
    case GL_GUILTY_CONTEXT_RESET:
492
        qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred.";
493
494
        break;

495
    case GL_INNOCENT_CONTEXT_RESET:
496
        qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred.";
497
498
        break;

499
    case GL_UNKNOWN_CONTEXT_RESET:
500
        qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred.";
501
502
503
504
505
506
507
508
509
510
        break;

    default:
        break;
    }

    QElapsedTimer timer;
    timer.start();

    // Wait until the reset is completed or max 10 seconds
511
    while (timer.elapsed() < 10000 && glGetGraphicsResetStatus() != GL_NO_ERROR)
512
513
        usleep(50);

514
    qCDebug(KWIN_OPENGL) << "Attempting to reset compositing.";
515
516
    QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection);

517
    KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
518
519

    m_resetOccurred = true;
520
521
}

522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537

void SceneOpenGL::triggerFence()
{
    if (m_syncManager) {
        m_currentFence = m_syncManager->nextFence();
        m_currentFence->trigger();
    }
}

void SceneOpenGL::insertWait()
{
    if (m_currentFence && m_currentFence->state() != SyncObject::Waiting) {
        m_currentFence->wait();
    }
}

538
539
540
/**
 * Render cursor texture in case hardware cursor is disabled.
 * Useful for screen recording apps or backends that can't do planes.
541
 */
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
542
void SceneOpenGL2::paintCursor(const QRegion &rendered)
543
{
544
545
    Cursor* cursor = Cursors::self()->currentCursor();

546
547
548
    // don't paint if we use hardware cursor or the cursor is hidden
    if (!kwinApp()->platform()->usesSoftwareCursor() ||
        kwinApp()->platform()->isCursorHidden() ||
549
        cursor->image().isNull()) {
550
551
552
        return;
    }

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
553
554
    // figure out which part of the cursor needs to be repainted
    const QPoint cursorPos = cursor->pos() - cursor->hotspot();
555
    const QRect cursorRect = cursor->rect();
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
556
557
558
559
560
561
562
563
    QRegion region;
    for (const QRect &rect : rendered) {
        region |= rect.translated(-cursorPos).intersected(cursorRect);
    }
    if (region.isEmpty()) {
        return;
    }

564
565
566
567
    // lazy init texture cursor only in case we need software rendering
    if (!m_cursorTexture) {
        auto updateCursorTexture = [this] {
            // don't paint if no image for cursor is set
568
            const QImage img = Cursors::self()->currentCursor()->image();
569
570
571
572
            if (img.isNull()) {
                return;
            }
            m_cursorTexture.reset(new GLTexture(img));
573
            m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE);
574
575
576
577
578
579
        };

        // init now
        updateCursorTexture();

        // handle shape update on case cursor image changed
580
        connect(Cursors::self(), &Cursors::currentCursorChanged, this, updateCursorTexture);
581
582
583
584
585
586
587
588
589
590
591
592
593
594
    }

    // get cursor position in projection coordinates
    QMatrix4x4 mvp = m_projectionMatrix;
    mvp.translate(cursorPos.x(), cursorPos.y());

    // handle transparence
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // paint texture in cursor offset
    m_cursorTexture->bind();
    ShaderBinder binder(ShaderTrait::MapTexture);
    binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
595
    m_cursorTexture->render(region, cursorRect);
596
597
598
599
    m_cursorTexture->unbind();
    glDisable(GL_BLEND);
}

600
void SceneOpenGL::aboutToStartPainting(int screenId, const QRegion &damage)
601
{
602
    m_backend->aboutToStartPainting(screenId, damage);
603
604
}

605
void SceneOpenGL::paint(int screenId, const QRegion &damage, const QList<Toplevel *> &toplevels,
606
                        RenderLoop *renderLoop)
607
{
608
609
610
611
612
    if (m_resetOccurred) {
        return; // A graphics reset has occurred, do nothing.
    }

    painted_screen = screenId;
613
    // actually paint the frame, flushed with the NEXT frame
614
    createStackingOrder(toplevels);
615

616
617
618
619
620
621
622
    QRegion update;
    QRegion valid;
    QRegion repaint;
    QRect geo;
    qreal scaling;

    // prepare rendering makes context current on the output
623
    repaint = m_backend->beginFrame(screenId);
624
625
626
627
628
629
630
    if (screenId != -1) {
        geo = screens()->geometry(screenId);
        scaling = screens()->scale(screenId);
    } else {
        geo = screens()->geometry();
        scaling = 1;
    }
631

632
633
634
635
    GLVertexBuffer::setVirtualScreenGeometry(geo);
    GLRenderTarget::setVirtualScreenGeometry(geo);
    GLVertexBuffer::setVirtualScreenScale(scaling);
    GLRenderTarget::setVirtualScreenScale(scaling);
636

637
638
639
    const GLenum status = glGetGraphicsResetStatus();
    if (status != GL_NO_ERROR) {
        handleGraphicsReset(status);
640
    } else {
641
        int mask = 0;
642
        updateProjectionMatrix();
643
        renderLoop->beginFrame();
644

645
        paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid,
646
                    renderLoop, projectionMatrix(), geo, scaling);   // call generic implementation
647
648
649
        paintCursor(valid);

        if (!GLPlatform::instance()->isGLES() && screenId == -1) {
650
651
652
653
654
655
            const QSize &screenSize = screens()->size();
            const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());

            // copy dirty parts from front to backbuffer
            if (!m_backend->supportsBufferAge() &&
                options->glPreferBufferSwap() == Options::CopyFrontBuffer &&
656
                valid != displayRegion) {
657
                glReadBuffer(GL_FRONT);
658
                m_backend->copyPixels(displayRegion - valid);
659
                glReadBuffer(GL_BACK);
660
                valid = displayRegion;
661
            }
662
        }
663

664
665
        renderLoop->endFrame();

666
        GLVertexBuffer::streamingBuffer()->endOfFrame();
667
        m_backend->endFrame(screenId, valid, update);
668
        GLVertexBuffer::streamingBuffer()->framePosted();
669

670
671
672
673
674
675
676
677
        if (m_currentFence) {
            if (!m_syncManager->updateFences()) {
                qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream.";
                qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized.";
                delete m_syncManager;
                m_syncManager = nullptr;
            }
            m_currentFence = nullptr;
678
679
680
        }
    }

681
    // do cleanup
682
    clearStackingOrder();
683
684
}

685
686
687
688
689
690
691
QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const
{
    QMatrix4x4 matrix;

    if (!(mask & PAINT_SCREEN_TRANSFORMED))
        return matrix;

692
    matrix.translate(data.translation());
693
694
    const QVector3D scale = data.scale();
    matrix.scale(scale.x(), scale.y(), scale.z());
695

696
    if (data.rotationAngle() == 0.0)
697
698
699
        return matrix;

    // Apply the rotation
700
    // cannot use data.rotation->applyTo(&matrix) as QGraphicsRotation uses projectedRotate to map back to 2D
701
702
703
704
    matrix.translate(data.rotationOrigin());
    const QVector3D axis = data.rotationAxis();
    matrix.rotate(data.rotationAngle(), axis.x(), axis.y(), axis.z());
    matrix.translate(-data.rotationOrigin());
705
706
707
708

    return matrix;
}

709
void SceneOpenGL::paintBackground(const QRegion &region)
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
{
    PaintClipper pc(region);
    if (!PaintClipper::clip()) {
        glClearColor(0, 0, 0, 1);
        glClear(GL_COLOR_BUFFER_BIT);
        return;
    }
    if (pc.clip() && pc.paintArea().isEmpty())
        return; // no background to paint
    QVector<float> verts;
    for (PaintClipper::Iterator iterator; !iterator.isDone(); iterator.next()) {
        QRect r = iterator.boundingRect();
        verts << r.x() + r.width() << r.y();
        verts << r.x() << r.y();
        verts << r.x() << r.y() + r.height();
        verts << r.x() << r.y() + r.height();
        verts << r.x() + r.width() << r.y() + r.height();
        verts << r.x() + r.width() << r.y();
    }
729
    doPaintBackground(verts);
730
731
}

732
733
void SceneOpenGL::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
734
735
736
    if (m_backend->supportsBufferAge())
        return;

737
    const QSize &screenSize = screens()->size();
738
    if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints
739
        const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
740
        uint damagedPixels = 0;
741
        const uint fullRepaintLimit = (opaqueFullscreen?0.49f:0.748f)*screenSize.width()*screenSize.height();
742
743
744
745
746
        // 16:9 is 75% of 4:3 and 2.55:1 is 49.01% of 5:4
        // (5:4 is the most square format and 2.55:1 is Cinemascope55 - the widest ever shot
        // movie aspect - two times ;-) It's a Fox format, though, so maybe we want to restrict
        // to 2.20:1 - Panavision - which has actually been used for interesting movies ...)
        // would be 57% of 5/4
747
        for (const QRect &r : region) {
748
749
750
751
752
753
754
755
//                 damagedPixels += r.width() * r.height(); // combined window damage test
            damagedPixels = r.width() * r.height(); // experimental single window damage testing
            if (damagedPixels > fullRepaintLimit) {
                region = displayRegion;
                return;
            }
        }
    } else if (options->glPreferBufferSwap() == Options::PaintFullScreen) { // forced full rePaint
756
        region = QRegion(0, 0, screenSize.width(), screenSize.height());
757
758
759
    }
}

760
SceneOpenGLTexture *SceneOpenGL::createTexture()
761
{
762
    return new SceneOpenGLTexture(m_backend);
763
}
764

765
bool SceneOpenGL::viewportLimitsMatched(const QSize &size) const {
766
767
768
769
    if (kwinApp()->operationMode() != Application::OperationModeX11) {
        // TODO: On Wayland we can't suspend. Find a solution that works here as well!
        return true;
    }
770
771
772
    GLint limit[2];
    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, limit);
    if (limit[0] < size.width() || limit[1] < size.height()) {
773
774
        auto compositor = static_cast<X11Compositor*>(Compositor::self());
        QMetaObject::invokeMethod(compositor, [compositor]() {
775
776
777
            qCDebug(KWIN_OPENGL) << "Suspending compositing because viewport limits are not met";
            compositor->suspend(X11Compositor::AllReasonSuspend);
        }, Qt::QueuedConnection);
778
779
780
781
782
        return false;
    }
    return true;
}

783
void SceneOpenGL::screenGeometryChanged(const QSize &size)
784
{
785
786
    if (!viewportLimitsMatched(size))
        return;
787
788
789
    Scene::screenGeometryChanged(size);
    glViewport(0,0, size.width(), size.height());
    m_backend->screenGeometryChanged(size);
790
    GLRenderTarget::setVirtualScreenSize(size);
791
792
}

793
794
795
796
void SceneOpenGL::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
{
    const QRect r = region.boundingRect();
    glEnable(GL_SCISSOR_TEST);
797
    glScissor(r.x(), screens()->size().height() - r.y() - r.height(), r.width(), r.height());
798
799
800
801
    KWin::Scene::paintDesktop(desktop, mask, region, data);
    glDisable(GL_SCISSOR_TEST);
}

802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
void SceneOpenGL::paintEffectQuickView(EffectQuickView *w)
{
    GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
    const QRect rect = w->geometry();

    GLTexture *t = w->bufferAsTexture();
    if (!t) {
        return;
    }

    QMatrix4x4 mvp(projectionMatrix());
    mvp.translate(rect.x(), rect.y());
    shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp);

    glEnable(GL_BLEND);
817
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
818
819
820
821
    t->bind();
    t->render(QRegion(infiniteRegion()), w->geometry());
    t->unbind();
    glDisable(GL_BLEND);
822
823

    ShaderManager::instance()->popShader();
824
825
}

826
827
828
829
830
831
832
833
834
835
bool SceneOpenGL::makeOpenGLContextCurrent()
{
    return m_backend->makeCurrent();
}

void SceneOpenGL::doneOpenGLContextCurrent()
{
    m_backend->doneCurrent();
}

836
837
838
839
840
bool SceneOpenGL::supportsSurfacelessContext() const
{
    return m_backend->supportsSurfacelessContext();
}

841
842
843
844
845
bool SceneOpenGL::supportsNativeFence() const
{
    return m_backend->supportsNativeFence();
}

846
847
848
849
850
Scene::EffectFrame *SceneOpenGL::createEffectFrame(EffectFrameImpl *frame)
{
    return new SceneOpenGL::EffectFrame(frame, this);
}

851
852
853
854
855
Shadow *SceneOpenGL::createShadow(Toplevel *toplevel)
{
    return new SceneOpenGLShadow(toplevel);
}

856
857
858
859
860
Decoration::Renderer *SceneOpenGL::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
    return new SceneOpenGLDecorationRenderer(impl);
}

861
862
863
864
865
bool SceneOpenGL::animationsSupported() const
{
    return !GLPlatform::instance()->isSoftwareEmulation();
}

866
867
868
869
870
QVector<QByteArray> SceneOpenGL::openGLPlatformInterfaceExtensions() const
{
    return m_backend->extensions().toVector();
}

871
872
873
874
875
QSharedPointer<GLTexture> SceneOpenGL::textureForOutput(AbstractOutput* output) const
{
    return m_backend->textureForOutput(output);
}

876
877
878
879
880
//****************************************
// SceneOpenGL2
//****************************************
bool SceneOpenGL2::supported(OpenGLBackend *backend)
{
881
882
    const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
    if (!forceEnv.isEmpty()) {
883
        if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
884
            qCDebug(KWIN_OPENGL) << "OpenGL 2 compositing enforced by environment variable";
885
886
887
888
889
890
            return true;
        } else {
            // OpenGL 2 disabled by environment variable
            return false;
        }
    }
891
892
893
    if (!backend->isDirectRendering()) {
        return false;
    }
894
    if (GLPlatform::instance()->recommendedCompositor() < OpenGL2Compositing) {
895
        qCDebug(KWIN_OPENGL) << "Driver does not recommend OpenGL 2 compositing";
896
897
898
899
900
        return false;
    }
    return true;
}

901
902
SceneOpenGL2::SceneOpenGL2(OpenGLBackend *backend, QObject *parent)
    : SceneOpenGL(backend, parent)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
903
    , m_lanczosFilter(nullptr)
904
{
905
906
907
908
    if (!init_ok) {
        // base ctor already failed
        return;
    }
909
910
911

    // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
    if (!hasGLVersion(2, 0)) {
912
        qCDebug(KWIN_OPENGL) << "OpenGL 2.0 is not supported";
913
914
915
916
        init_ok = false;
        return;
    }

917
918
    const QSize &s = screens()->size();
    GLRenderTarget::setVirtualScreenSize(s);
919
    GLRenderTarget::setVirtualScreenGeometry(screens()->geometry());
920
921

    // push one shader on the stack so that one is always bound
922
    ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
923
    if (checkGLError("Init")) {
924
        qCCritical(KWIN_OPENGL) << "OpenGL 2 compositing setup failed";
925
        init_ok = false;
926
927
        return; // error
    }
928
929

    // It is not legal to not have a vertex array object bound in a core context
930
    if (!GLPlatform::instance()->isGLES() && hasGLExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
931
932
933
934
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    }

935
    if (!ShaderManager::instance()->selfTest()) {
936
        qCCritical(KWIN_OPENGL) << "ShaderManager self test failed";
937
938
939
940
        init_ok = false;
        return;
    }

941
    qCDebug(KWIN_OPENGL) << "OpenGL 2 compositing successfully initialized";
942
    init_ok = true;
943
944
945
946
}

SceneOpenGL2::~SceneOpenGL2()
{
947
948
949
950
951
    if (m_lanczosFilter) {
        makeOpenGLContextCurrent();
        delete m_lanczosFilter;
        m_lanczosFilter = nullptr;
    }
952
953
}

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
QMatrix4x4 SceneOpenGL2::createProjectionMatrix() const
{
    // Create a perspective projection with a 60° field-of-view,
    // and an aspect ratio of 1.0.
    const float fovY   =   60.0f;
    const float aspect =    1.0f;
    const float zNear  =    0.1f;
    const float zFar   =  100.0f;

    const float yMax   =  zNear * std::tan(fovY * M_PI / 360.0f);
    const float yMin   = -yMax;
    const float xMin   =  yMin * aspect;
    const float xMax   =  yMax * aspect;

    QMatrix4x4 projection;
    projection.frustum(xMin, xMax, yMin, yMax, zNear, zFar);

    // Create a second matrix that transforms screen coordinates
    // to world coordinates.
    const float scaleFactor = 1.1 * std::tan(fovY * M_PI / 360.0f) / yMax;
    const QSize size = screens()->size();

    QMatrix4x4 matrix;
    matrix.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
    matrix.scale( (xMax - xMin) * scaleFactor / size.width(),
                 -(yMax - yMin) * scaleFactor / size.height(),
                  0.001);

    // Combine the matrices
    return projection * matrix;
}

986
void SceneOpenGL2::updateProjectionMatrix()
987
988
{
    m_projectionMatrix = createProjectionMatrix();
989
990
}

991
void SceneOpenGL2::paintSimpleScreen(int mask, const QRegion &region)
992
{
993
994
995
996
997
    m_screenProjectionMatrix = m_projectionMatrix;

    Scene::paintSimpleScreen(mask, region);
}

998
void SceneOpenGL2::paintGenericScreen(int mask, const ScreenPaintData &data)
999
{
1000
    const QMatrix4x4 screenMatrix = transformation(mask, data);
For faster browsing, not all history is shown. View entire blame