drm_pipeline.cpp 21.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
    KWin - the KDE window manager
    This file is part of the KDE project.

    SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "drm_pipeline.h"

#include <errno.h>

#include "logging.h"
#include "drm_gpu.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
#include "drm_buffer.h"
#include "cursor.h"
#include "session.h"
22
#include "drm_output.h"
23
24
25
26
#include "drm_backend.h"
#include "egl_gbm_backend.h"
#include "drm_buffer_gbm.h"

Xaver Hugl's avatar
Xaver Hugl committed
27
#include <gbm.h>
Xaver Hugl's avatar
Xaver Hugl committed
28
29
#include <drm_fourcc.h>

30
31
32
namespace KWin
{

33
DrmPipeline::DrmPipeline(DrmConnector *conn)
34
    : m_output(nullptr)
35
36
    , m_connector(conn)
{
Xaver Hugl's avatar
Xaver Hugl committed
37
38
}

39
DrmPipeline::~DrmPipeline()
40
{
41
42
43
44
    m_output = nullptr;
    if (m_pageflipPending && m_current.crtc) {
        pageFlipped({});
    }
45
46
47
48
}

bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
{
49
    Q_ASSERT(pending.crtc);
50
    Q_ASSERT(buffer);
51
    m_primaryBuffer = buffer;
Xaver Hugl's avatar
Xaver Hugl committed
52
    auto buf = dynamic_cast<DrmGbmBuffer*>(buffer.data());
53
    // with direct scanout disallow modesets, calling presentFailed() and logging warnings
Xaver Hugl's avatar
Xaver Hugl committed
54
    bool directScanout = buf && buf->clientBuffer();
55
56
57
58
59
60
61
    if (gpu()->needsModeset()) {
        if (directScanout) {
            return false;
        }
        m_modesetPresentPending = true;
        return gpu()->maybeModeset();
    }
62
63
    if (gpu()->atomicModeSetting()) {
        if (!commitPipelines({this}, CommitMode::Commit)) {
64
            // update properties and try again
65
66
67
68
69
70
            m_connector->updateProperties();
            if (pending.crtc) {
                pending.crtc->updateProperties();
                if (pending.crtc->primaryPlane()) {
                    pending.crtc->primaryPlane()->updateProperties();
                }
71
72
73
                if (pending.crtc->cursorPlane()) {
                    pending.crtc->cursorPlane()->updateProperties();
                }
74
            }
75
            if (!commitPipelines({this}, CommitMode::Commit)) {
76
77
78
                if (directScanout) {
                    return false;
                }
79
80
                qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
                printDebugInfo();
81
82
83
                if (m_output) {
                    m_output->presentFailed();
                }
84
85
86
87
88
89
                return false;
            }
        }
    } else {
        if (!presentLegacy()) {
            qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno);
90
91
92
            if (m_output) {
                m_output->presentFailed();
            }
93
94
            return false;
        }
95
    }
96
    return true;
97
98
}

99
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
100
{
Xaver Hugl's avatar
Xaver Hugl committed
101
    Q_ASSERT(!pipelines.isEmpty());
102
    if (pipelines[0]->gpu()->atomicModeSetting()) {
103
104
105
106
107
108
109
110
111
112
        return commitPipelinesAtomic(pipelines, mode, unusedObjects);
    } else {
        return commitPipelinesLegacy(pipelines, mode);
    }
}

