drm_gpu.cpp 18.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/*
    KWin - the KDE window manager
    This file is part of the KDE project.

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

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

#include "drm_gpu.h"
11
#include <config-kwin.h>
12
13
14
15
16
17
#include "drm_backend.h"
#include "drm_output.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "abstract_egl_backend.h"
#include "logging.h"
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
18
#include "session.h"
Xaver Hugl's avatar
Xaver Hugl committed
19
#include "renderloop_p.h"
20
#include "main.h"
21
#include "drm_pipeline.h"
22
#include "drm_virtual_output.h"
23
24
25
26
27
28
29
30

#if HAVE_GBM
#include "egl_gbm_backend.h"
#include <gbm.h>
#include "gbm_dmabuf.h"
#endif
// system
#include <algorithm>
31
#include <errno.h>
32
#include <poll.h>
33
34
35
36
37
#include <unistd.h>
// drm
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <libdrm/drm_mode.h>
38
#include <drm_fourcc.h>
39
40
41
42

namespace KWin
{

43
DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t deviceId)
44
45
46
    : m_backend(backend)
    , m_devNode(devNode)
    , m_fd(fd)
47
    , m_deviceId(deviceId)
48
49
    , m_atomicModeSetting(false)
    , m_gbmDevice(nullptr)
50
{
51
52
53
54
55
56
57
58
59
60
61
62
63
    uint64_t capability = 0;

    if (drmGetCap(fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) {
        m_cursorSize.setWidth(capability);
    } else {
        m_cursorSize.setWidth(64);
    }

    if (drmGetCap(fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) {
        m_cursorSize.setHeight(capability);
    } else {
        m_cursorSize.setHeight(64);
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
64

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
65
66
67
68
69
70
71
    int ret = drmGetCap(fd, DRM_CAP_TIMESTAMP_MONOTONIC, &capability);
    if (ret == 0 && capability == 1) {
        m_presentationClock = CLOCK_MONOTONIC;
    } else {
        m_presentationClock = CLOCK_REALTIME;
    }

Xaver Hugl's avatar
Xaver Hugl committed
72
73
    m_addFB2ModifiersSupported = drmGetCap(fd, DRM_CAP_ADDFB2_MODIFIERS, &capability) == 0 && capability == 1;
    qCDebug(KWIN_DRM) << "drmModeAddFB2WithModifiers is" << (m_addFB2ModifiersSupported ? "supported" : "not supported") << "on GPU" << m_devNode;
74

75
76
    // find out if this GPU is using the NVidia proprietary driver
    DrmScopedPointer<drmVersion> version(drmGetVersion(fd));
77
78
79
80
    m_isNVidia = strstr(version->name, "nvidia-drm");
    m_useEglStreams = m_isNVidia;
#if HAVE_GBM
    m_gbmDevice = gbm_create_device(m_fd);
81
82
83
84
85
    bool envVarIsSet = false;
    bool value = qEnvironmentVariableIntValue("KWIN_DRM_FORCE_EGL_STREAMS", &envVarIsSet) != 0;
    if (envVarIsSet) {
        m_useEglStreams = m_isNVidia && value;
    } else if (m_gbmDevice) {
86
87
88
        m_useEglStreams = m_isNVidia && strcmp(gbm_device_get_backend_name(m_gbmDevice), "nvidia") != 0;
    }
#endif
89

Xaver Hugl's avatar
Xaver Hugl committed
90
    m_socketNotifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
91
    connect(m_socketNotifier, &QSocketNotifier::activated, this, &DrmGpu::dispatchEvents);
92
93
94
95

    // trying to activate Atomic Mode Setting (this means also Universal Planes)
    static const bool atomicModesetting = !qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS");
    if (atomicModesetting) {
Xaver Hugl's avatar
Xaver Hugl committed
96
        initDrmResources();
97
    }
98
99
100
101
}

DrmGpu::~DrmGpu()
{
Xaver Hugl's avatar
Xaver Hugl committed
102
    waitIdle();
103
104
    const auto outputs = m_outputs;
    for (const auto &output : outputs) {
105
106
107
108
109
        if (auto drmOutput = qobject_cast<DrmOutput *>(output)) {
            removeOutput(drmOutput);
        } else {
            removeVirtualOutput(dynamic_cast<DrmVirtualOutput*>(output));
        }
110
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
111
112
113
    if (m_eglDisplay != EGL_NO_DISPLAY) {
        eglTerminate(m_eglDisplay);
    }
114
115
116
    qDeleteAll(m_crtcs);
    qDeleteAll(m_connectors);
    qDeleteAll(m_planes);
Xaver Hugl's avatar
Xaver Hugl committed
117
    delete m_socketNotifier;
118
119
120
121
122
#if HAVE_GBM
    if (m_gbmDevice) {
        gbm_device_destroy(m_gbmDevice);
    }
#endif
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
123
    m_backend->session()->closeRestricted(m_fd);
124
125
}

Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
126
127
128
129
130
clockid_t DrmGpu::presentationClock() const
{
    return m_presentationClock;
}

Xaver Hugl's avatar
Xaver Hugl committed
131
void DrmGpu::initDrmResources()
132
{
Xaver Hugl's avatar
Xaver Hugl committed
133
    // try atomic mode setting
134
135
    if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) {
        DrmScopedPointer<drmModePlaneRes> planeResources(drmModeGetPlaneResources(m_fd));
Xaver Hugl's avatar
Xaver Hugl committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
        if (planeResources) {
            qCDebug(KWIN_DRM) << "Using Atomic Mode Setting on gpu" << m_devNode;
            qCDebug(KWIN_DRM) << "Number of planes on GPU" << m_devNode << ":" << planeResources->count_planes;
            // create the plane objects
            for (unsigned int i = 0; i < planeResources->count_planes; ++i) {
                DrmScopedPointer<drmModePlane> kplane(drmModeGetPlane(m_fd, planeResources->planes[i]));
                DrmPlane *p = new DrmPlane(this, kplane->plane_id);
                if (p->init()) {
                    m_planes << p;
                } else {
                    delete p;
                }
            }
            if (m_planes.isEmpty()) {
                qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode on GPU " << m_devNode;
                m_atomicModeSetting = false;
152
            } else {
Xaver Hugl's avatar
Xaver Hugl committed
153
                m_atomicModeSetting = true;
154
            }
Xaver Hugl's avatar
Xaver Hugl committed
155
156
        } else {
            qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode on GPU " << m_devNode;
157
158
            m_atomicModeSetting = false;
        }
159
160
    } else {
        qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode on GPU" << m_devNode;
Xaver Hugl's avatar
Xaver Hugl committed
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
        m_atomicModeSetting = false;
    }

    DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
    if (!resources) {
        qCCritical(KWIN_DRM) << "drmModeGetResources for getting CRTCs failed on GPU" << m_devNode;
        return;
    }
    for (int i = 0; i < resources->count_crtcs; ++i) {
        auto c = new DrmCrtc(this, resources->crtcs[i], i);
        if (!c->init()) {
            delete c;
            continue;
        }
        m_crtcs << c;
176
177
178
179
180
    }
}

bool DrmGpu::updateOutputs()
{
181
    waitIdle();
182
183
184
185
186
187
    DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
    if (!resources) {
        qCWarning(KWIN_DRM) << "drmModeGetResources failed";
        return false;
    }

Xaver Hugl's avatar
Xaver Hugl committed
188
189
    // check for added and removed connectors
    QVector<DrmConnector *> removedConnectors = m_connectors;
190
191
192
193
    for (int i = 0; i < resources->count_connectors; ++i) {
        const uint32_t currentConnector = resources->connectors[i];
        auto it = std::find_if(m_connectors.constBegin(), m_connectors.constEnd(), [currentConnector] (DrmConnector *c) { return c->id() == currentConnector; });
        if (it == m_connectors.constEnd()) {
194
            auto c = new DrmConnector(this, currentConnector);
Xaver Hugl's avatar
Xaver Hugl committed
195
            if (!c->init() || !c->isConnected() || c->isNonDesktop()) {
Xaver Hugl's avatar
Xaver Hugl committed
196
197
198
                delete c;
                continue;
            }
199
200
            m_connectors << c;
        } else {
201
            (*it)->updateProperties();
Xaver Hugl's avatar
Xaver Hugl committed
202
203
204
            if ((*it)->isConnected()) {
                removedConnectors.removeOne(*it);
            }
205
206
        }
    }
Xaver Hugl's avatar
Xaver Hugl committed
207
208
209
210
211
    for (const auto &connector : qAsConst(removedConnectors)) {
        if (auto output = findOutput(connector->id())) {
            removeOutput(output);
        }
        m_connectors.removeOne(connector);
212
        delete connector;
Xaver Hugl's avatar
Xaver Hugl committed
213
    }
214

Xaver Hugl's avatar
Xaver Hugl committed
215
216
217
218
219
220
221
222
    // find unused and connected connectors
    QVector<DrmConnector *> connectedConnectors;
    for (const auto &conn : qAsConst(m_connectors)) {
        auto output = findOutput(conn->id());
        if (conn->isConnected() && !conn->isNonDesktop()) {
            connectedConnectors << conn;
            if (output) {
                output->updateModes();
223
            }
Xaver Hugl's avatar
Xaver Hugl committed
224
225
        } else if (output) {
            removeOutput(output);
226
227
228
        }
    }

Xaver Hugl's avatar
Xaver Hugl committed
229
230
231
    // update crtc properties
    for (const auto &crtc : qAsConst(m_crtcs)) {
        crtc->updateProperties();
232
    }
Xaver Hugl's avatar
Xaver Hugl committed
233
234
235
    // update plane properties
    for (const auto &plane : qAsConst(m_planes)) {
        plane->updateProperties();
236
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
237

238
239
    // stash away current pipelines of active outputs
    QMap<DrmOutput*, DrmPipeline*> oldPipelines;
240
    for (const auto &output : qAsConst(m_drmOutputs)) {
241
242
243
244
        if (!output->isEnabled()) {
            // create render resources for findWorkingCombination
            Q_EMIT outputEnabled(output);
        }
245
        m_pipelines.removeOne(output->pipeline());
246
        oldPipelines.insert(output, output->pipeline());
247
248
        output->setPipeline(nullptr);
    }
249

250
251
252
253
254
255
    if (m_atomicModeSetting) {
        // sort outputs by being already connected (to any CRTC) so that already working outputs get preferred
        std::sort(connectedConnectors.begin(), connectedConnectors.end(), [](auto c1, auto c2){
            return c1->getProp(DrmConnector::PropertyIndex::CrtcId)->current() > c2->getProp(DrmConnector::PropertyIndex::CrtcId)->current();
        });
    }
256
257
258
259
260
261
262
263
264
265
266
267
    auto config = findWorkingCombination({}, connectedConnectors, m_crtcs, m_planes);
    if (config.isEmpty() && !connectedConnectors.isEmpty()) {
        qCCritical(KWIN_DRM) << "DrmGpu::findWorkingCombination failed to find any functional combinations! Reverting to the old configuration!";
        for (auto it = oldPipelines.begin(); it != oldPipelines.end(); it++) {
            it.value()->setOutput(it.key());
            config << it.value();
        }
    } else {
        for (const auto &pipeline : qAsConst(oldPipelines)) {
            delete pipeline;
        }
    }
268
    m_pipelines << config;
269

270
271
    for (auto it = config.crbegin(); it != config.crend(); it++) {
        const auto &pipeline = *it;
272
273
        auto output = pipeline->output();
        if (m_outputs.contains(output)) {
274
275
276
277
278
279
280
            // restore output properties
            if (output->isEnabled()) {
                output->updateTransform(output->transform());
                if (output->dpmsMode() != AbstractWaylandOutput::DpmsMode::On) {
                    pipeline->setActive(false);
                }
            } else {
Xaver Hugl's avatar
Xaver Hugl committed
281
                pipeline->setActive(false);
282
                Q_EMIT outputDisabled(output);
Xaver Hugl's avatar
Xaver Hugl committed
283
            }
284
285
286
287
        } else {
            qCDebug(KWIN_DRM).nospace() << "New output on GPU " << m_devNode << ": " << pipeline->connector()->modelName();
            if (!output->initCursor(m_cursorSize)) {
                m_backend->setSoftwareCursorForced(true);
Xaver Hugl's avatar
Xaver Hugl committed
288
            }
289
290
291
            m_outputs << output;
            m_drmOutputs << output;
            Q_EMIT outputAdded(output);
292
293
294
        }
    }

Xaver Hugl's avatar
Xaver Hugl committed
295
296
297
298
299
300
301
    return true;
}

QVector<DrmPipeline *> DrmGpu::findWorkingCombination(const QVector<DrmPipeline *> &pipelines, QVector<DrmConnector *> connectors, QVector<DrmCrtc *> crtcs, const QVector<DrmPlane *> &planes)
{
    if (connectors.isEmpty() || crtcs.isEmpty()) {
        // no further pipelines can be added -> test configuration
302
        if (pipelines.isEmpty() || commitCombination(pipelines)) {
Xaver Hugl's avatar
Xaver Hugl committed
303
304
305
            return pipelines;
        } else {
            return {};
306
307
        }
    }
Xaver Hugl's avatar
Xaver Hugl committed
308
309
310
311
312
313
314
315
316
317
318
319
320
    auto connector = connectors.takeFirst();
    const auto encoders = connector->encoders();

    if (m_atomicModeSetting) {
        // try the crtc that this connector is already connected to first
        std::sort(crtcs.begin(), crtcs.end(), [connector](auto c1, auto c2){
            Q_UNUSED(c2)
            if (connector->getProp(DrmConnector::PropertyIndex::CrtcId)->current() == c1->id()) {
                return true;
            } else {
                return false;
            }
        });
321
322
    }

Xaver Hugl's avatar
Xaver Hugl committed
323
324
325
326
327
328
329
330
331
332
333
    auto recurse = [this, connector, connectors, crtcs, planes, pipelines] (DrmCrtc *crtc, DrmPlane *primaryPlane) {
        auto pipeline = new DrmPipeline(this, connector, crtc, primaryPlane);
        auto crtcsLeft = crtcs;
        crtcsLeft.removeOne(crtc);
        auto planesLeft = planes;
        planesLeft.removeOne(primaryPlane);
        auto allPipelines = pipelines;
        allPipelines << pipeline;
        auto ret = findWorkingCombination(allPipelines, connectors, crtcsLeft, planesLeft);
        if (ret.isEmpty()) {
            delete pipeline;
334
        }
Xaver Hugl's avatar
Xaver Hugl committed
335
336
337
338
339
340
341
342
343
344
345
346
        return ret;
    };
    for (const auto &encoderId : encoders) {
        DrmScopedPointer<drmModeEncoder> encoder(drmModeGetEncoder(m_fd, encoderId));
        for (const auto &crtc : qAsConst(crtcs)) {
            if (m_atomicModeSetting) {
                for (const auto &plane : qAsConst(planes)) {
                    if (plane->type() == DrmPlane::TypeIndex::Primary
                        && plane->isCrtcSupported(crtc->pipeIndex())) {
                        if (auto workingPipelines = recurse(crtc, plane); !workingPipelines.isEmpty()) {
                            return workingPipelines;
                        }
347
348
                    }
                }
Xaver Hugl's avatar
Xaver Hugl committed
349
350
351
            } else {
                if (auto workingPipelines = recurse(crtc, nullptr); !workingPipelines.isEmpty()) {
                    return workingPipelines;
352
353
354
355
                }
            }
        }
    }
Xaver Hugl's avatar
Xaver Hugl committed
356
357
    return {};
}
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
358

359
bool DrmGpu::commitCombination(const QVector<DrmPipeline *> &pipelines)
Xaver Hugl's avatar
Xaver Hugl committed
360
361
362
363
364
365
366
367
368
369
370
{
    for (const auto &pipeline : pipelines) {
        auto output = findOutput(pipeline->connector()->id());
        if (output) {
            output->setPipeline(pipeline);
            pipeline->setOutput(output);
        } else {
            output = new DrmOutput(this, pipeline);
            Q_EMIT outputEnabled(output);// create render resources for the test
        }
        pipeline->setup();
371
    }
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
372

373
    if (DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::Test)) {
Xaver Hugl's avatar
Xaver Hugl committed
374
375
376
        return true;
    } else {
        for (const auto &pipeline : qAsConst(pipelines)) {
377
378
379
380
            if (!m_outputs.contains(pipeline->output())) {
                Q_EMIT outputDisabled(pipeline->output());
                delete pipeline->output();
            }
Xaver Hugl's avatar
Xaver Hugl committed
381
382
383
        }
        return false;
    }
384
385
386
387
}

DrmOutput *DrmGpu::findOutput(quint32 connector)
{
388
    auto it = std::find_if(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [connector] (DrmOutput *o) {
Xaver Hugl's avatar
Xaver Hugl committed
389
        return o->connector()->id() == connector;
390
    });
391
    if (it != m_drmOutputs.constEnd()) {
392
393
394
395
396
        return *it;
    }
    return nullptr;
}

Xaver Hugl's avatar
Xaver Hugl committed
397
398
399
400
void DrmGpu::waitIdle()
{
    m_socketNotifier->setEnabled(false);
    while (true) {
401
        const bool idle = std::all_of(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [](DrmOutput *output){
Xaver Hugl's avatar
Xaver Hugl committed
402
403
404
405
406
            return !output->m_pageFlipPending;
        });
        if (idle) {
            break;
        }
407
408
409
410
411
412
413
414
415
416
417
        pollfd pfds[1];
        pfds[0].fd = m_fd;
        pfds[0].events = POLLIN;

        const int ready = poll(pfds, 1, 30000);
        if (ready < 0) {
            if (errno != EINTR) {
                qCWarning(KWIN_DRM) << Q_FUNC_INFO << "poll() failed:" << strerror(errno);
                break;
            }
        } else if (ready == 0) {
Xaver Hugl's avatar
Xaver Hugl committed
418
419
            qCWarning(KWIN_DRM) << "No drm events for gpu" << m_devNode << "within last 30 seconds";
            break;
420
421
        } else {
            dispatchEvents();
Xaver Hugl's avatar
Xaver Hugl committed
422
423
424
425
426
        }
    };
    m_socketNotifier->setEnabled(true);
}

Xaver Hugl's avatar
Xaver Hugl committed
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
static std::chrono::nanoseconds convertTimestamp(const timespec &timestamp)
{
    return std::chrono::seconds(timestamp.tv_sec) + std::chrono::nanoseconds(timestamp.tv_nsec);
}

static std::chrono::nanoseconds convertTimestamp(clockid_t sourceClock, clockid_t targetClock,
                                                 const timespec &timestamp)
{
    if (sourceClock == targetClock) {
        return convertTimestamp(timestamp);
    }

    timespec sourceCurrentTime = {};
    timespec targetCurrentTime = {};

    clock_gettime(sourceClock, &sourceCurrentTime);
    clock_gettime(targetClock, &targetCurrentTime);

    const auto delta = convertTimestamp(sourceCurrentTime) - convertTimestamp(timestamp);
    return convertTimestamp(targetCurrentTime) - delta;
}

static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data)
{
    Q_UNUSED(fd)
    Q_UNUSED(frame)
453
454
455
456
457
458
459
460
    auto backend = dynamic_cast<DrmBackend*>(kwinApp()->platform());
    if (!backend) {
        return;
    }
    auto gpu = backend->findGpuByFd(fd);
    if (!gpu) {
        return;
    }
Xaver Hugl's avatar
Xaver Hugl committed
461
    auto output = static_cast<DrmOutput *>(data);
462
463
464
465
    if (!gpu->outputs().contains(output)) {
        // output already got deleted
        return;
    }
Xaver Hugl's avatar
Xaver Hugl committed
466

467
    // The static_cast<> here are for a 32-bit environment where
Adriaan de Groot's avatar
Adriaan de Groot committed
468
469
470
471
    // sizeof(time_t) == sizeof(unsigned int) == 4 . Putting @p sec
    // into a time_t cuts off the most-significant bit (after the
    // year 2038), similarly long can't hold all the bits of an
    // unsigned multiplication.
Xaver Hugl's avatar
Xaver Hugl committed
472
473
    std::chrono::nanoseconds timestamp = convertTimestamp(output->gpu()->presentationClock(),
                                                          CLOCK_MONOTONIC,
Adriaan de Groot's avatar
Adriaan de Groot committed
474
                                                          { static_cast<time_t>(sec), static_cast<long>(usec * 1000) });
Xaver Hugl's avatar
Xaver Hugl committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
    if (timestamp == std::chrono::nanoseconds::zero()) {
        qCDebug(KWIN_DRM, "Got invalid timestamp (sec: %u, usec: %u) on output %s",
                sec, usec, qPrintable(output->name()));
        timestamp = std::chrono::steady_clock::now().time_since_epoch();
    }

    output->pageFlipped();
    RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output->renderLoop());
    renderLoopPrivate->notifyFrameCompleted(timestamp);
}

void DrmGpu::dispatchEvents()
{
    if (!m_backend->session()->isActive()) {
        return;
    }
    drmEventContext context = {};
    context.version = 2;
    context.page_flip_handler = pageFlipHandler;
    drmHandleEvent(m_fd, &context);
}

497
498
void DrmGpu::removeOutput(DrmOutput *output)
{
499
    m_drmOutputs.removeOne(output);
500
    m_outputs.removeOne(output);
501
502
503
    if (output->isEnabled()) {
        Q_EMIT outputDisabled(output);
    }
504
    Q_EMIT outputRemoved(output);
505
    auto pipeline = output->m_pipeline;
506
    delete output;
507
508
    m_pipelines.removeOne(pipeline);
    delete pipeline;
509
510
}

511
AbstractEglDrmBackend *DrmGpu::eglBackend() const
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
512
513
514
515
{
    return m_eglBackend;
}

516
void DrmGpu::setEglBackend(AbstractEglDrmBackend *eglBackend)
Vlad Zahorodnii's avatar
Vlad Zahorodnii committed
517
518
519
520
{
    m_eglBackend = eglBackend;
}

521
522
523
524
525
526
527
528
529
DrmBackend *DrmGpu::platform() const {
    return m_backend;
}

const QVector<DrmPipeline*> DrmGpu::pipelines() const
{
    return m_pipelines;
}

530
531
532
533
534
535
536
537
538
539
540
541
542
DrmVirtualOutput *DrmGpu::createVirtualOutput()
{
    auto output = new DrmVirtualOutput(this);
    output->setPlaceholder(true);
    m_outputs << output;
    Q_EMIT outputEnabled(output);
    Q_EMIT outputAdded(output);
    return output;
}

void DrmGpu::removeVirtualOutput(DrmVirtualOutput *output)
{
    if (m_outputs.removeOne(output)) {
543
544
545
        if (output->isEnabled()) {
            Q_EMIT outputDisabled(output);
        }
546
547
548
549
550
        Q_EMIT outputRemoved(output);
        delete output;
    }
}

551
bool DrmGpu::isFormatSupported(uint32_t drmFormat) const
Xaver Hugl's avatar
Xaver Hugl committed
552
553
{
    if (!m_atomicModeSetting) {
554
        return drmFormat == DRM_FORMAT_XRGB8888 || drmFormat == DRM_FORMAT_ARGB8888;
Xaver Hugl's avatar
Xaver Hugl committed
555
556
    } else {
        for (const auto &plane : qAsConst(m_planes)) {
557
            if (plane->type() == DrmPlane::TypeIndex::Primary && !plane->formats().contains(drmFormat)) {
Xaver Hugl's avatar
Xaver Hugl committed
558
559
560
561
562
563
564
                return false;
            }
        }
        return true;
    }
}

565
566
567
568
569
bool DrmGpu::isNVidia() const
{
    return m_isNVidia;
}

570
}