Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/drm/drm_output.cpp
Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | 56 | DrmOutput::DrmOutput(DrmBackend *backend) | |||
---|---|---|---|---|---|
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 | cleanupBlackBuffer(); | ||
66 | delete m_currentBuffer; | ||||
67 | delete m_nextBuffer; | ||||
68 | if (m_backend->atomicModeSetting()) { | ||||
69 | // TODO: when having multiple planes, also clean up these | ||||
70 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | ||||
71 | bool modeset = false; | ||||
72 | if (m_primaryPlane) { | ||||
73 | delete m_primaryPlane->current(); | ||||
74 | delete m_primaryPlane->next(); | ||||
75 | m_primaryPlane->setCurrent(nullptr); | ||||
76 | m_primaryPlane->setNext(nullptr); | ||||
77 | | ||||
78 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); | ||||
79 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); | ||||
80 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); | ||||
81 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); | ||||
82 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); | ||||
83 | | ||||
84 | m_primaryPlane->atomicPopulate(req); | ||||
85 | modeset = true; | ||||
86 | } | ||||
87 | if (m_conn) { | ||||
88 | m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), 0); | ||||
89 | m_conn->atomicPopulate(req); | ||||
90 | modeset = true; | ||||
91 | } | ||||
92 | if (m_crtc) { | ||||
93 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), 0); | ||||
94 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), 0); | ||||
95 | m_crtc->atomicPopulate(req); | ||||
96 | modeset = true; | ||||
97 | } | ||||
98 | if (modeset) { | ||||
99 | drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this); | ||||
100 | } | ||||
101 | m_primaryPlane->setOutput(nullptr); | ||||
102 | } | ||||
66 | delete m_crtc; | 103 | delete m_crtc; | ||
67 | delete m_conn; | 104 | delete m_conn; | ||
68 | delete m_waylandOutput.data(); | 105 | delete m_waylandOutput.data(); | ||
69 | delete m_waylandOutputDevice.data(); | 106 | delete m_waylandOutputDevice.data(); | ||
70 | } | 107 | } | ||
71 | 108 | | |||
72 | void DrmOutput::hideCursor() | 109 | void DrmOutput::hideCursor() | ||
73 | { | 110 | { | ||
74 | drmModeSetCursor(m_backend->fd(), m_crtcId, 0, 0, 0); | 111 | drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0); | ||
75 | } | 112 | } | ||
76 | 113 | | |||
77 | void DrmOutput::restoreSaved() | 114 | void DrmOutput::restoreSaved() | ||
78 | { | 115 | { | ||
79 | if (!m_savedCrtc.isNull()) { | 116 | if (!m_savedCrtc.isNull()) { | ||
117 | uint32_t connId = m_conn->id(); | ||||
80 | drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, | 118 | drmModeSetCrtc(m_backend->fd(), m_savedCrtc->crtc_id, m_savedCrtc->buffer_id, | ||
81 | m_savedCrtc->x, m_savedCrtc->y, &m_connector, 1, &m_savedCrtc->mode); | 119 | m_savedCrtc->x, m_savedCrtc->y, &connId, 1, &m_savedCrtc->mode); | ||
82 | } | 120 | } | ||
83 | } | 121 | } | ||
84 | 122 | | |||
85 | void DrmOutput::showCursor(DrmBuffer *c) | 123 | void DrmOutput::showCursor(DrmDumbBuffer *c) | ||
86 | { | 124 | { | ||
87 | const QSize &s = c->size(); | 125 | const QSize &s = c->size(); | ||
88 | drmModeSetCursor(m_backend->fd(), m_crtcId, c->handle(), s.width(), s.height()); | 126 | drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()); | ||
89 | } | 127 | } | ||
90 | 128 | | |||
91 | void DrmOutput::moveCursor(const QPoint &globalPos) | 129 | void DrmOutput::moveCursor(const QPoint &globalPos) | ||
92 | { | 130 | { | ||
93 | const QPoint p = globalPos - m_globalPos; | 131 | const QPoint p = globalPos - m_globalPos; | ||
94 | drmModeMoveCursor(m_backend->fd(), m_crtcId, p.x(), p.y()); | 132 | drmModeMoveCursor(m_backend->fd(), m_crtc->id(), p.x(), p.y()); | ||
95 | } | 133 | } | ||
96 | 134 | | |||
97 | QSize DrmOutput::size() const | 135 | QSize DrmOutput::size() const | ||
98 | { | 136 | { | ||
99 | return QSize(m_mode.hdisplay, m_mode.vdisplay); | 137 | return QSize(m_mode.hdisplay, m_mode.vdisplay); | ||
100 | } | 138 | } | ||
101 | 139 | | |||
102 | QRect DrmOutput::geometry() const | 140 | QRect DrmOutput::geometry() const | ||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Line(s) | 201 | { | |||
164 | initEdid(connector); | 202 | initEdid(connector); | ||
165 | initDpms(connector); | 203 | initDpms(connector); | ||
166 | initUuid(); | 204 | initUuid(); | ||
167 | if (m_backend->atomicModeSetting()) { | 205 | if (m_backend->atomicModeSetting()) { | ||
168 | if (!initPrimaryPlane()) { | 206 | if (!initPrimaryPlane()) { | ||
169 | return false; | 207 | return false; | ||
170 | } | 208 | } | ||
171 | } | 209 | } | ||
172 | m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtcId)); | 210 | m_savedCrtc.reset(drmModeGetCrtc(m_backend->fd(), m_crtc->id())); | ||
173 | if (!blank()) { | 211 | if (!blank()) { | ||
174 | return false; | 212 | return false; | ||
175 | } | 213 | } | ||
176 | setDpms(DpmsMode::On); | 214 | setDpms(DpmsMode::On); | ||
177 | if (!m_waylandOutput.isNull()) { | 215 | if (!m_waylandOutput.isNull()) { | ||
178 | delete m_waylandOutput.data(); | 216 | delete m_waylandOutput.data(); | ||
179 | m_waylandOutput.clear(); | 217 | m_waylandOutput.clear(); | ||
180 | } | 218 | } | ||
▲ Show 20 Lines • Show All 102 Lines • ▼ Show 20 Line(s) | |||||
283 | qCDebug(KWIN_DRM) << "Created OutputDevice"; | 321 | qCDebug(KWIN_DRM) << "Created OutputDevice"; | ||
284 | m_waylandOutputDevice->create(); | 322 | m_waylandOutputDevice->create(); | ||
285 | return true; | 323 | return true; | ||
286 | } | 324 | } | ||
287 | 325 | | |||
288 | void DrmOutput::initUuid() | 326 | void DrmOutput::initUuid() | ||
289 | { | 327 | { | ||
290 | QCryptographicHash hash(QCryptographicHash::Md5); | 328 | QCryptographicHash hash(QCryptographicHash::Md5); | ||
291 | hash.addData(QByteArray::number(m_connector)); | 329 | hash.addData(QByteArray::number(m_conn->id())); | ||
292 | hash.addData(m_edid.eisaId); | 330 | hash.addData(m_edid.eisaId); | ||
293 | hash.addData(m_edid.monitorName); | 331 | hash.addData(m_edid.monitorName); | ||
294 | hash.addData(m_edid.serialNumber); | 332 | hash.addData(m_edid.serialNumber); | ||
295 | m_uuid = hash.result().toHex().left(10); | 333 | m_uuid = hash.result().toHex().left(10); | ||
296 | } | 334 | } | ||
297 | 335 | | |||
298 | bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const | 336 | bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const | ||
299 | { | 337 | { | ||
▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Line(s) | 521 | if (p->type() != DrmPlane::TypeIndex::Primary) { | |||
484 | continue; | 522 | continue; | ||
485 | } | 523 | } | ||
486 | if (p->output()) { // Plane already has an output | 524 | if (p->output()) { // Plane already has an output | ||
487 | continue; | 525 | continue; | ||
488 | } | 526 | } | ||
489 | if (m_primaryPlane) { // Output already has a primary plane | 527 | if (m_primaryPlane) { // Output already has a primary plane | ||
490 | continue; | 528 | continue; | ||
491 | } | 529 | } | ||
492 | if (!p->isCrtcSupported(m_crtcId)) { | 530 | if (!p->isCrtcSupported(m_crtc->resIndex())) { | ||
493 | continue; | 531 | continue; | ||
494 | } | 532 | } | ||
495 | p->setOutput(this); | 533 | p->setOutput(this); | ||
496 | m_primaryPlane = p; | 534 | m_primaryPlane = p; | ||
497 | qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtcId; | 535 | qCDebug(KWIN_DRM) << "Initialized primary plane" << p->id() << "on CRTC" << m_crtc->id(); | ||
498 | return true; | 536 | return true; | ||
499 | } | 537 | } | ||
500 | qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; | 538 | qCCritical(KWIN_DRM) << "Failed to initialize primary plane."; | ||
501 | return false; | 539 | return false; | ||
502 | } | 540 | } | ||
503 | 541 | | |||
504 | bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) | 542 | bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first) | ||
505 | { | 543 | { | ||
506 | for (int i = 0; i < m_backend->planes().size(); ++i) { | 544 | for (int i = 0; i < m_backend->planes().size(); ++i) { | ||
507 | DrmPlane* p = m_backend->planes()[i]; | 545 | DrmPlane* p = m_backend->planes()[i]; | ||
508 | if (!p) { | 546 | if (!p) { | ||
509 | continue; | 547 | continue; | ||
510 | } | 548 | } | ||
511 | if (p->type() != DrmPlane::TypeIndex::Cursor) { | 549 | if (p->type() != DrmPlane::TypeIndex::Cursor) { | ||
512 | continue; | 550 | continue; | ||
513 | } | 551 | } | ||
514 | if (p->output()) { // Plane already has an output | 552 | if (p->output()) { // Plane already has an output | ||
515 | continue; | 553 | continue; | ||
516 | } | 554 | } | ||
517 | if (m_cursorPlane) { // Output already has a cursor plane | 555 | if (m_cursorPlane) { // Output already has a cursor plane | ||
518 | continue; | 556 | continue; | ||
519 | } | 557 | } | ||
520 | if (!p->isCrtcSupported(m_crtcId)) { | 558 | if (!p->isCrtcSupported(m_crtc->resIndex())) { | ||
521 | continue; | 559 | continue; | ||
522 | } | 560 | } | ||
523 | p->setOutput(this); | 561 | p->setOutput(this); | ||
524 | m_cursorPlane = p; | 562 | m_cursorPlane = p; | ||
525 | qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtcId; | 563 | qCDebug(KWIN_DRM) << "Initialized cursor plane" << p->id() << "on CRTC" << m_crtc->id(); | ||
526 | return true; | 564 | return true; | ||
527 | } | 565 | } | ||
528 | return false; | 566 | return false; | ||
529 | } | 567 | } | ||
530 | 568 | | |||
531 | void DrmOutput::initDpms(drmModeConnector *connector) | 569 | void DrmOutput::initDpms(drmModeConnector *connector) | ||
532 | { | 570 | { | ||
533 | for (int i = 0; i < connector->count_props; ++i) { | 571 | for (int i = 0; i < connector->count_props; ++i) { | ||
534 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | 572 | ScopedDrmPointer<_drmModeProperty, &drmModeFreeProperty> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | ||
535 | if (!property) { | 573 | if (!property) { | ||
536 | continue; | 574 | continue; | ||
537 | } | 575 | } | ||
538 | if (qstrcmp(property->name, "DPMS") == 0) { | 576 | if (qstrcmp(property->name, "DPMS") == 0) { | ||
539 | m_dpms.swap(property); | 577 | m_dpms.swap(property); | ||
540 | break; | 578 | break; | ||
541 | } | 579 | } | ||
542 | } | 580 | } | ||
543 | } | 581 | } | ||
544 | 582 | | |||
545 | void DrmOutput::setDpms(DrmOutput::DpmsMode mode) | 583 | void DrmOutput::setDpms(DrmOutput::DpmsMode mode) | ||
546 | { | 584 | { | ||
547 | if (m_dpms.isNull()) { | 585 | if (m_dpms.isNull()) { | ||
548 | return; | 586 | return; | ||
549 | } | 587 | } | ||
550 | if (mode == m_dpmsMode) { | 588 | if (mode == m_dpmsModePending) { | ||
551 | qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; | 589 | qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; | ||
552 | return; | 590 | return; | ||
553 | } | 591 | } | ||
554 | 592 | | |||
555 | if (m_backend->atomicModeSetting()) { | 593 | m_dpmsModePending = mode; | ||
556 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | | |||
557 | 594 | | |||
558 | if (atomicReqModesetPopulate(req, mode == DpmsMode::On) == DrmObject::AtomicReturn::Error) { | 595 | if (m_backend->atomicModeSetting()) { | ||
559 | qCWarning(KWIN_DRM) << "Failed to populate atomic request for output" << m_crtcId; | 596 | if (mode == DpmsMode::On) { | ||
560 | return; | 597 | if (m_pageFlipPending) { | ||
561 | } | 598 | m_pageFlipPending = false; | ||
562 | if (drmModeAtomicCommit(m_backend->fd(), req, DRM_MODE_ATOMIC_ALLOW_MODESET, this)) { | 599 | Compositor::self()->bufferSwapComplete(); | ||
563 | qCWarning(KWIN_DRM) << "Failed to commit atomic request for output" << m_crtcId; | 600 | } | ||
601 | dpmsOnHandler(); | ||||
564 | } else { | 602 | } else { | ||
565 | qCDebug(KWIN_DRM) << "DPMS set for output" << m_crtcId; | 603 | if (!m_pageFlipPending) { | ||
604 | dpmsOffAtomically(); | ||||
605 | } | ||||
566 | } | 606 | } | ||
567 | drmModeAtomicFree(req); | | |||
568 | } else { | 607 | } else { | ||
569 | if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) < 0) { | 608 | if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(mode)) < 0) { | ||
609 | m_dpmsModePending = m_dpmsMode; | ||||
570 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | 610 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | ||
571 | return; | 611 | return; | ||
572 | } | 612 | } | ||
613 | if (mode == DpmsMode::On) { | ||||
614 | dpmsOnHandler(); | ||||
615 | } else { | ||||
616 | dpmsOffHandler(); | ||||
617 | } | ||||
618 | m_dpmsMode = m_dpmsModePending; | ||||
573 | } | 619 | } | ||
620 | } | ||||
621 | | ||||
622 | void DrmOutput::dpmsOnHandler() | ||||
623 | { | ||||
624 | qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On."; | ||||
574 | 625 | | |||
575 | m_dpmsMode = mode; | | |||
576 | if (m_waylandOutput) { | 626 | if (m_waylandOutput) { | ||
577 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsMode)); | 627 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); | ||
578 | } | 628 | } | ||
579 | emit dpmsChanged(); | 629 | emit dpmsChanged(); | ||
580 | if (m_dpmsMode != DpmsMode::On) { | 630 | | ||
581 | m_backend->outputWentOff(); | 631 | m_backend->checkOutputsAreOn(); | ||
582 | } else { | 632 | blank(); | ||
583 | m_backend->checkOutputsAreOn(); | 633 | if (Compositor *compositor = Compositor::self()) { | ||
584 | blank(); | 634 | compositor->addRepaintFull(); | ||
585 | if (Compositor *compositor = Compositor::self()) { | 635 | } | ||
586 | compositor->addRepaintFull(); | 636 | } | ||
587 | } | 637 | | ||
638 | void DrmOutput::dpmsOffHandler() | ||||
639 | { | ||||
640 | qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off."; | ||||
641 | | ||||
642 | if (m_waylandOutput) { | ||||
643 | m_waylandOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); | ||||
588 | } | 644 | } | ||
645 | emit dpmsChanged(); | ||||
646 | | ||||
647 | m_backend->outputWentOff(); | ||||
589 | } | 648 | } | ||
590 | 649 | | |||
591 | QString DrmOutput::name() const | 650 | QString DrmOutput::name() const | ||
592 | { | 651 | { | ||
593 | if (!m_waylandOutput) { | 652 | if (!m_waylandOutput) { | ||
594 | return i18n("unknown"); | 653 | return i18n("unknown"); | ||
595 | } | 654 | } | ||
596 | return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); | 655 | return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); | ||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Line(s) | 716 | if (m_changeset->scaleChanged()) { | |||
659 | m_waylandOutputDevice->setScale(m_changeset->scale()); | 718 | m_waylandOutputDevice->setScale(m_changeset->scale()); | ||
660 | // FIXME: implement for wl_output | 719 | // FIXME: implement for wl_output | ||
661 | } | 720 | } | ||
662 | return true; | 721 | return true; | ||
663 | } | 722 | } | ||
664 | 723 | | |||
665 | void DrmOutput::pageFlipped() | 724 | void DrmOutput::pageFlipped() | ||
666 | { | 725 | { | ||
667 | if (m_backend->atomicModeSetting()){ | 726 | // Egl based surface buffers get destroyed, QPainter based dumb buffers not | ||
668 | foreach (DrmPlane *p, m_planesFlipList) { | 727 | // TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely? | ||
669 | pageFlippedBufferRemover(p->current(), p->next()); | 728 | if (m_backend->deleteBufferAfterPageFlip()) { | ||
670 | p->setCurrent(p->next()); | 729 | if (m_backend->atomicModeSetting()) { | ||
671 | p->setNext(nullptr); | 730 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||
672 | } | 731 | p->next()->retire(); | ||
673 | m_planesFlipList.clear(); | 732 | delete p->current(); | ||
674 | 733 | p->setCurrent(p->next()); | |||
675 | } else { | 734 | p->setNext(nullptr); | ||
676 | if (!m_nextBuffer) { | 735 | } | ||
677 | return; | 736 | m_nextPlanesFlipList.clear(); | ||
678 | } | | |||
679 | pageFlippedBufferRemover(m_currentBuffer, m_nextBuffer); | | |||
680 | m_currentBuffer = m_nextBuffer; | | |||
681 | m_nextBuffer = nullptr; | | |||
682 | } | | |||
683 | cleanupBlackBuffer(); | | |||
684 | } | | |||
685 | 737 | | |||
686 | void DrmOutput::pageFlippedBufferRemover(DrmBuffer *oldbuffer, DrmBuffer *newbuffer) | 738 | } else { | ||
687 | { | 739 | if (m_currentBuffer != m_nextBuffer) { | ||
688 | if (newbuffer->deleteAfterPageFlip()) { | 740 | delete m_currentBuffer; | ||
689 | if ( oldbuffer && oldbuffer != newbuffer ) { | 741 | } | ||
690 | delete oldbuffer; | 742 | if (m_nextBuffer) { | ||
743 | m_nextBuffer->retire(); | ||||
744 | } | ||||
745 | m_currentBuffer = m_nextBuffer; | ||||
746 | m_nextBuffer = nullptr; | ||||
747 | cleanupBlackBuffer(); | ||||
691 | } | 748 | } | ||
692 | } else { | 749 | } else { | ||
693 | // although oldbuffer's pointer is remapped in pageFlipped(), | 750 | if (m_backend->atomicModeSetting()){ | ||
694 | // we ignore the pointer completely anywhere else in this case | 751 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||
695 | newbuffer->releaseGbm(); | 752 | p->setCurrent(p->next()); | ||
753 | p->setNext(nullptr); | ||||
754 | } | ||||
755 | m_nextPlanesFlipList.clear(); | ||||
756 | } else { | ||||
757 | m_currentBuffer = m_nextBuffer; | ||||
758 | m_nextBuffer = nullptr; | ||||
759 | cleanupBlackBuffer(); | ||||
760 | } | ||||
696 | } | 761 | } | ||
762 | m_pageFlipPending = false; | ||||
697 | } | 763 | } | ||
698 | 764 | | |||
699 | void DrmOutput::cleanupBlackBuffer() | 765 | void DrmOutput::cleanupBlackBuffer() | ||
700 | { | 766 | { | ||
701 | if (m_blackBuffer) { | 767 | delete m_blackBuffer; | ||
702 | delete m_blackBuffer; | 768 | m_blackBuffer = nullptr; | ||
703 | m_blackBuffer = nullptr; | | |||
704 | } | | |||
705 | } | 769 | } | ||
706 | 770 | | |||
707 | bool DrmOutput::blank() | 771 | bool DrmOutput::blank() | ||
708 | { | 772 | { | ||
773 | if (m_backend->atomicModeSetting()) { | ||||
774 | // In Atomic Mode Setting we don't need any blanks. | ||||
775 | return true; | ||||
776 | } | ||||
709 | if (!m_blackBuffer) { | 777 | if (!m_blackBuffer) { | ||
710 | m_blackBuffer = m_backend->createBuffer(size()); | 778 | m_blackBuffer = m_backend->createBuffer(size()); | ||
711 | if (!m_blackBuffer->map()) { | 779 | if (!m_blackBuffer->map()) { | ||
712 | cleanupBlackBuffer(); | 780 | cleanupBlackBuffer(); | ||
713 | return false; | 781 | return false; | ||
714 | } | 782 | } | ||
715 | m_blackBuffer->image()->fill(Qt::black); | 783 | m_blackBuffer->image()->fill(Qt::black); | ||
716 | } | 784 | } | ||
717 | // TODO: Do this atomically | | |||
718 | return setModeLegacy(m_blackBuffer); | 785 | return setModeLegacy(m_blackBuffer); | ||
719 | } | 786 | } | ||
720 | 787 | | |||
788 | // TODO: We also need a function to deassign a plane. | ||||
789 | bool DrmOutput::assignClientPlane(KWayland::Server::BufferInterface *buffer) | ||||
790 | { | ||||
791 | if (m_pageFlipPending) { | ||||
792 | qCWarning(KWIN_DRM) << "Page not yet flipped."; | ||||
793 | return false; | ||||
794 | } | ||||
795 | | ||||
796 | for (DrmPlane *p : m_backend->overlayPlanes()) { | ||||
797 | if (p->next()) { | ||||
798 | // already assigned | ||||
799 | continue; | ||||
800 | } | ||||
801 | if (!p->isCrtcSupported(m_crtc->resIndex()) ) { | ||||
802 | continue; | ||||
803 | } | ||||
804 | | ||||
805 | // TODO: use init function to test if gbm_bo_import was successful, otherwise use render! | ||||
806 | DrmImportBuffer *b = new DrmImportBuffer(m_backend, buffer); | ||||
807 | p->setOutput(this); | ||||
808 | p->setNext(b); | ||||
809 | | ||||
810 | | ||||
811 | //TODO: Query position values for overlays. | ||||
812 | int x = 0; | ||||
813 | int y = 0; | ||||
814 | int width = b->size().width(); | ||||
815 | int height = b->size().height(); | ||||
816 | | ||||
817 | p->setValue(int(DrmPlane::PropertyIndex::SrcX), x << 16); | ||||
818 | p->setValue(int(DrmPlane::PropertyIndex::SrcY), y << 16); | ||||
819 | p->setValue(int(DrmPlane::PropertyIndex::SrcW), width << 16); | ||||
820 | p->setValue(int(DrmPlane::PropertyIndex::SrcH), height << 16); | ||||
821 | p->setValue(int(DrmPlane::PropertyIndex::CrtcX), x); | ||||
822 | p->setValue(int(DrmPlane::PropertyIndex::CrtcY), y); | ||||
823 | p->setValue(int(DrmPlane::PropertyIndex::CrtcW), width); | ||||
824 | p->setValue(int(DrmPlane::PropertyIndex::CrtcH), height); | ||||
825 | p->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); | ||||
826 | | ||||
827 | m_nextPlanesFlipList << p; | ||||
828 | return true; | ||||
829 | } | ||||
830 | return false; | ||||
831 | } | ||||
832 | | ||||
721 | bool DrmOutput::present(DrmBuffer *buffer) | 833 | bool DrmOutput::present(DrmBuffer *buffer) | ||
722 | { | 834 | { | ||
723 | if (!buffer || buffer->bufferId() == 0) { | 835 | if (!buffer || buffer->bufferId() == 0) { | ||
724 | return false; | 836 | return false; | ||
725 | } | 837 | } | ||
726 | if (m_backend->atomicModeSetting()) { | 838 | if (m_backend->atomicModeSetting()) { | ||
727 | return presentAtomically(buffer); | 839 | return presentAtomically(buffer); | ||
728 | } else { | 840 | } else { | ||
729 | return presentLegacy(buffer); | 841 | return presentLegacy(buffer); | ||
730 | } | 842 | } | ||
731 | } | 843 | } | ||
732 | 844 | | |||
733 | bool DrmOutput::presentAtomically(DrmBuffer *buffer) | 845 | bool DrmOutput::dpmsOffAtomically() | ||
734 | { | 846 | { | ||
735 | if (!LogindIntegration::self()->isActiveSession()) { | 847 | // TODO: With multiple planes: deactivate all of them here | ||
736 | qCWarning(KWIN_DRM) << "Logind session not active."; | 848 | m_primaryPlane->setNext(nullptr); | ||
849 | m_nextPlanesFlipList << m_primaryPlane; | ||||
850 | | ||||
851 | if (!doAtomicCommit(AtomicCommitMode::Test)) { | ||||
852 | qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting."; | ||||
737 | return false; | 853 | return false; | ||
738 | } | 854 | } | ||
739 | if (m_dpmsMode != DpmsMode::On) { | 855 | if (!doAtomicCommit(AtomicCommitMode::Real)) { | ||
740 | qCWarning(KWIN_DRM) << "No present() while screen off."; | 856 | qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting."; | ||
741 | return false; | 857 | return false; | ||
742 | } | 858 | } | ||
743 | if (m_primaryPlane->next()) { | 859 | m_nextPlanesFlipList.clear(); | ||
744 | qCWarning(KWIN_DRM) << "Page not yet flipped."; | 860 | dpmsOffHandler(); | ||
745 | return false; | | |||
746 | } | | |||
747 | | ||||
748 | DrmObject::AtomicReturn ret; | | |||
749 | uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; | | |||
750 | 861 | | |||
751 | // TODO: throwing an exception would be really handy here! (would mean change of compile options) | 862 | return true; | ||
752 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | | |||
753 | if (!req) { | | |||
754 | qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; | | |||
755 | delete buffer; | | |||
756 | return false; | | |||
757 | } | | |||
758 | | ||||
759 | // Do we need to set a new mode first? | | |||
760 | bool doModeset = !m_primaryPlane->current(); | | |||
761 | if (doModeset) { | | |||
762 | qCDebug(KWIN_DRM) << "Atomic Modeset requested"; | | |||
763 | 863 | | |||
764 | if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId)) { | 864 | } | ||
765 | qCWarning(KWIN_DRM) << "Failed to create property blob"; | | |||
766 | delete buffer; | | |||
767 | return false; | | |||
768 | } | | |||
769 | 865 | | |||
770 | ret = atomicReqModesetPopulate(req, true); | 866 | bool DrmOutput::presentAtomically(DrmBuffer *buffer) | ||
771 | if (ret == DrmObject::AtomicReturn::Error){ | 867 | { | ||
772 | drmModeAtomicFree(req); | 868 | if (!LogindIntegration::self()->isActiveSession()) { | ||
773 | delete buffer; | 869 | qCWarning(KWIN_DRM) << "Logind session not active."; | ||
774 | return false; | 870 | return false; | ||
775 | } | | |||
776 | if (ret == DrmObject::AtomicReturn::Success) { | | |||
777 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | | |||
778 | } | | |||
779 | } | 871 | } | ||
780 | 872 | | |||
781 | m_primaryPlane->setNext(buffer); // TODO: Later not only use the primary plane for the buffer! | 873 | if (m_pageFlipPending) { | ||
782 | // i.e.: Assign planes | 874 | qCWarning(KWIN_DRM) << "Page not yet flipped."; | ||
783 | bool anyDamage = false; | 875 | return false; | ||
784 | foreach (DrmPlane* p, m_backend->planes()){ | | |||
785 | if (p->output() != this) { | | |||
786 | continue; | | |||
787 | } | | |||
788 | ret = p->atomicReqPlanePopulate(req); | | |||
789 | if (ret == DrmObject::AtomicReturn::Error) { | | |||
790 | drmModeAtomicFree(req); | | |||
791 | m_primaryPlane->setNext(nullptr); | | |||
792 | m_planesFlipList.clear(); | | |||
793 | delete buffer; | | |||
794 | return false; | | |||
795 | } | | |||
796 | if (ret == DrmObject::AtomicReturn::Success) { | | |||
797 | anyDamage = true; | | |||
798 | m_planesFlipList << p; | | |||
799 | } | | |||
800 | } | 876 | } | ||
801 | 877 | | |||
802 | // no damage but force flip for atleast the primary plane anyway | 878 | m_primaryPlane->setNext(buffer); | ||
803 | if (!anyDamage) { | 879 | m_nextPlanesFlipList << m_primaryPlane; | ||
804 | m_primaryPlane->setPropsValid(0); | 880 | | ||
805 | if (m_primaryPlane->atomicReqPlanePopulate(req) == DrmObject::AtomicReturn::Error) { | 881 | if (!doAtomicCommit(AtomicCommitMode::Test)) { | ||
806 | drmModeAtomicFree(req); | 882 | //TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout? | ||
807 | m_primaryPlane->setNext(nullptr); | 883 | qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present."; | ||
808 | m_planesFlipList.clear(); | 884 | if (this->m_backend->deleteBufferAfterPageFlip()) { | ||
809 | delete buffer; | 885 | delete buffer; | ||
810 | return false; | | |||
811 | } | 886 | } | ||
812 | m_planesFlipList << m_primaryPlane; | | |||
813 | } | | |||
814 | | ||||
815 | if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { | | |||
816 | qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); | | |||
817 | drmModeAtomicFree(req); | | |||
818 | m_primaryPlane->setNext(nullptr); | | |||
819 | m_planesFlipList.clear(); | | |||
820 | delete buffer; | | |||
821 | return false; | 887 | return false; | ||
822 | } | 888 | } | ||
823 | 889 | if (!doAtomicCommit(AtomicCommitMode::Real)) { | |||
824 | if (doModeset) { | 890 | qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present."; | ||
825 | m_crtc->setPropsValid(m_crtc->propsValid() | m_crtc->propsPending()); | 891 | return false; | ||
826 | m_conn->setPropsValid(m_conn->propsValid() | m_conn->propsPending()); | | |||
827 | } | | |||
828 | foreach (DrmPlane* p, m_planesFlipList) { | | |||
829 | p->setPropsValid(p->propsValid() | p->propsPending()); | | |||
830 | } | 892 | } | ||
831 | 893 | m_pageFlipPending = true; | |||
832 | drmModeAtomicFree(req); | | |||
833 | return true; | 894 | return true; | ||
834 | } | 895 | } | ||
835 | 896 | | |||
836 | | ||||
837 | bool DrmOutput::presentLegacy(DrmBuffer *buffer) | 897 | bool DrmOutput::presentLegacy(DrmBuffer *buffer) | ||
838 | { | 898 | { | ||
839 | if (m_nextBuffer) { | 899 | if (m_nextBuffer) { | ||
840 | return false; | 900 | return false; | ||
841 | } | 901 | } | ||
842 | if (!LogindIntegration::self()->isActiveSession()) { | 902 | if (!LogindIntegration::self()->isActiveSession()) { | ||
843 | m_nextBuffer = buffer; | 903 | m_nextBuffer = buffer; | ||
844 | return false; | 904 | return false; | ||
845 | } | 905 | } | ||
846 | if (m_dpmsMode != DpmsMode::On) { | 906 | if (m_dpmsMode != DpmsMode::On) { | ||
847 | return false; | 907 | return false; | ||
848 | } | 908 | } | ||
849 | 909 | | |||
850 | // Do we need to set a new mode first? | 910 | // Do we need to set a new mode first? | ||
851 | if (m_lastStride != buffer->stride() || m_lastGbm != buffer->isGbm()){ | 911 | if (!m_currentBuffer || m_currentBuffer->needsModeChange(buffer)) { | ||
852 | if (!setModeLegacy(buffer)) | 912 | if (!setModeLegacy(buffer)) { | ||
853 | return false; | 913 | return false; | ||
914 | } | ||||
854 | } | 915 | } | ||
855 | int errno_save = 0; | 916 | int errno_save = 0; | ||
856 | const bool ok = drmModePageFlip(m_backend->fd(), m_crtcId, buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0; | 917 | if (drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0) { | ||
857 | if (ok) { | | |||
858 | m_nextBuffer = buffer; | 918 | m_nextBuffer = buffer; | ||
919 | return true; | ||||
859 | } else { | 920 | } else { | ||
860 | errno_save = errno; | 921 | errno_save = errno; | ||
861 | qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); | 922 | qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno); | ||
862 | delete buffer; | 923 | if (m_backend->deleteBufferAfterPageFlip()) { | ||
924 | delete buffer; | ||||
925 | } | ||||
926 | return false; | ||||
863 | } | 927 | } | ||
864 | return ok; | | |||
865 | } | 928 | } | ||
866 | 929 | | |||
867 | bool DrmOutput::setModeLegacy(DrmBuffer *buffer) | 930 | bool DrmOutput::setModeLegacy(DrmBuffer *buffer) | ||
868 | { | 931 | { | ||
869 | if (drmModeSetCrtc(m_backend->fd(), m_crtcId, buffer->bufferId(), 0, 0, &m_connector, 1, &m_mode) == 0) { | 932 | uint32_t connId = m_conn->id(); | ||
870 | m_lastStride = buffer->stride(); | 933 | if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { | ||
871 | m_lastGbm = buffer->isGbm(); | | |||
872 | return true; | 934 | return true; | ||
873 | } else { | 935 | } else { | ||
874 | qCWarning(KWIN_DRM) << "Mode setting failed"; | 936 | qCWarning(KWIN_DRM) << "Mode setting failed"; | ||
875 | return false; | 937 | return false; | ||
876 | } | 938 | } | ||
877 | } | 939 | } | ||
878 | 940 | | |||
879 | DrmObject::AtomicReturn DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) | 941 | bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) | ||
880 | { | 942 | { | ||
881 | if (enable) { | 943 | drmModeAtomicReq *req = drmModeAtomicAlloc(); | ||
882 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); | 944 | | ||
883 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); | 945 | auto errorHandler = [this, mode, req] () { | ||
884 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); | 946 | if (mode == AtomicCommitMode::Test) { | ||
885 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); | 947 | // TODO: when we later test overlay planes, make sure we change only the right stuff back | ||
948 | } | ||||
949 | drmModeAtomicFree(req); | ||||
950 | | ||||
951 | if (m_dpmsMode != m_dpmsModePending) { | ||||
952 | qCWarning(KWIN_DRM) << "Setting DPMS failed"; | ||||
953 | m_dpmsModePending = m_dpmsMode; | ||||
954 | if (m_dpmsMode != DpmsMode::On) { | ||||
955 | dpmsOffHandler(); | ||||
956 | } | ||||
957 | } | ||||
958 | | ||||
959 | // TODO: see above, rework later for overlay planes! | ||||
960 | for (DrmPlane *p : m_nextPlanesFlipList) { | ||||
961 | p->setNext(nullptr); | ||||
962 | } | ||||
963 | m_nextPlanesFlipList.clear(); | ||||
964 | | ||||
965 | }; | ||||
966 | | ||||
967 | if (!req) { | ||||
968 | qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request"; | ||||
969 | errorHandler(); | ||||
970 | return false; | ||||
971 | } | ||||
972 | | ||||
973 | uint32_t flags = 0; | ||||
974 | | ||||
975 | // Do we need to set a new mode? | ||||
976 | if (!m_primaryPlane->current() || m_dpmsModePending != m_dpmsMode) { | ||||
977 | if (m_dpmsModePending == DpmsMode::On) { | ||||
978 | if (drmModeCreatePropertyBlob(m_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) { | ||||
979 | qCWarning(KWIN_DRM) << "Failed to create property blob"; | ||||
980 | errorHandler(); | ||||
981 | return false; | ||||
982 | } | ||||
983 | } | ||||
984 | if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){ | ||||
985 | qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset"; | ||||
986 | errorHandler(); | ||||
987 | return false; | ||||
988 | } | ||||
989 | flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; | ||||
990 | } | ||||
991 | | ||||
992 | if (mode == AtomicCommitMode::Real) { | ||||
993 | if (m_dpmsModePending == DpmsMode::On) { | ||||
994 | if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { | ||||
995 | // TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10. | ||||
996 | flags |= DRM_MODE_ATOMIC_NONBLOCK; | ||||
997 | } | ||||
998 | flags |= DRM_MODE_PAGE_FLIP_EVENT; | ||||
999 | } | ||||
886 | } else { | 1000 | } else { | ||
887 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcW), 0); | 1001 | flags |= DRM_MODE_ATOMIC_TEST_ONLY; | ||
888 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::SrcH), 0); | | |||
889 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcW), 0); | | |||
890 | m_primaryPlane->setPropValue(int(DrmPlane::PropertyIndex::CrtcH), 0); | | |||
891 | } | 1002 | } | ||
892 | 1003 | | |||
893 | bool ret = true; | 1004 | bool ret = true; | ||
1005 | // TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order. | ||||
1006 | for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) { | ||||
1007 | DrmPlane *p = m_nextPlanesFlipList[i]; | ||||
1008 | ret &= p->atomicPopulate(req); | ||||
1009 | } | ||||
894 | 1010 | | |||
895 | m_crtc->setPropsPending(0); | 1011 | if (!ret) { | ||
896 | m_conn->setPropsPending(0); | 1012 | qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!"; | ||
1013 | errorHandler(); | ||||
1014 | return false; | ||||
1015 | } | ||||
897 | 1016 | | |||
898 | ret &= m_conn->atomicAddProperty(req, int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); | 1017 | if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) { | ||
899 | ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); | 1018 | qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno); | ||
900 | ret &= m_crtc->atomicAddProperty(req, int(DrmCrtc::PropertyIndex::Active), enable); | 1019 | drmModeAtomicFree(req); | ||
1020 | errorHandler(); | ||||
1021 | return false; | ||||
1022 | } | ||||
901 | 1023 | | |||
902 | if (!ret) { | 1024 | if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) { | ||
903 | qCWarning(KWIN_DRM) << "Failed to populate atomic modeset"; | 1025 | qCDebug(KWIN_DRM) << "Atomic Modeset successful."; | ||
904 | return DrmObject::AtomicReturn::Error; | 1026 | m_dpmsMode = m_dpmsModePending; | ||
905 | } | 1027 | } | ||
906 | if (!m_crtc->propsPending() && !m_conn->propsPending()) { | 1028 | | ||
907 | return DrmObject::AtomicReturn::NoChange; | 1029 | drmModeAtomicFree(req); | ||
1030 | return true; | ||||
1031 | } | ||||
1032 | | ||||
1033 | bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable) | ||||
1034 | { | ||||
1035 | if (enable) { | ||||
1036 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), m_mode.hdisplay << 16); | ||||
1037 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), m_mode.vdisplay << 16); | ||||
1038 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), m_mode.hdisplay); | ||||
1039 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), m_mode.vdisplay); | ||||
1040 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), m_crtc->id()); | ||||
1041 | } else { | ||||
1042 | delete m_primaryPlane->current(); | ||||
1043 | delete m_primaryPlane->next(); | ||||
1044 | m_primaryPlane->setCurrent(nullptr); | ||||
1045 | m_primaryPlane->setNext(nullptr); | ||||
1046 | | ||||
1047 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcW), 0); | ||||
1048 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::SrcH), 0); | ||||
1049 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcW), 0); | ||||
1050 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcH), 0); | ||||
1051 | m_primaryPlane->setValue(int(DrmPlane::PropertyIndex::CrtcId), 0); | ||||
908 | } | 1052 | } | ||
909 | return DrmObject::AtomicReturn::Success; | 1053 | m_conn->setValue(int(DrmConnector::PropertyIndex::CrtcId), enable ? m_crtc->id() : 0); | ||
1054 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::ModeId), enable ? m_blobId : 0); | ||||
1055 | m_crtc->setValue(int(DrmCrtc::PropertyIndex::Active), enable); | ||||
1056 | | ||||
1057 | bool ret = true; | ||||
1058 | ret &= m_conn->atomicPopulate(req); | ||||
1059 | ret &= m_crtc->atomicPopulate(req); | ||||
1060 | | ||||
1061 | return ret; | ||||
910 | } | 1062 | } | ||
911 | 1063 | | |||
912 | } | 1064 | } | ||
Context not available. |