bool DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline*> &pipelines, CommitMode mode, const QVector<DrmObject*> &unusedObjects)
{
    drmModeAtomicReq *req = drmModeAtomicAlloc();
    if (!req) {
113
        qCCritical(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
114
115
116
117
118
119
        return false;
    }
    uint32_t flags = 0;
    const auto &failed = [pipelines, req, &flags, unusedObjects](){
        drmModeAtomicFree(req);
        printFlags(flags);
Xaver Hugl's avatar
Xaver Hugl committed
120
        for (const auto &pipeline : pipelines) {
121
122
            pipeline->printDebugInfo();
            pipeline->atomicCommitFailed();
123
        }
124
125
126
        for (const auto &obj : unusedObjects) {
            printProps(obj, PrintMode::OnlyChanged);
            obj->rollbackPending();
127
        }
128
129
130
131
132
        return false;
    };
    for (const auto &pipeline : pipelines) {
        if (!pipeline->checkTestBuffer()) {
            qCWarning(KWIN_DRM) << "Checking test buffer failed for" << mode;
133
            return failed();
134
        }
135
136
        if (!pipeline->populateAtomicValues(req, flags)) {
            qCWarning(KWIN_DRM) << "Populating atomic values failed for" << mode;
137
138
            return failed();
        }
139
140
141
142
143
    }
    for (const auto &unused : unusedObjects) {
        unused->disable();
        if (unused->needsModeset()) {
            flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
144
        }
145
146
147
        if (!unused->atomicPopulate(req)) {
            qCWarning(KWIN_DRM) << "Populating atomic values failed for unused resource" << unused;
            return failed();
148
        }
149
150
151
152
153
154
155
156
157
    }
    bool modeset = flags & DRM_MODE_ATOMIC_ALLOW_MODESET;
    Q_ASSERT(!modeset || mode != CommitMode::Commit);
    if (modeset) {
        // The kernel fails commits with DRM_MODE_PAGE_FLIP_EVENT when a crtc is disabled in the commit
        // and already was disabled before, to work around some quirks in old userspace.
        // Instead of using DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_NONBLOCK, do the modeset in a blocking
        // fashion without page flip events and directly call the pageFlipped method afterwards
        flags = flags & (~DRM_MODE_PAGE_FLIP_EVENT);
158
    } else {
159
160
161
        flags |= DRM_MODE_ATOMIC_NONBLOCK;
    }
    if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, nullptr) != 0) {
162
        qCDebug(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
163
164
165
        return failed();
    }
    if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, nullptr) != 0) {
166
        qCCritical(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
167
168
169
        return failed();
    }
    for (const auto &pipeline : pipelines) {
170
        pipeline->atomicCommitSuccessful(mode);
171
172
173
174
175
    }
    for (const auto &obj : unusedObjects) {
        obj->commitPending();
        if (mode != CommitMode::Test) {
            obj->commit();
176
        }
177
    }
178
179
    drmModeAtomicFree(req);
    return true;
180
181
182
183
}

bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
{
184
    if (needsModeset()) {
185
        prepareAtomicModeset();
186
        flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
187
    }
188
    if (activePending()) {
189
        flags |= DRM_MODE_PAGE_FLIP_EVENT;
190
    }
191
    if (pending.crtc) {
192
193
        pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive);
        pending.crtc->setPending(DrmCrtc::PropertyIndex::Gamma_LUT, pending.gamma ? pending.gamma->blobId() : 0);
194
        auto modeSize = m_connector->modes().at(pending.modeIndex)->size();
195
        pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : bufferSize(), QPoint(0, 0), modeSize);
196
        pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr);
197
198
199
200
201
202
203
204
205

        if (pending.crtc->cursorPlane()) {
            pending.crtc->cursorPlane()->set(QPoint(0, 0), gpu()->cursorSize(), pending.cursorPos, gpu()->cursorSize());
            pending.crtc->cursorPlane()->setBuffer(activePending() ? pending.cursorBo.get() : nullptr);
            pending.crtc->cursorPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, (activePending() && pending.cursorBo) ? pending.crtc->id() : 0);
        }
    }
    if (!m_connector->atomicPopulate(req)) {
        return false;
206
    }
207
208
209
210
211
212
213
214
215
216
217
218
    if (pending.crtc) {
        if (!pending.crtc->atomicPopulate(req)) {
            return false;
        }
        if (!pending.crtc->primaryPlane()->atomicPopulate(req)) {
            return false;
        }
        if (pending.crtc->cursorPlane() && !pending.crtc->cursorPlane()->atomicPopulate(req)) {
            return false;
        }
    }
    return true;
219
220
}

221
222
223
224
225
226
void DrmPipeline::prepareAtomicModeset()
{
    if (!pending.crtc) {
        m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, 0);
        return;
    }
227
    auto mode = m_connector->modes().at(pending.modeIndex);
228
229
230
231
232

    m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
    if (const auto &prop = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
        prop->setEnum(pending.rgbRange);
    }
Xaver Hugl's avatar
Xaver Hugl committed
233
234
235
    if (const auto &prop = m_connector->getProp(DrmConnector::PropertyIndex::LinkStatus)) {
        prop->setEnum(DrmConnector::LinkStatus::Good);
    }
