scene_opengl.cpp 93.8 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
20
#include "scene_opengl.h"

21
#include "platform.h"
22
#include "wayland_server.h"
23
#include "platformsupport/scenes/opengl/texture.h"
24

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 "screens.h"
37
#include "cursor.h"
38
#include "decorations/decoratedclient.h"
39
#include <logging.h>
40

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

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

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

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

64
65
66
67
68
69
70
71
72
73
74
// 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

75
76
77
namespace KWin
{

78
79
extern int currentRefreshRate();

80
81
82
83

/**
 * SyncObject represents a fence used to synchronize operations in
 * the kwin command stream with operations in the X command stream.
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
121
122
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()
{
123
124
125
126
127
128
129
130
131
132
133
    // 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());
    }
134
135
136
137
138
139
140
141
142
    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
143
    Q_ASSERT(m_state == Ready || m_state == Resetting);
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

    // 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
170
    Q_ASSERT(m_state == TriggerSent || m_state == Waiting);
171
172
173
174
175
176

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

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

        // 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:
184
            qCWarning(KWIN_OPENGL) << "Timeout while waiting for X fence";
185
186
187
            return false;

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

    m_state = Done;
    return true;
}

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

    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
217
    Q_ASSERT(m_state == Resetting);
218
219
220
221
222
223
224
225
226
227
228
229
230
    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.
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
297
298
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;
}


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

299
300
301
302
/************************************************
 * SceneOpenGL
 ***********************************************/

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

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

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

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

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

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

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

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

    delete m_syncManager;

    // backend might be still needed for a different scene
    delete m_backend;
366
367
}

368

Fredrik Höglund's avatar
Fredrik Höglund committed
369
370
371
void SceneOpenGL::initDebugOutput()
{
    const bool have_KHR_debug = hasGLExtension(QByteArrayLiteral("GL_KHR_debug"));
372
373
    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
374
375
        return;

376
377
378
379
380
381
382
383
    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;
            }
384
385
        } else if (!hasGLVersion(3, 0)) {
            return;
386
        }
387
        // can only be queried with either OpenGL >= 3.0 or OpenGL ES of at least 3.1
388
389
390
391
392
393
394
        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
395
396
397
398
399
400
401
402
    // 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)
403
        while (length && std::isspace(message[length - 1])) {
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
404
            --length;
405
        }
Fredrik Höglund's avatar
Fredrik Höglund committed
406
407
408
409

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

413
        case GL_DEBUG_TYPE_OTHER:
Fredrik Höglund's avatar
Fredrik Höglund committed
414
415
416
417
        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
        case GL_DEBUG_TYPE_PORTABILITY:
        case GL_DEBUG_TYPE_PERFORMANCE:
        default:
418
            qCDebug(KWIN_OPENGL, "%#x: %.*s", id, length, message);
Fredrik Höglund's avatar
Fredrik Höglund committed
419
420
421
422
423
424
425
426
427
428
            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
429
#if !defined(QT_NO_DEBUG)
Fredrik Höglund's avatar
Fredrik Höglund committed
430
431
432
433
434
435
436
437
438
439
440
441
442
443
    // 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());
}

