Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/drm/drm_output.cpp
Show First 20 Lines • Show All 56 Lines • ▼ Show 20 Line(s) | 56 | DrmOutput::DrmOutput(DrmBackend *backend) | |||
---|---|---|---|---|---|
57 | : QObject() | 57 | : QObject() | ||
58 | , m_backend(backend) | 58 | , m_backend(backend) | ||
59 | { | 59 | { | ||
60 | } | 60 | } | ||
61 | 61 | | |||
62 | DrmOutput::~DrmOutput() | 62 | DrmOutput::~DrmOutput() | ||
63 | { | 63 | { | ||
64 | hideCursor(); | 64 | hideCursor(); | ||
65 | cleanupBlackBuffer(); | 65 | m_crtc->blank(); | ||
66 | delete m_crtc; | 66 | | ||
67 | delete m_conn; | 67 | if (m_primaryPlane) { | ||
68 | // TODO: when having multiple planes, also clean up these | ||||
69 | m_primaryPlane->setOutput(nullptr); | ||||
70 | | ||||
71 | if (m_backend->deleteBufferAfterPageFlip()) { | ||||
72 | delete m_primaryPlane->current(); | ||||
73 | } | ||||
74 | m_primaryPlane->setCurrent(nullptr); | ||||
75 | } | ||||
76 | | ||||
77 | m_crtc->setOutput(nullptr); | ||||
78 | m_conn->setOutput(nullptr); | ||||
79 | | ||||
68 | delete m_waylandOutput.data(); | 80 | delete m_waylandOutput.data(); | ||
69 | delete m_waylandOutputDevice.data(); | 81 | delete m_waylandOutputDevice.data(); | ||
70 | } | 82 | } | ||
71 | 83 | | |||
72 | void DrmOutput::cleanup() | 84 | void DrmOutput::releaseGbm() | ||
73 | { | 85 | { | ||
74 | if (m_currentBuffer) { | 86 | if (DrmBuffer *b = m_crtc->current()) { | ||
75 | m_currentBuffer->releaseGbm(); | 87 | b->releaseGbm(); | ||
76 | } | | |||
77 | if (m_nextBuffer) { | | |||
78 | m_nextBuffer->releaseGbm(); | | |||
79 | } | 88 | } | ||
80 | if (m_primaryPlane) { | 89 | if (m_primaryPlane && m_primaryPlane->current()) { | ||
81 | if (m_primaryPlane->current()) { | | |||
82 | m_primaryPlane->current()->releaseGbm(); | 90 | m_primaryPlane->current()->releaseGbm(); | ||
83 | } | 91 | } | ||
84 | if (m_primaryPlane->next()) { | | |||
85 | m_primaryPlane->next()->releaseGbm(); | | |||
86 | } | | |||
87 | } | | |||
88 | } | 92 | } | ||
89 | 93 | | |||
90 | void DrmOutput::hideCursor() | 94 | void DrmOutput::hideCursor() | ||
91 | { | 95 | { | ||
92 | drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); | 96 | drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0); | ||
93 | } | 97 | } | ||
94 | 98 | | |||
95 | void DrmOutput::restoreSaved() | 99 | void DrmOutput::showCursor(DrmDumbBuffer *c) | ||
96 | { | | |||
97 | if (!m_savedCrtc.isNull()) { | | |||
98 | drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, | | |||
99 | m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); | | |||
100 | } | | |||
101 | } | | |||
102 | | ||||
103 | void DrmOutput::showCursor(DrmBuffer *c) | | |||
104 | { | 100 | { | ||
105 | const QSize &s = c->size(); | 101 | const QSize &s = c->size(); | ||
106 | drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); | 102 | drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()); | ||
107 | } | 103 | } | ||
108 | 104 | | |||
109 | void DrmOutput::moveCursor(const QPoint &globalPos) | 105 | void DrmOutput::moveCursor(const QPoint &globalPos) | ||
110 | { | 106 | { | ||
111 | const QPoint p = (globalPos - m_globalPos) * m_scale; | 107 | const QPoint p = (globalPos - m_globalPos) * m_scale; | ||
112 | drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); | 108 | drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); | ||
113 | } | 109 | } | ||
114 | 110 | | |||
115 | QSize DrmOutput::pixelSize() const | 111 | QSize DrmOutput::pixelSize() const | ||
116 | { | 112 | { | ||
117 | return QSize(m_mode.hdisplay, m_mode.vdisplay); | 113 | return QSize(m_mode.hdisplay, m_mode.vdisplay); | ||
118 | } | 114 | } | ||
119 | 115 | | |||
120 | QRect DrmOutput::geometry() const | 116 | QRect DrmOutput::geometry() const | ||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Line(s) | |||||
187 | { | 183 | { | ||
188 | initEdid(connector); | 184 | initEdid(connector); | ||
189 | initDpms(connector); | 185 | initDpms(connector); | ||
190 | initUuid(); | 186 | initUuid(); | ||
191 | if (m_backend->atomicModeSetting()) { | 187 | if (m_backend->atomicModeSetting()) { | ||
192 | if (!initPrimaryPlane()) { | 188 | if (!initPrimaryPlane()) { | ||
193 | return false; | 189 | return false; | ||
194 | } | 190 | } | ||
195 | } | 191 | } else if (!m_crtc->blank()) { | ||
196 | m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); | | |||
197 | if (!blank()) { | | |||
198 | return false; | 192 | return false; | ||
199 | } | 193 | } | ||
194 | | ||||
200 | setDpms(DpmsMode::On); | 195 | setDpms(DpmsMode::On); | ||
201 | if (!m_waylandOutput.isNull()) { | 196 | if (!m_waylandOutput.isNull()) { | ||
202 | delete m_waylandOutput.data(); | 197 | delete m_waylandOutput.data(); | ||
203 | m_waylandOutput.clear(); | 198 | m_waylandOutput.clear(); | ||
204 | } | 199 | } | ||
205 | m_waylandOutput = waylandServer()->display()->createOutput(); | 200 | m_waylandOutput = waylandServer()->display()->createOutput(); | ||
206 | if (!m_waylandOutputDevice.isNull()) { | 201 | if (!m_waylandOutputDevice.isNull()) { | ||
207 | delete m_waylandOutputDevice.data(); | 202 | delete m_waylandOutputDevice.data(); | ||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Line(s) | |||||
307 | qCDebug(KWIN_DRM) << "Created OutputDevice"; | 302 | qCDebug(KWIN_DRM) << "Created OutputDevice"; | ||
308 | m_waylandOutputDevice->create(); | 303 | m_waylandOutputDevice->create(); | ||
309 | return true; | 304 | return true; | ||
310 | } | 305 | } | ||
311 | 306 | | |||
312 | void DrmOutput::initUuid() | 307 | void DrmOutput::initUuid() | ||
313 | { | 308 | { | ||
314 | QCryptographicHash hash(QCryptographicHash::Md5); | 309 | QCryptographicHash hash(QCryptographicHash::Md5); | ||
315 | hash.addData(QByteArray::number(m_connector)); | 310 | hash.addData(QByteArray::number(m_conn->id())); | ||
316 | hash.addData(m_edid.eisaId); | 311 | hash.addData(m_edid.eisaId); | ||
317 | hash.addData(m_edid.monitorName); | 312 | hash.addData(m_edid.monitorName); | ||
318 | hash.addData(m_edid.serialNumber); | 313 | hash.addData(m_edid.serialNumber); | ||
319 | m_uuid = hash.result().toHex().left(10); | 314 | m_uuid = hash.result().toHex().left(10); | ||
320 | } | 315 | } | ||
321 | 316 | | |||
322 | bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const | 317 | bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const | ||
323 | { | 318 | { | ||
▲ Show 20 Lines • Show All 184 Lines • ▼ Show 20 Line(s) | 502 | if (p->type() != DrmPlane::TypeIndex::Primary) { | |||
508 | continue; | 503 | continue; | ||
509 | } | 504 | } | ||
510 | if (p->output()) { // Plane already has an output | 505 | if (p->output()) { // Plane already has an output | ||
511 | continue; | 506 | continue; | ||
512 | } | 507 | } | ||
513 | if (m_primaryPlane) { // Output already has a primary plane | 508 | if (m_primaryPlane) { // Output already has a primary plane | ||
514 | continue; | 509 | continue; | ||
515 | } | 510 | } | ||
516 | if (!p->isCrtcSupported(m_crtcId)) { | 511 | if (!p->isCrtcSupported(m_crtc->resIndex())) { | ||
517 | continue; | 512 | continue; | ||
518 | } | 513 | } | ||
519 | p->setOutput(this); | 514 | p->setOutput(this); | ||
520 | m_primaryPlane = p; | 515 | m_primaryPlane = p; | ||
521 | qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId; | 516 | qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtc->id(); | ||
522 | return true; | 517 | return true; | ||
523 | } | 518 | } | ||
524 | qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; | 519 | qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; | ||
525 | return false; | 520 | return false; | ||
526 | } | 521 | } | ||
527 | 522 | | |||
528 | bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) | 523 | bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) | ||
529 | { | 524 | { | ||
530 | for (int i = 0; i < m_backend->planes().size(); ++i) { | 525 | for (int i = 0; i < m_backend->planes().size(); ++i) { | ||
531 | DrmPlane* p = m_backend->planes()[i]; | 526 | DrmPlane* p = m_backend->planes()[i]; | ||
532 | if (!p) { | 527 | if (!p) { | ||
533 | continue; | 528 | continue; | ||
534 | } | 529 | } | ||
535 | if (p->type() != DrmPlane::TypeIndex::Cursor) { | 530 | if (p->type() != DrmPlane::TypeIndex::Cursor) { | ||
536 | continue; | 531 | continue; | ||
537 | } | 532 | } | ||
538 | if (p->output()) { // Plane already has an output | 533 | if (p->output()) { // Plane already has an output | ||
539 | continue; | 534 | continue; | ||
540 | } | 535 | } | ||
541 | if (m_cursorPlane) { // Output already has a cursor plane | 536 | if (m_cursorPlane) { // Output already has a cursor plane | ||
542 | continue; | 537 | continue; | ||
543 | } | 538 | } | ||
544 | if (!p->isCrtcSupported(m_crtcId)) { | 539 | if (!p->isCrtcSupported(m_crtc->resIndex())) { | ||
545 | continue; | 540 | continue; | ||
546 | } | 541 | } | ||
547 | p->setOutput(this); | 542 | p->setOutput(this); | ||
548 | m_cursorPlane = p; | 543 | m_cursorPlane = p; | ||
549 | qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId; | 544 | qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtc->id(); | ||
550 | return true; | 545 | return true; | ||
551 | } | 546 | } | ||
552 | return false; | 547 | return false; | ||
553 | } | 548 | } | ||
554 | 549 | | |||
555 | void DrmOutput::initDpms(drmModeConnector *connector) | 550 | void DrmOutput::initDpms(drmModeConnector *connector) | ||
556 | { | 551 | { | ||
557 | for (int i = 0; i < connector->count_props; ++i) { | 552 | for (int i = 0; i < connector->count_props; ++i) { | ||
558 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | 553 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | ||
559 | if (!property) { | 554 | if (!property) { | ||
560 | continue; | 555 | continue; | ||
561 | } | 556 | } | ||
562 | if (qstrcmp(property->name, "DPMS") == 0) { | 557 | if (qstrcmp(property->name, "DPMS") == 0) { | ||
563 | m_dpms.swap(property); | 558 | m_dpms.swap(property); | ||
564 | break; | 559 | break; | ||
565 | } | 560 | } | ||
566 | } | 561 | } | ||
567 | } | 562 | } | ||
568 | 563 | | |||
569 | void DrmOutput::setDpms(DrmOutput::DpmsMode mode) | 564 | void DrmOutput::setDpms(DrmOutput::DpmsMode mode) | ||
570 | { | 565 | { | ||
571 | if (m_dpms.isNull()) { | 566 | if (m_dpms.isNull()) { | ||
572 | return; | 567 | return; | ||
573 | } | 568 | } | ||
574 | if (mode == m_dpmsMode) { | 569 | if (mode == m_dpmsModePending) { | ||
575 | qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; | 570 | qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; | ||
576 | return; | 571 | return; | ||
577 | } | 572 | } | ||
578 | 573 | | |||
579 | if (m_backend->atomicModeSetting()) { | 574 | m_dpmsModePending = mode; | ||
580 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | | |||
581 | 575 | | |||
582 | if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) { | 576 | if (m_backend->atomicModeSetting()) { | ||
583 | qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtcId; | 577 | m_modesetRequested = true; | ||
584 | return; | 578 | if (mode == DpmsMode::On) { | ||
579 | if (m_pageFlipPending) { | ||||
580 | m_pageFlipPending = false; | ||||
581 | Compositor::self()->bufferSwapComplete(); | ||||
585 | } | 582 | } | ||
586 | if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) { | 583 | dpmsOnHandler(); | ||
587 | qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtcId; | | |||
588 | } else { | 584 | } else { | ||
589 | qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId; | 585 | m_dpmsAtomicOffPending = true; | ||
586 | if (!m_pageFlipPending) { | ||||
587 | dpmsAtomicOff(); | ||||
588 | } | ||||
590 | } | 589 | } | ||
591 | drmModeAtomicFree(req); | | |||
592 | } else { | 590 | } else { | ||
593 | if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) { | 591 | if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) { | ||
592 | m_dpmsModePending = m_dpmsMode; | ||||
594 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | 593 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | ||
595 | return; | 594 | return; | ||
596 | } | 595 | } | ||
596 | if (mode == DpmsMode::On) { | ||||
597 | dpmsOnHandler(); | ||||
598 | } else { | ||||
599 | dpmsOffHandler(); | ||||
600 | } | ||||
601 | m_dpmsMode = m_dpmsModePending; | ||||
597 | } | 602 | } | ||
603 | } | ||||
604 | | ||||
605 | void DrmOutput::dpmsOnHandler() | ||||
606 | { | ||||
607 | qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On."; | ||||
598 | 608 | | |||
599 | m_dpmsMode = mode; | | |||
600 | if (m_waylandOutput) { | 609 | if (m_waylandOutput) { | ||
601 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); | 610 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); | ||
602 | } | 611 | } | ||
603 | emit dpmsChanged(); | 612 | emit dpmsChanged(); | ||
604 | if (m_dpmsMode != DpmsMode::On) { | 613 | | ||
605 | m_backend->outputWentOff(); | | |||
606 | } else { | | |||
607 | m_backend->checkOutputsAreOn(); | 614 | m_backend->checkOutputsAreOn(); | ||
608 | blank(); | 615 | if (!m_backend->atomicModeSetting()) { | ||
616 | m_crtc->blank(); | ||||
617 | } | ||||
609 | if (Compositor *compositor = Compositor::self()) { | 618 | if (Compositor *compositor = Compositor::self()) { | ||
610 | compositor->addRepaintFull(); | 619 | compositor->addRepaintFull(); | ||
611 | } | 620 | } | ||
612 | } | 621 | } | ||
622 | | ||||
623 | void DrmOutput::dpmsOffHandler() | ||||
624 | { | ||||
625 | qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off."; | ||||
626 | | ||||
627 | if (m_waylandOutput) { | ||||
628 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); | ||||
629 | } | ||||
630 | emit dpmsChanged(); | ||||
631 | | ||||
632 | m_backend->outputWentOff(); | ||||
613 | } | 633 | } | ||
614 | 634 | | |||
615 | QString DrmOutput::name() const | 635 | QString DrmOutput::name() const | ||
616 | { | 636 | { | ||
617 | if (!m_waylandOutput) { | 637 | if (!m_waylandOutput) { | ||
618 | return i18n("unknown"); | 638 | return i18n("unknown"); | ||
619 | } | 639 | } | ||
620 | return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); | 640 | return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); | ||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Line(s) | 712 | if (m_changeset->scaleChanged()) { | |||
693 | qCDebug(KWIN_DRM) << "Setting scale:" << m_changeset->scale(); | 713 | qCDebug(KWIN_DRM) << "Setting scale:" << m_changeset->scale(); | ||
694 | setScale(m_changeset->scale()); | 714 | setScale(m_changeset->scale()); | ||
695 | } | 715 | } | ||
696 | return true; | 716 | return true; | ||
697 | } | 717 | } | ||
698 | 718 | | |||
699 | void DrmOutput::pageFlipped() | 719 | void DrmOutput::pageFlipped() | ||
700 | { | 720 | { | ||
701 | if (m_backend->atomicModeSetting()){ | 721 | m_pageFlipPending = false; | ||
702 | foreach (DrmPlane *p, m_planesFlipList) { | | |||
703 | pageFlippedBufferRemover(p->current(), p->next()); | | |||
704 | p->setCurrent(p->next()); | | |||
705 | p->setNext(nullptr); | | |||
706 | } | | |||
707 | m_planesFlipList.clear(); | | |||
708 | 722 | | |||
709 | } else { | 723 | if (!m_crtc) { | ||
710 | if (!m_nextBuffer) { | 724 | return; | ||
725 | } | ||||
726 | // Egl based surface buffers get destroyed, QPainter based dumb buffers not | ||||
727 | // TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely? | ||||
728 | if (m_backend->deleteBufferAfterPageFlip()) { | ||||
729 | if (m_backend->atomicModeSetting()) { | ||||
730 | if (!m_primaryPlane->next()) { | ||||
711 | // on manual vt switch | 731 | // on manual vt switch | ||
712 | if (m_currentBuffer) { | 732 | // TODO: when we later use overlay planes it might happen, that we have a page flip with only | ||
713 | m_currentBuffer->releaseGbm(); | 733 | // damage on one of these, and therefore the primary plane has no next buffer | ||
734 | // -> Then we don't want to return here! | ||||
735 | if (m_primaryPlane->current()) { | ||||
736 | m_primaryPlane->current()->releaseGbm(); | ||||
714 | } | 737 | } | ||
715 | return; | 738 | return; | ||
716 | } | 739 | } | ||
717 | pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer); | 740 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||
718 | m_currentBuffer = m_nextBuffer; | 741 | p->flipBufferWithDelete(); | ||
719 | m_nextBuffer = nullptr; | | |||
720 | } | | |||
721 | cleanupBlackBuffer(); | | |||
722 | } | 742 | } | ||
723 | 743 | m_nextPlanesFlipList.clear(); | |||
724 | void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) | 744 | } else { | ||
725 | { | 745 | if (!m_crtc->next()) { | ||
726 | if (oldbuffer && oldbuffer->deleteAfterPageFlip() && oldbuffer != newbuffer) { | 746 | // on manual vt switch | ||
727 | delete oldbuffer; | 747 | if (DrmBuffer *b = m_crtc->current()) { | ||
748 | b->releaseGbm(); | ||||
728 | } | 749 | } | ||
729 | } | 750 | } | ||
730 | 751 | m_crtc->flipBuffer(); | |||
731 | void DrmOutput::cleanupBlackBuffer() | | |||
732 | { | | |||
733 | if (m_blackBuffer) { | | |||
734 | delete m_blackBuffer; | | |||
735 | m_blackBuffer = nullptr; | | |||
736 | } | 752 | } | ||
753 | } else { | ||||
754 | if (m_backend->atomicModeSetting()){ | ||||
755 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||||
756 | p->flipBuffer(); | ||||
737 | } | 757 | } | ||
738 | 758 | m_nextPlanesFlipList.clear(); | |||
739 | bool DrmOutput::blank() | 759 | } else { | ||
740 | { | 760 | m_crtc->flipBuffer(); | ||
741 | if (!m_blackBuffer) { | | |||
742 | m_blackBuffer = m_backend->createBuffer(pixelSize()); | | |||
743 | if (!m_blackBuffer->map()) { | | |||
744 | cleanupBlackBuffer(); | | |||
745 | return false; | | |||
746 | } | 761 | } | ||
747 | m_blackBuffer->image()->fill(Qt::black); | 762 | m_crtc->flipBuffer(); | ||
748 | } | 763 | } | ||
749 | // TODO: Do this atomically | | |||
750 | return setModeLegacy(m_blackBuffer); | | |||
751 | } | 764 | } | ||
752 | 765 | | |||
753 | bool DrmOutput::present(DrmBuffer *buffer) | 766 | bool DrmOutput::present(DrmBuffer *buffer) | ||
754 | { | 767 | { | ||
755 | if (!buffer || buffer->bufferId() == 0) { | 768 | if (!buffer || buffer->bufferId() == 0) { | ||
756 | return false; | 769 | return false; | ||
757 | } | 770 | } | ||
758 | if (m_backend->atomicModeSetting()) { | 771 | if (m_backend->atomicModeSetting()) { | ||
759 | return presentAtomically(buffer); | 772 | return presentAtomically(buffer); | ||
760 | } else { | 773 | } else { | ||
761 | return presentLegacy(buffer); | 774 | return presentLegacy(buffer); | ||
762 | } | 775 | } | ||
763 | } | 776 | } | ||
764 | 777 | | |||
765 | bool DrmOutput::presentAtomically(DrmBuffer *buffer) | 778 | bool DrmOutput::dpmsAtomicOff() | ||
766 | { | 779 | { | ||
767 | if (!LogindIntegration::self()->isActiveSession()) { | 780 | m_dpmsAtomicOffPending = false; | ||
768 | qCWarning(KWIN_DRM) << "Logind session not active."; | 781 | | ||
769 | return false; | 782 | // TODO: With multiple planes: deactivate all of them here | ||
770 | } | 783 | delete m_primaryPlane->next(); | ||
771 | if (m_dpmsMode != DpmsMode::On) { | 784 | m_primaryPlane->setNext(nullptr); | ||
772 | qCWarning(KWIN_DRM) << "No present() while screen off."; | 785 | m_nextPlanesFlipList << m_primaryPlane; | ||
786 | | ||||
787 | if (!doAtomicCommit(AtomicCommitMode::Test)) { | ||||
788 | qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting."; | ||||
773 | return false; | 789 | return false; | ||
774 | } | 790 | } | ||
775 | if (m_primaryPlane->next()) { | 791 | if (!doAtomicCommit(AtomicCommitMode::Real)) { | ||
776 | qCWarning(KWIN_DRM) << "Page not yet flipped."; | 792 | qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting."; | ||
777 | return false; | 793 | return false; | ||
778 | } | 794 | } | ||
795 | m_nextPlanesFlipList.clear(); | ||||
796 | dpmsOffHandler(); | ||||
779 | 797 | | |||
780 | DrmObject::AtomicReturn ret; | 798 | return true; | ||
781 | uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; | | |||
782 | 799 | | |||
783 | // TODO: throwing an exception would be really handy here! (would mean change of compile options) | | |||
784 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | | |||
785 | if (!req) { | | |||
786 | qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; | | |||
787 | delete buffer; | | |||
788 | return false; | | |||
789 | } | 800 | } | ||
790 | 801 | | |||
791 | // Do we need to set a new mode first? | 802 | bool DrmOutput::presentAtomically(DrmBuffer *buffer) | ||
792 | bool doModeset = !m_primaryPlane->current(); | 803 | { | ||
793 | if (doModeset) { | 804 | if (!LogindIntegration::self()->isActiveSession()) { | ||
794 | qCDebug(KWIN_DRM) << "Atomic Modeset requested"; | 805 | qCWarning(KWIN_DRM) << "Logind session not active."; | ||
795 | | ||||
796 | if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId)) { | | |||
797 | qCWarning(KWIN_DRM) << "Failed to create property blob"; | | |||
798 | delete buffer; | | |||
799 | return false; | 806 | return false; | ||
800 | } | 807 | } | ||
801 | 808 | | |||
802 | ret = atomicReqModesetPopulate(req, true); | 809 | if (m_pageFlipPending) { | ||
803 | if (ret == DrmObject::AtomicReturn::Error){ | 810 | qCWarning(KWIN_DRM) << "Page not yet flipped."; | ||
804 | drmModeAtomicFree(req); | | |||
805 | delete buffer; | | |||
806 | return false; | 811 | return false; | ||
807 | } | 812 | } | ||
808 | if (ret == DrmObject::AtomicReturn::Success) { | | |||
809 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | | |||
810 | } | | |||
811 | } | | |||
812 | 813 | | |||
813 | m_primaryPlane->setNext(buffer); // TODO: Later not only use the primary plane for the buffer! | 814 | m_primaryPlane->setNext(buffer); | ||
814 | // i.e.: Assign planes | 815 | m_nextPlanesFlipList << m_primaryPlane; | ||
815 | bool anyDamage = false; | | |||
816 | foreach (DrmPlane* p, m_backend->planes()){ | | |||
817 | if (p->output() != this) { | | |||
818 | continue; | | |||
819 | } | | |||
820 | ret = p->atomicReqPlanePopulate(req); | | |||
821 | if (ret == DrmObject::AtomicReturn::Error) { | | |||
822 | drmModeAtomicFree(req); | | |||
823 | m_primaryPlane->setNext(nullptr); | | |||
824 | m_planesFlipList.clear(); | | |||
825 | delete buffer; | | |||
826 | return false; | | |||
827 | } | | |||
828 | if (ret == DrmObject::AtomicReturn::Success) { | | |||
829 | anyDamage = true; | | |||
830 | m_planesFlipList << p; | | |||
831 | } | | |||
832 | } | | |||
833 | 816 | | |||
834 | // no damage but force flip for atleast the primary plane anyway | 817 | if (!doAtomicCommit(AtomicCommitMode::Test)) { | ||
835 | if (!anyDamage) { | 818 | //TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout? | ||
836 | m_primaryPlane->setPropsValid(0); | 819 | qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present."; | ||
837 | if (m_primaryPlane->atomicReqPlanePopulate(req) == DrmObject::AtomicReturn::Error) { | 820 | if (this->m_backend->deleteBufferAfterPageFlip()) { | ||
838 | drmModeAtomicFree(req); | | |||
839 | m_primaryPlane->setNext(nullptr); | | |||
840 | m_planesFlipList.clear(); | | |||
841 | delete buffer; | 821 | delete buffer; | ||
842 | return false; | | |||
843 | } | 822 | } | ||
844 | m_planesFlipList << m_primaryPlane; | | |||
845 | } | | |||
846 | | ||||
847 | if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { | | |||
848 | qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); | | |||
849 | drmModeAtomicFree(req); | | |||
850 | m_primaryPlane->setNext(nullptr); | | |||
851 | m_planesFlipList.clear(); | | |||
852 | delete buffer; | | |||
853 | return false; | 823 | return false; | ||
854 | } | 824 | } | ||
855 | 825 | if (!doAtomicCommit(AtomicCommitMode::Real)) { | |||
856 | if (doModeset) { | 826 | qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present."; | ||
857 | m_crtc->setPropsValid(m_crtc->propsValid() | m_crtc->propsPending()); | 827 | return false; | ||
858 | m_conn->setPropsValid(m_conn->propsValid() | m_conn->propsPending()); | | |||
859 | } | | |||
860 | foreach (DrmPlane* p, m_planesFlipList) { | | |||
861 | p->setPropsValid(p->propsValid() | p->propsPending()); | | |||
862 | } | 828 | } | ||
863 | 829 | m_pageFlipPending = true; | |||
864 | drmModeAtomicFree(req); | | |||
865 | return true; | 830 | return true; | ||
866 | } | 831 | } | ||
867 | 832 | | |||
868 | | ||||
869 | bool DrmOutput::presentLegacy(DrmBuffer *buffer) | 833 | bool DrmOutput::presentLegacy(DrmBuffer *buffer) | ||
870 | { | 834 | { | ||
871 | if (m_nextBuffer) { | 835 | if (m_crtc->next()) { | ||
872 | return false; | 836 | return false; | ||
873 | } | 837 | } | ||
874 | if (!LogindIntegration::self()->isActiveSession()) { | 838 | if (!LogindIntegration::self()->isActiveSession()) { | ||
875 | m_nextBuffer = buffer; | 839 | m_crtc->setNext(buffer); | ||
876 | return false; | 840 | return false; | ||
877 | } | 841 | } | ||
878 | if (m_dpmsMode != DpmsMode::On) { | 842 | if (m_dpmsMode != DpmsMode::On) { | ||
879 | return false; | 843 | return false; | ||
880 | } | 844 | } | ||
881 | 845 | | |||
882 | // Do we need to set a new mode first? | 846 | // Do we need to set a new mode first? | ||
883 | if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){ | 847 | if (!m_crtc->current() || m_crtc->current()->needsModeChange(buffer)) { | ||
884 | if (!setModeLegacy(buffer)) | 848 | if (!setModeLegacy(buffer)) { | ||
885 | return false; | 849 | return false; | ||
886 | } | 850 | } | ||
851 | } | ||||
887 | int errno_save = 0; | 852 | int errno_save = 0; | ||
888 | const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; | 853 | const bool ok = drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; | ||
889 | if (ok) { | 854 | if (ok) { | ||
890 | m_nextBuffer = buffer; | 855 | m_crtc->setNext(buffer); | ||
891 | } else { | 856 | } else { | ||
892 | errno_save = errno; | 857 | errno_save = errno; | ||
893 | qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); | 858 | qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); | ||
894 | delete buffer; | 859 | delete buffer; | ||
895 | } | 860 | } | ||
896 | return ok; | 861 | return ok; | ||
897 | } | 862 | } | ||
898 | 863 | | |||
899 | bool DrmOutput::setModeLegacy(DrmBuffer *buffer) | 864 | bool DrmOutput::setModeLegacy(DrmBuffer *buffer) | ||
900 | { | 865 | { | ||
901 | if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { | 866 | uint32_t connId = m_conn->id(); | ||
902 | m_lastStride = buffer->stride(); | 867 | if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { | ||
903 | m_lastGbm = buffer->isGbm(); | | |||
904 | return true; | 868 | return true; | ||
905 | } else { | 869 | } else { | ||
906 | qCWarning(KWIN_DRM) << "Mode setting failed"; | 870 | qCWarning(KWIN_DRM) << "Mode setting failed"; | ||
907 | return false; | 871 | return false; | ||
908 | } | 872 | } | ||
909 | } | 873 | } | ||
910 | 874 | | |||
911 | DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) | 875 | bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) | ||
912 | { | 876 | { | ||
913 | if (enable) { | 877 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | ||
914 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); | 878 | | ||
915 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); | 879 | auto errorHandler = [this, mode, req] () { | ||
916 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); | 880 | if (mode == AtomicCommitMode::Test) { | ||
917 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); | 881 | // TODO: when we later test overlay planes, make sure we change only the right stuff back | ||
918 | } else { | 882 | } | ||
919 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), 0); | 883 | if (req) { | ||
920 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), 0); | 884 | drmModeAtomicFree(req); | ||
921 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), 0); | | |||
922 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), 0); | | |||
923 | } | 885 | } | ||
924 | 886 | | |||
925 | bool ret = true; | 887 | if (m_dpmsMode != m_dpmsModePending) { | ||
888 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | ||||
889 | m_dpmsModePending = m_dpmsMode; | ||||
890 | if (m_dpmsMode != DpmsMode::On) { | ||||
891 | dpmsOffHandler(); | ||||
892 | } | ||||
893 | } | ||||
926 | 894 | | |||
927 | m_crtc->setPropsPending(0); | 895 | // TODO: see above, rework later for overlay planes! | ||
928 | m_conn->setPropsPending(0); | 896 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||
897 | p->setNext(nullptr); | ||||
898 | } | ||||
899 | m_nextPlanesFlipList.clear(); | ||||
929 | 900 | | |||
930 | ret &= m_conn->atomicAddProperty(req, int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); | 901 | }; | ||
931 | ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); | 902 | | ||
932 | ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::Active), enable); | 903 | if (!req) { | ||
904 | qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; | ||||
905 | errorHandler(); | ||||
906 | return false; | ||||
907 | } | ||||
908 | | ||||
909 | uint32_t flags = 0; | ||||
910 | | ||||
911 | // Do we need to set a new mode? | ||||
912 | if (m_modesetRequested) { | ||||
913 | if (m_dpmsModePending == DpmsMode::On) { | ||||
914 | if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) { | ||||
915 | qCWarning(KWIN_DRM) << "Failed to create property blob"; | ||||
916 | errorHandler(); | ||||
917 | return false; | ||||
918 | } | ||||
919 | } | ||||
920 | if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){ | ||||
921 | qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset"; | ||||
922 | errorHandler(); | ||||
923 | return false; | ||||
924 | } | ||||
925 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | ||||
926 | } | ||||
927 | | ||||
928 | if (mode == AtomicCommitMode::Real) { | ||||
929 | if (m_dpmsModePending == DpmsMode::On) { | ||||
930 | if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { | ||||
931 | // TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10. | ||||
932 | flags |= DRM_MODE_ATOMIC_NONBLOCK; | ||||
933 | } | ||||
934 | flags |= DRM_MODE_PAGE_FLIP_EVENT; | ||||
935 | } | ||||
936 | } else { | ||||
937 | flags |= DRM_MODE_ATOMIC_TEST_ONLY; | ||||
938 | } | ||||
939 | | ||||
940 | bool ret = true; | ||||
941 | // TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order. | ||||
942 | for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) { | ||||
943 | DrmPlane *p = m_nextPlanesFlipList[i]; | ||||
944 | ret &= p->atomicPopulate(req); | ||||
945 | } | ||||
933 | 946 | | |||
934 | if (!ret) { | 947 | if (!ret) { | ||
935 | qCWarning(KWIN_DRM) << "Failed to populate atomic modeset"; | 948 | qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!"; | ||
936 | return DrmObject::AtomicReturn::Error; | 949 | errorHandler(); | ||
950 | return false; | ||||
951 | } | ||||
952 | | ||||
953 | if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { | ||||
954 | qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); | ||||
955 | errorHandler(); | ||||
956 | return false; | ||||
957 | } | ||||
958 | | ||||
959 | if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { | ||||
960 | qCDebug(KWIN_DRM) << "Atomic Modeset successful."; | ||||
961 | m_modesetRequested = false; | ||||
962 | m_dpmsMode = m_dpmsModePending; | ||||
963 | } | ||||
964 | | ||||
965 | drmModeAtomicFree(req); | ||||
966 | return true; | ||||
937 | } | 967 | } | ||
938 | if (!m_crtc->propsPending() && !m_conn->propsPending()) { | 968 | | ||
939 | return DrmObject::AtomicReturn::NoChange; | 969 | bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) | ||
970 | { | ||||
971 | if (enable) { | ||||
972 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); | ||||
973 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); | ||||
974 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); | ||||
975 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); | ||||
976 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); | ||||
977 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); | ||||
978 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); | ||||
979 | } else { | ||||
980 | if (m_backend->deleteBufferAfterPageFlip()) { | ||||
981 | delete m_primaryPlane->current(); | ||||
982 | delete m_primaryPlane->next(); | ||||
940 | } | 983 | } | ||
941 | return DrmObject::AtomicReturn::Success; | 984 | m_primaryPlane->setCurrent(nullptr); | ||
985 | m_primaryPlane->setNext(nullptr); | ||||
986 | | ||||
987 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcX), 0); | ||||
988 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcY), 0); | ||||
989 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); | ||||
990 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); | ||||
991 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); | ||||
992 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); | ||||
993 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); | ||||
994 | } | ||||
995 | m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); | ||||
996 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); | ||||
997 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), enable); | ||||
998 | | ||||
999 | bool ret = true; | ||||
1000 | ret &= m_conn->atomicPopulate(req); | ||||
1001 | ret &= m_crtc->atomicPopulate(req); | ||||
1002 | | ||||
1003 | return ret; | ||||
942 | } | 1004 | } | ||
943 | 1005 | | |||
944 | } | 1006 | } |