Xaver Hugl's avatar
Xaver Hugl committed
236
237
238
239
240
241
242
243
    if (const auto overscan = m_connector->getProp(DrmConnector::PropertyIndex::Overscan)) {
        overscan->setPending(pending.overscan);
    } else if (const auto underscan = m_connector->getProp(DrmConnector::PropertyIndex::Underscan)) {
        const uint32_t hborder = calculateUnderscan();
        underscan->setEnum(pending.overscan != 0 ? DrmConnector::UnderscanOptions::On : DrmConnector::UnderscanOptions::Off);
        m_connector->getProp(DrmConnector::PropertyIndex::Underscan_vborder)->setPending(pending.overscan);
        m_connector->getProp(DrmConnector::PropertyIndex::Underscan_hborder)->setPending(hborder);
    }
244
    if (const auto bpc = m_connector->getProp(DrmConnector::PropertyIndex::MaxBpc)) {
245
246
247
248
249
        uint64_t preferred = 8;
        if (gpu()->eglBackend() && gpu()->eglBackend()->prefer10bpc()) {
            preferred = 10;
        }
        bpc->setPending(std::min(bpc->maxValue(), preferred));
250
    }
251
252
253
254
255

    pending.crtc->setPending(DrmCrtc::PropertyIndex::Active, activePending());
    pending.crtc->setPending(DrmCrtc::PropertyIndex::ModeId, activePending() ? mode->blobId() : 0);

    pending.crtc->primaryPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
256
    pending.crtc->primaryPlane()->setTransformation(pending.bufferTransformation);
257
258
259
260
261
    if (pending.crtc->cursorPlane()) {
        pending.crtc->cursorPlane()->setTransformation(DrmPlane::Transformation::Rotate0);
    }
}

Xaver Hugl's avatar
Xaver Hugl committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
uint32_t DrmPipeline::calculateUnderscan()
{
    const auto modes = m_connector->modes();
    const auto size = modes[pending.modeIndex]->size();
    const float aspectRatio = size.width() / static_cast<float>(size.height());
    uint32_t hborder = pending.overscan * aspectRatio;
    if (hborder > 128) {
        // overscan only goes from 0-100 so we cut off the 101-128 value range of underscan_vborder
        hborder = 128;
        pending.overscan = 128 / aspectRatio;
    }
    return hborder;
}

276
void DrmPipeline::atomicCommitFailed()
277
{
278
279
280
    if (m_oldTestBuffer) {
        m_primaryBuffer = m_oldTestBuffer;
        m_oldTestBuffer = nullptr;
281
    }
282
283
284
285
286
287
288
289
290
291
    m_connector->rollbackPending();
    if (pending.crtc) {
        pending.crtc->rollbackPending();
        pending.crtc->primaryPlane()->rollbackPending();
        if (pending.crtc->cursorPlane()) {
            pending.crtc->cursorPlane()->rollbackPending();
        }
    }
}

292
void DrmPipeline::atomicCommitSuccessful(CommitMode mode)
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
{
    m_oldTestBuffer = nullptr;
    m_connector->commitPending();
    if (pending.crtc) {
        pending.crtc->commitPending();
        pending.crtc->primaryPlane()->commitPending();
        if (pending.crtc->cursorPlane()) {
            pending.crtc->cursorPlane()->commitPending();
        }
    }
    if (mode != CommitMode::Test) {
        if (activePending()) {
            m_pageflipPending = true;
        }
        m_connector->commit();
        if (pending.crtc) {
            pending.crtc->commit();
            pending.crtc->primaryPlane()->setNext(m_primaryBuffer);
            pending.crtc->primaryPlane()->commit();
            if (pending.crtc->cursorPlane()) {
                pending.crtc->cursorPlane()->setNext(pending.cursorBo);
                pending.crtc->cursorPlane()->commit();
            }
        }
        m_current = pending;
        if (mode == CommitMode::CommitModeset && activePending()) {
            pageFlipped(std::chrono::steady_clock::now().time_since_epoch());
        }
321
322
323
324
325
    }
}