444
SceneOpenGL *SceneOpenGL::createScene(QObject *parent)
445
{
446
    OpenGLBackend *backend = kwinApp()->platform()->createOpenGLBackend();
447
448
449
450
451
452
453
    if (!backend) {
        return nullptr;
    }
    if (!backend->isFailed()) {
        backend->init();
    }
    if (backend->isFailed()) {
454
        delete backend;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
455
        return nullptr;
456
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
457
    SceneOpenGL *scene = nullptr;
458
459
    // first let's try an OpenGL 2 scene
    if (SceneOpenGL2::supported(backend)) {
460
        scene = new SceneOpenGL2(backend, parent);
461
462
        if (scene->initFailed()) {
            delete scene;
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
463
            scene = nullptr;
464
465
466
467
468
        } else {
            return scene;
        }
    }
    if (!scene) {
469
        if (GLPlatform::instance()->recommendedCompositor() == XRenderCompositing) {
470
471
            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
472
            qCCritical(KWIN_OPENGL) << "For more information see https://community.kde.org/KWin/Environment_Variables#KWIN_COMPOSE";
473
        }
474
475
476
477
478
479
        delete backend;
    }

    return scene;
}

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

485
486
487
488
489
bool SceneOpenGL::syncsToVBlank() const
{
    return m_backend->syncsToVBlank();
}

490
491
492
493
494
bool SceneOpenGL::blocksForRetrace() const
{
    return m_backend->blocksForRetrace();
}

495
496
void SceneOpenGL::idle()
{
497
    m_backend->idle();
498
499
500
    Scene::idle();
}

501
bool SceneOpenGL::initFailed() const
502
{
503
    return !init_ok;
504
}
505

506
507
508
void SceneOpenGL::handleGraphicsReset(GLenum status)
{
    switch (status) {
509
    case GL_GUILTY_CONTEXT_RESET:
510
        qCDebug(KWIN_OPENGL) << "A graphics reset attributable to the current GL context occurred.";
511
512
        break;

513
    case GL_INNOCENT_CONTEXT_RESET:
514
        qCDebug(KWIN_OPENGL) << "A graphics reset not attributable to the current GL context occurred.";
515
516
        break;

517
    case GL_UNKNOWN_CONTEXT_RESET:
518
        qCDebug(KWIN_OPENGL) << "A graphics reset of an unknown cause occurred.";
519
520
521
522
523
524
525
526
527
528
        break;

    default:
        break;
    }

    QElapsedTimer timer;
    timer.start();

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

532
    qCDebug(KWIN_OPENGL) << "Attempting to reset compositing.";
533
534
    QMetaObject::invokeMethod(this, "resetCompositing", Qt::QueuedConnection);

535
    KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
536
537
}

538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

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();
    }
}

554
555
556
/**
 * Render cursor texture in case hardware cursor is disabled.
 * Useful for screen recording apps or backends that can't do planes.
557
 */
558
559
void SceneOpenGL2::paintCursor()
{
560
561
    Cursor* cursor = Cursors::self()->currentCursor();

562
563
564
    // don't paint if we use hardware cursor or the cursor is hidden
    if (!kwinApp()->platform()->usesSoftwareCursor() ||
        kwinApp()->platform()->isCursorHidden() ||
565
        cursor->image().isNull()) {
566
567
568
569
570
571
572
        return;
    }

    // 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
573
            const QImage img = Cursors::self()->currentCursor()->image();
574
575
576
577
578
579
580
581
582
583
            if (img.isNull()) {
                return;
            }
            m_cursorTexture.reset(new GLTexture(img));
        };

        // init now
        updateCursorTexture();

        // handle shape update on case cursor image changed
584
        connect(Cursors::self(), &Cursors::currentCursorChanged, this, updateCursorTexture);
585
586
587
    }

    // get cursor position in projection coordinates
588
    const qreal scale = cursor->image().devicePixelRatio();
589
    const QPoint cursorPos = cursor->pos() - cursor->hotspot();
590
    const QRect cursorRect(QPoint(0, 0), m_cursorTexture->size() / scale);
591
592
593
594
595
596
597
598
599
600
601
602
603
604
    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);
    m_cursorTexture->render(QRegion(cursorRect), cursorRect);
    m_cursorTexture->unbind();

605
    cursor->markAsRendered();
606
607
608
609

    glDisable(GL_BLEND);
}

610
611
612
613
614
void SceneOpenGL::aboutToStartPainting(const QRegion &damage)
{
    m_backend->aboutToStartPainting(damage);
}