bool DrmPipeline::checkTestBuffer()
{
326
    if (!pending.crtc || (m_primaryBuffer && m_primaryBuffer->size() == bufferSize())) {
327
328
        return true;
    }
329
    auto backend = gpu()->eglBackend();
330
    QSharedPointer<DrmBuffer> buffer;
331
332
    // try to re-use buffers if possible.
    const auto &checkBuffer = [this, backend, &buffer](const QSharedPointer<DrmBuffer> &buf){
333
        const auto &mods = supportedModifiers(buf->format());
Xaver Hugl's avatar
Xaver Hugl committed
334
        if (backend && buf->format() == backend->drmFormat(m_output)
335
            && (mods.isEmpty() || mods.contains(buf->modifier()))
336
            && buf->size() == bufferSize()) {
337
338
339
            buffer = buf;
        }
    };
340
341
342
343
344
345
346
347
    if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->next()) {
        checkBuffer(pending.crtc->primaryPlane()->next());
    } else if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->current()) {
        checkBuffer(pending.crtc->primaryPlane()->current());
    } else if (pending.crtc->next()) {
        checkBuffer(pending.crtc->next());
    } else if (pending.crtc->current()) {
        checkBuffer(pending.crtc->current());
348
349
    }
    // if we don't have a fitting buffer already, get or create one
Xaver Hugl's avatar
Xaver Hugl committed
350
351
352
353
    if (!buffer) {
        if (backend && m_output) {
            buffer = backend->renderTestFrame(m_output);
        } else if (backend && gpu()->gbmDevice()) {
354
            gbm_bo *bo = gbm_bo_create(gpu()->gbmDevice(), bufferSize().width(), bufferSize().height(), DRM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
Xaver Hugl's avatar
Xaver Hugl committed
355
356
357
358
359
            if (!bo) {
                return false;
            }
            buffer = QSharedPointer<DrmGbmBuffer>::create(gpu(), bo, nullptr);
        } else {
360
            buffer = QSharedPointer<DrmDumbBuffer>::create(gpu(), bufferSize(), DRM_FORMAT_XRGB8888);
361
362
363
364
365
366
367
368
369
370
        }
    }
    if (buffer && buffer->bufferId()) {
        m_oldTestBuffer = m_primaryBuffer;
        m_primaryBuffer = buffer;
        return true;
    }
    return false;
}

371
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot)
372
{
373
374
375
    if (pending.cursorBo == buffer && pending.cursorHotspot == hotspot) {
        return true;
    }
376
377
    bool result;
    const bool visibleBefore = isCursorVisible();
378
379
    pending.cursorBo = buffer;
    pending.cursorHotspot = hotspot;
380
381
382
383
    // explicitly check for the cursor plane and not for AMS, as we might not always have one
    if (pending.crtc->cursorPlane()) {
        result = commitPipelines({this}, CommitMode::Test);
    } else {
384
        result = setCursorLegacy();
385
    }
386
387
388
389
390
391
392
    if (result) {
        m_next = pending;
        if (m_output && (visibleBefore || isCursorVisible())) {
            m_output->renderLoop()->scheduleRepaint();
        }
    } else {
        pending = m_next;
393
394
    }
    return result;
395
396
397
398
}

bool DrmPipeline::moveCursor(QPoint pos)
{
399
400
401
    if (pending.cursorPos == pos) {
        return true;
    }
402
    const bool visibleBefore = isCursorVisible();
403
404
    bool result;
    pending.cursorPos = pos;
405
406
407
408
    // explicitly check for the cursor plane and not for AMS, as we might not always have one
    if (pending.crtc->cursorPlane()) {
        result = commitPipelines({this}, CommitMode::Test);
    } else {
409
        result = moveCursorLegacy();
410
    }
411
412
413
414
415
416
417
    if (result) {
        m_next = pending;
        if (m_output && (visibleBefore || isCursorVisible())) {
            m_output->renderLoop()->scheduleRepaint();
        }
    } else {
        pending = m_next;
418
419
    }
    return result;
420
421
}

422
423
424
425
void DrmPipeline::applyPendingChanges()
{
    if (!pending.crtc) {
        pending.active = false;
426
    }
427
    m_next = pending;
428
429
}

430
431
QSize DrmPipeline::bufferSize() const
{
432
    const auto modeSize = m_connector->modes().at(pending.modeIndex)->size();
433
434
435
436
437
438
    if (pending.bufferTransformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) {
        return modeSize.transposed();
    }
    return modeSize;
}