615
qint64 SceneOpenGL::paint(const QRegion &damage, const QList<Toplevel *> &toplevels)
616
{
617
    // actually paint the frame, flushed with the NEXT frame
618
    createStackingOrder(toplevels);
619

620
621
622
623
624
625
    // After this call, updateRegion will contain the damaged region in the
    // back buffer. This is the region that needs to be posted to repair
    // the front buffer. It doesn't include the additional damage returned
    // by prepareRenderingFrame(). validRegion is the region that has been
    // repainted, and may be larger than updateRegion.
    QRegion updateRegion, validRegion;
626
    if (m_backend->perScreenRendering()) {
627
628
        // trigger start render timer
        m_backend->prepareRenderingFrame();
629
630
        for (int i = 0; i < screens()->count(); ++i) {
            const QRect &geo = screens()->geometry(i);
631
            const qreal scaling = screens()->scale(i);
632
633
            QRegion update;
            QRegion valid;
634
635
            // prepare rendering makes context current on the output
            QRegion repaint = m_backend->prepareRenderingForScreen(i);
636
            GLVertexBuffer::setVirtualScreenGeometry(geo);
637
            GLRenderTarget::setVirtualScreenGeometry(geo);
638
639
            GLVertexBuffer::setVirtualScreenScale(scaling);
            GLRenderTarget::setVirtualScreenScale(scaling);
640

641
            const GLenum status = glGetGraphicsResetStatus();
642
643
644
645
646
647
            if (status != GL_NO_ERROR) {
                handleGraphicsReset(status);
                return 0;
            }

            int mask = 0;
648
            updateProjectionMatrix();
649
650

            paintScreen(&mask, damage.intersected(geo), repaint, &update, &valid, projectionMatrix(), geo, scaling);   // call generic implementation
651
            paintCursor();
652
653
654
655
656
657

            GLVertexBuffer::streamingBuffer()->endOfFrame();

            m_backend->endRenderingFrameForScreen(i, valid, update);

            GLVertexBuffer::streamingBuffer()->framePosted();
658
659
        }
    } else {
660
661
662
        m_backend->makeCurrent();
        QRegion repaint = m_backend->prepareRenderingFrame();

663
        const GLenum status = glGetGraphicsResetStatus();
664
665
666
667
        if (status != GL_NO_ERROR) {
            handleGraphicsReset(status);
            return 0;
        }
668
        GLVertexBuffer::setVirtualScreenGeometry(screens()->geometry());
669
        GLRenderTarget::setVirtualScreenGeometry(screens()->geometry());
David Edmundson's avatar
David Edmundson committed
670
        GLVertexBuffer::setVirtualScreenScale(1);
David Edmundson's avatar
David Edmundson committed
671
        GLRenderTarget::setVirtualScreenScale(1);
672
673

        int mask = 0;
674
        updateProjectionMatrix();
675
        paintScreen(&mask, damage, repaint, &updateRegion, &validRegion, projectionMatrix());   // call generic implementation
676

677
678
679
680
681
682
683
684
685
        if (!GLPlatform::instance()->isGLES()) {
            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 &&
                validRegion != displayRegion) {
                glReadBuffer(GL_FRONT);
686
                m_backend->copyPixels(displayRegion - validRegion);
687
688
689
                glReadBuffer(GL_BACK);
                validRegion = displayRegion;
            }
690
        }
691

692
        GLVertexBuffer::streamingBuffer()->endOfFrame();
693

694
        m_backend->endRenderingFrame(validRegion, updateRegion);
695

696
697
        GLVertexBuffer::streamingBuffer()->framePosted();
    }
698

699
700
    if (m_currentFence) {
        if (!m_syncManager->updateFences()) {
701
702
            qCDebug(KWIN_OPENGL) << "Aborting explicit synchronization with the X command stream.";
            qCDebug(KWIN_OPENGL) << "Future frames will be rendered unsynchronized.";
703
704
705
706
707
708
            delete m_syncManager;
            m_syncManager = nullptr;
        }
        m_currentFence = nullptr;
    }

709
    // do cleanup
710
    clearStackingOrder();
711
712
713
    return m_backend->renderTime();
}

714
715
716
717
718
719
720
QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const
{
    QMatrix4x4 matrix;

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

721
    matrix.translate(data.translation());
722
    data.scale().applyTo(&matrix);
723

724
    if (data.rotationAngle() == 0.0)
725
726
727
        return matrix;

    // Apply the rotation
728
    // cannot use data.rotation->applyTo(&matrix) as QGraphicsRotation uses projectedRotate to map back to 2D
729
730
731
732
    matrix.translate(data.rotationOrigin());
    const QVector3D axis = data.rotationAxis();
    matrix.rotate(data.rotationAngle(), axis.x(), axis.y(), axis.z());
    matrix.translate(-data.rotationOrigin());
733
734
735
736

    return matrix;
}

737
void SceneOpenGL::paintBackground(const QRegion &region)
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
{
    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();
    }
757
    doPaintBackground(verts);
758
759
}

760
761
void SceneOpenGL::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
762
763
764
    if (m_backend->supportsBufferAge())
        return;

765
    const QSize &screenSize = screens()->size();
766
    if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints
767
        const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
768
        uint damagedPixels = 0;
769
        const uint fullRepaintLimit = (opaqueFullscreen?0.49f:0.748f)*screenSize.width()*screenSize.height();
770
771
772
773
774
        // 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
775
        for (const QRect &r : region) {
776
777
778
779
780
781
782
783
//                 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
784
        region = QRegion(0, 0, screenSize.width(), screenSize.height());
785
786
787
    }
}

788
SceneOpenGLTexture *SceneOpenGL::createTexture()
789
{
790
    return new SceneOpenGLTexture(m_backend);
791
}
792

793
bool SceneOpenGL::viewportLimitsMatched(const QSize &size) const {
794
795
796
797
    if (kwinApp()->operationMode() != Application::OperationModeX11) {
        // TODO: On Wayland we can't suspend. Find a solution that works here as well!
        return true;
    }
798
799
800
    GLint limit[2];
    glGetIntegerv(GL_MAX_VIEWPORT_DIMS, limit);
    if (limit[0] < size.width() || limit[1] < size.height()) {
801
802
        auto compositor = static_cast<X11Compositor*>(Compositor::self());
        QMetaObject::invokeMethod(compositor, [compositor]() {
803
804
805
            qCDebug(KWIN_OPENGL) << "Suspending compositing because viewport limits are not met";
            compositor->suspend(X11Compositor::AllReasonSuspend);
        }, Qt::QueuedConnection);
806
807
808
809
810
        return false;
    }
    return true;
}

811
void SceneOpenGL::screenGeometryChanged(const QSize &size)
812
{
813
814
    if (!viewportLimitsMatched(size))
        return;
815
816
817
    Scene::screenGeometryChanged(size);
    glViewport(0,0, size.width(), size.height());
    m_backend->screenGeometryChanged(size);
818
    GLRenderTarget::setVirtualScreenSize(size);
819
820
}

821
822
823
824
void SceneOpenGL::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
{
    const QRect r = region.boundingRect();
    glEnable(GL_SCISSOR_TEST);
825
    glScissor(r.x(), screens()->size().height() - r.y() - r.height(), r.width(), r.height());
826
827
828
829
    KWin::Scene::paintDesktop(desktop, mask, region, data);
    glDisable(GL_SCISSOR_TEST);
}

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
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);
845
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
846
847
848
849
    t->bind();
    t->render(QRegion(infiniteRegion()), w->geometry());
    t->unbind();
    glDisable(GL_BLEND);
850
851

    ShaderManager::instance()->popShader();
852
853
}

854
855
856
857
858
859
860
861
862
863
bool SceneOpenGL::makeOpenGLContextCurrent()
{
    return m_backend->makeCurrent();
}

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

864
865
866
867
868
bool SceneOpenGL::supportsSurfacelessContext() const
{
    return m_backend->supportsSurfacelessContext();
}