439
440
QSize DrmPipeline::sourceSize() const
{
441
    const auto modeSize = m_connector->modes().at(pending.modeIndex)->size();
442
443
    if (pending.sourceTransformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) {
        return modeSize.transposed();
444
    }
445
    return modeSize;
446
447
448
449
}

bool DrmPipeline::isCursorVisible() const
{
450
    const QRect mode = QRect(QPoint(), m_connector->modes().at(pending.modeIndex)->size());
451
    return pending.cursorBo && QRect(pending.cursorPos, pending.cursorBo->size()).intersects(mode);
452
453
454
455
456
457
458
}

DrmConnector *DrmPipeline::connector() const
{
    return m_connector;
}

459
DrmGpu *DrmPipeline::gpu() const
460
{
461
    return m_connector->gpu();
462
463
}

464
void DrmPipeline::pageFlipped(std::chrono::nanoseconds timestamp)
465
{
466
467
468
    m_current.crtc->flipBuffer();
    if (m_current.crtc->primaryPlane()) {
        m_current.crtc->primaryPlane()->flipBuffer();
469
    }
470
471
472
    if (m_current.crtc->cursorPlane()) {
        m_current.crtc->cursorPlane()->flipBuffer();
    }
473
474
475
476
    m_pageflipPending = false;
    if (m_output) {
        m_output->pageFlipped(timestamp);
    }
477
478
}

479
void DrmPipeline::setOutput(DrmOutput *output)
480
{
481
482
483
484
485
486
    m_output = output;
}

DrmOutput *DrmPipeline::output() const
{
    return m_output;
487
488
}

489
static const QMap<uint32_t, QVector<uint64_t>> legacyFormats = {
490
    {DRM_FORMAT_XRGB8888, {}}
491
492
};

Xaver Hugl's avatar
Xaver Hugl committed
493
494
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
{
Xaver Hugl's avatar
Xaver Hugl committed
495
496
497
498
    if (pending.crtc) {
        if (pending.crtc->primaryPlane()) {
            return pending.crtc->primaryPlane()->formats().contains(drmFormat);
        } else {
499
            return legacyFormats.contains(drmFormat);
Xaver Hugl's avatar
Xaver Hugl committed
500
501
502
503
        }
    } else {
        return false;
    }
Xaver Hugl's avatar
Xaver Hugl committed
504
505
506
507
}

QVector<uint64_t> DrmPipeline::supportedModifiers(uint32_t drmFormat) const
{
Xaver Hugl's avatar
Xaver Hugl committed
508
    if (pending.crtc && pending.crtc->primaryPlane()) {
509
        return pending.crtc->primaryPlane()->formats().value(drmFormat);
Xaver Hugl's avatar
Xaver Hugl committed
510
511
512
    } else {
        return {};
    }
Xaver Hugl's avatar
Xaver Hugl committed
513
514
}

515
516
517
518
519
520
521
522
523
524
525
526
527
QMap<uint32_t, QVector<uint64_t>> DrmPipeline::supportedFormats() const
{
    if (pending.crtc) {
        if (pending.crtc->primaryPlane()) {
            return pending.crtc->primaryPlane()->formats();
        } else {
            return legacyFormats;
        }
    } else {
        return {};
    }
}

528
529
530
531
532
533
bool DrmPipeline::needsModeset() const
{
    return pending.crtc != m_current.crtc
        || pending.active != m_current.active
        || pending.modeIndex != m_current.modeIndex
        || pending.rgbRange != m_current.rgbRange
534
        || pending.bufferTransformation != m_current.bufferTransformation
Xaver Hugl's avatar
Xaver Hugl committed
535
        || m_connector->linkStatus() == DrmConnector::LinkStatus::Bad
536
        || m_modesetPresentPending;
537
538
539
540
541
542
543
544
545
546
547
548
}

bool DrmPipeline::activePending() const
{
    return pending.crtc && pending.active;
}

void DrmPipeline::revertPendingChanges()
{
    pending = m_next;
}

549
550
551
552
553
554
555
556
557
558
bool DrmPipeline::pageflipPending() const
{
    return m_pageflipPending;
}

bool DrmPipeline::modesetPresentPending() const
{
    return m_modesetPresentPending;
}

559
560
561
562
563
void DrmPipeline::resetModesetPresentPending()
{
    m_modesetPresentPending = false;
}