869
870
871
872
873
bool SceneOpenGL::supportsNativeFence() const
{
    return m_backend->supportsNativeFence();
}

874
875
876
877
878
Scene::EffectFrame *SceneOpenGL::createEffectFrame(EffectFrameImpl *frame)
{
    return new SceneOpenGL::EffectFrame(frame, this);
}

879
880
881
882
883
Shadow *SceneOpenGL::createShadow(Toplevel *toplevel)
{
    return new SceneOpenGLShadow(toplevel);
}

884
885
886
887
888
Decoration::Renderer *SceneOpenGL::createDecorationRenderer(Decoration::DecoratedClientImpl *impl)
{
    return new SceneOpenGLDecorationRenderer(impl);
}

889
890
891
892
893
bool SceneOpenGL::animationsSupported() const
{
    return !GLPlatform::instance()->isSoftwareEmulation();
}

894
895
896
897
898
QVector<QByteArray> SceneOpenGL::openGLPlatformInterfaceExtensions() const
{
    return m_backend->extensions().toVector();
}

899
900
901
902
903
QSharedPointer<GLTexture> SceneOpenGL::textureForOutput(AbstractOutput* output) const
{
    return m_backend->textureForOutput(output);
}

904
905
906
907
908
//****************************************
// SceneOpenGL2
//****************************************
bool SceneOpenGL2::supported(OpenGLBackend *backend)
{
909
910
    const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
    if (!forceEnv.isEmpty()) {
911
        if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
912
            qCDebug(KWIN_OPENGL) << "OpenGL 2 compositing enforced by environment variable";
913
914
915
916
917
918
            return true;
        } else {
            // OpenGL 2 disabled by environment variable
            return false;
        }
    }
919
920
921
    if (!backend->isDirectRendering()) {
        return false;
    }
922
    if (GLPlatform::instance()->recommendedCompositor() < OpenGL2Compositing) {
923
        qCDebug(KWIN_OPENGL) << "Driver does not recommend OpenGL 2 compositing";
924
925
926
927
928
        return false;
    }
    return true;
}

929
930
SceneOpenGL2::SceneOpenGL2(OpenGLBackend *backend, QObject *parent)
    : SceneOpenGL(backend, parent)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
931
    , m_lanczosFilter(nullptr)
932
{
933
934
935
936
    if (!init_ok) {
        // base ctor already failed
        return;
    }
937
938
939

    // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
    if (!hasGLVersion(2, 0)) {
940
        qCDebug(KWIN_OPENGL) << "OpenGL 2.0 is not supported";
941
942
943
944
        init_ok = false;
        return;
    }

945
946
    const QSize &s = screens()->size();
    GLRenderTarget::setVirtualScreenSize(s);
947
    GLRenderTarget::setVirtualScreenGeometry(screens()->geometry());
948
949

    // push one shader on the stack so that one is always bound
950
    ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
951
    if (checkGLError("Init")) {
952
        qCCritical(KWIN_OPENGL) << "OpenGL 2 compositing setup failed";
953
        init_ok = false;
954
955
        return; // error
    }
956
957

    // It is not legal to not have a vertex array object bound in a core context
958
    if (!GLPlatform::instance()->isGLES() && hasGLExtension(QByteArrayLiteral("GL_ARB_vertex_array_object"))) {
959
960
961
962
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
    }

963
    if (!ShaderManager::instance()->selfTest()) {
964
        qCCritical(KWIN_OPENGL) << "ShaderManager self test failed";
965
966
967
968
        init_ok = false;
        return;
    }

969
    qCDebug(KWIN_OPENGL) << "OpenGL 2 compositing successfully initialized";
970
    init_ok = true;
971
972
973
974
}

SceneOpenGL2::~SceneOpenGL2()
{
975
976
977
978
979
    if (m_lanczosFilter) {
        makeOpenGLContextCurrent();
        delete m_lanczosFilter;
        m_lanczosFilter = nullptr;
    }
980
981
}

982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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.