564
565
566
567
568
DrmCrtc *DrmPipeline::currentCrtc() const
{
    return m_current.crtc;
}

569
DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut)
570
571
    : m_gpu(gpu)
    , m_lut(lut)
572
573
{
    if (gpu->atomicModeSetting()) {
574
575
        QVector<drm_color_lut> atomicLut(lut.size());
        for (uint32_t i = 0; i < lut.size(); i++) {
576
577
578
579
            atomicLut[i].red = lut.red()[i];
            atomicLut[i].green = lut.green()[i];
            atomicLut[i].blue = lut.blue()[i];
        }
580
581
582
        if (drmModeCreatePropertyBlob(gpu->fd(), atomicLut.data(), sizeof(drm_color_lut) * lut.size(), &m_blobId) != 0) {
            qCWarning(KWIN_DRM) << "Failed to create gamma blob!" << strerror(errno);
        }
583
584
585
586
587
    }
}

DrmGammaRamp::~DrmGammaRamp()
{
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    if (m_blobId != 0) {
        drmModeDestroyPropertyBlob(m_gpu->fd(), m_blobId);
    }
}

uint32_t DrmGammaRamp::blobId() const
{
    return m_blobId;
}

uint32_t DrmGammaRamp::size() const
{
    return m_lut.size();
}

uint16_t *DrmGammaRamp::red() const
{
    return const_cast<uint16_t*>(m_lut.red());
}

uint16_t *DrmGammaRamp::green() const
{
    return const_cast<uint16_t*>(m_lut.green());
}

uint16_t *DrmGammaRamp::blue() const
{
    return const_cast<uint16_t*>(m_lut.blue());
616
617
}

618
619
620
void DrmPipeline::printFlags(uint32_t flags)
{
    if (flags == 0) {
621
        qCDebug(KWIN_DRM) << "Flags: none";
622
    } else {
623
        qCDebug(KWIN_DRM) << "Flags:";
624
        if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
625
            qCDebug(KWIN_DRM) << "\t DRM_MODE_PAGE_FLIP_EVENT";
626
627
        }
        if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) {
628
            qCDebug(KWIN_DRM) << "\t DRM_MODE_ATOMIC_ALLOW_MODESET";
629
630
        }
        if (flags & DRM_MODE_PAGE_FLIP_ASYNC) {
631
            qCDebug(KWIN_DRM) << "\t DRM_MODE_PAGE_FLIP_ASYNC";
632
633
634
635
636
        }
    }
}

void DrmPipeline::printProps(DrmObject *object, PrintMode mode)
637
638
{
    auto list = object->properties();
639
640
641
642
643
644
    bool any = mode == PrintMode::All || std::any_of(list.constBegin(), list.constEnd(), [](const auto &prop){
        return prop && !prop->isImmutable() && prop->needsCommit();
    });
    if (!any) {
        return;
    }
645
    qCDebug(KWIN_DRM) << object->typeName() << object->id();
646
647
    for (const auto &prop : list) {
        if (prop) {
648
            uint64_t current = prop->name().startsWith("SRC_") ? prop->current() >> 16 : prop->current();
649
            if (prop->isImmutable() || !prop->needsCommit()) {
650
                if (mode == PrintMode::All) {
651
                    qCDebug(KWIN_DRM).nospace() << "\t" << prop->name() << ": " << current;
652
                }
653
            } else {
654
                uint64_t pending = prop->name().startsWith("SRC_") ? prop->pending() >> 16 : prop->pending();
655
                qCDebug(KWIN_DRM).nospace() << "\t" << prop->name() << ": " << current << "->" << pending;
656
657
658
659
660
661
662
            }
        }
    }
}

void DrmPipeline::printDebugInfo() const
{
663
    qCDebug(KWIN_DRM) << "Drm objects:";
664
    printProps(m_connector, PrintMode::All);
665
    if (pending.crtc) {
666
        printProps(pending.crtc, PrintMode::All);
667
        if (pending.crtc->primaryPlane()) {
668
            printProps(pending.crtc->primaryPlane(), PrintMode::All);
669
        }
670
671
672
        if (pending.crtc->cursorPlane()) {
            printProps(pending.crtc->cursorPlane(), PrintMode::All);
        }
673
674
675
676
    }
}

}