From c89961a4e2511ac2c7c9128f810fd2d5dca1c422 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 25 May 2021 23:08:31 +0200 Subject: [PATCH] platforms/drm: save modelist in DrmConnector --- src/plugins/platforms/drm/drm_gpu.cpp | 3 +- .../platforms/drm/drm_object_connector.cpp | 103 ++++++++++++++- .../platforms/drm/drm_object_connector.h | 27 +++- src/plugins/platforms/drm/drm_object_crtc.cpp | 6 + src/plugins/platforms/drm/drm_object_crtc.h | 2 + src/plugins/platforms/drm/drm_output.cpp | 125 ++++-------------- src/plugins/platforms/drm/drm_output.h | 7 +- 7 files changed, 163 insertions(+), 110 deletions(-) diff --git a/src/plugins/platforms/drm/drm_gpu.cpp b/src/plugins/platforms/drm/drm_gpu.cpp index e4b23cfad5..e080ad8653 100644 --- a/src/plugins/platforms/drm/drm_gpu.cpp +++ b/src/plugins/platforms/drm/drm_gpu.cpp @@ -275,9 +275,8 @@ bool DrmGpu::updateOutputs() output->m_conn = con; output->m_crtc = crtc; output->m_primaryPlane = primary; - output->m_mode = connector->modes[0]; - if (!output->init(connector.data())) { + if (!output->init()) { qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id(); delete output; continue; diff --git a/src/plugins/platforms/drm/drm_object_connector.cpp b/src/plugins/platforms/drm/drm_object_connector.cpp index cb061ed6a3..1a66cfdc0e 100644 --- a/src/plugins/platforms/drm/drm_object_connector.cpp +++ b/src/plugins/platforms/drm/drm_object_connector.cpp @@ -3,6 +3,7 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2016 Roman Gilg + SPDX-FileCopyrightText: 2021 Xaver Hugl SPDX-License-Identifier: GPL-2.0-or-later */ @@ -35,6 +36,25 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId) DrmConnector::~DrmConnector() = default; +namespace { +quint64 refreshRateForMode(_drmModeModeInfo *m) +{ + // Calculate higher precision (mHz) refresh rate + // logic based on Weston, see compositor-drm.c + quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal; + if (m->flags & DRM_MODE_FLAG_INTERLACE) { + refreshRate *= 2; + } + if (m->flags & DRM_MODE_FLAG_DBLSCAN) { + refreshRate /= 2; + } + if (m->vscan > 1) { + refreshRate /= m->vscan; + } + return refreshRate; +} +} + bool DrmConnector::init() { if (!m_conn || !m_conn->count_modes) { @@ -108,16 +128,26 @@ bool DrmConnector::init() m_physicalSize = overwriteSize; } + // init modes + for (int i = 0; i < m_conn->count_modes; i++) { + auto mode = m_conn->modes[i]; + Mode m; + m.mode = mode; + m.size = QSize(mode.hdisplay, mode.vdisplay); + m.refreshRate = refreshRateForMode(&mode); + m_modes << m; + } + return true; } bool DrmConnector::isConnected() { - DrmScopedPointer con(drmModeGetConnector(gpu()->fd(), id())); - if (!con) { + m_conn.reset(drmModeGetConnector(gpu()->fd(), id())); + if (!m_conn) { return false; } - return con->connection == DRM_MODE_CONNECTED; + return m_conn->connection == DRM_MODE_CONNECTED; } static QHash s_connectorNames = { @@ -168,6 +198,73 @@ QSize DrmConnector::physicalSize() const return m_physicalSize; } +const DrmConnector::Mode &DrmConnector::currentMode() const +{ + return m_modes[m_modeIndex]; +} + +int DrmConnector::currentModeIndex() const +{ + return m_modeIndex; +} + +const QVector &DrmConnector::modes() +{ + return m_modes; +} + +void DrmConnector::setModeIndex(int index) +{ + m_modeIndex = index; +} + +static bool checkIfEqual(drmModeModeInfo one, drmModeModeInfo two) +{ + return one.clock == two.clock + && one.hdisplay == two.hdisplay + && one.hsync_start == two.hsync_start + && one.hsync_end == two.hsync_end + && one.htotal == two.htotal + && one.hskew == two.hskew + && one.vdisplay == two.vdisplay + && one.vsync_start == two.vsync_start + && one.vsync_end == two.vsync_end + && one.vtotal == two.vtotal + && one.vscan == two.vscan + && one.vrefresh == two.vrefresh; +} + +void DrmConnector::findCurrentMode(drmModeModeInfo currentMode) +{ + for (int i = 0; i < m_modes.count(); i++) { + if (checkIfEqual(m_modes[i].mode, currentMode)) { + m_modeIndex = i; + return; + } + } + m_modeIndex = 0; +} + +AbstractWaylandOutput::SubPixel DrmConnector::subpixel() const +{ + switch (m_conn->subpixel) { + case DRM_MODE_SUBPIXEL_UNKNOWN: + return AbstractWaylandOutput::SubPixel::Unknown; + case DRM_MODE_SUBPIXEL_NONE: + return AbstractWaylandOutput::SubPixel::None; + case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: + return AbstractWaylandOutput::SubPixel::Horizontal_RGB; + case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: + return AbstractWaylandOutput::SubPixel::Horizontal_BGR; + case DRM_MODE_SUBPIXEL_VERTICAL_RGB: + return AbstractWaylandOutput::SubPixel::Vertical_RGB; + case DRM_MODE_SUBPIXEL_VERTICAL_BGR: + return AbstractWaylandOutput::SubPixel::Vertical_BGR; + default: + Q_UNREACHABLE(); + } +} + bool DrmConnector::hasOverscan() const { return m_props[static_cast(PropertyIndex::Overscan)] || m_props[static_cast(PropertyIndex::Underscan)]; diff --git a/src/plugins/platforms/drm/drm_object_connector.h b/src/plugins/platforms/drm/drm_object_connector.h index 34dedafd7c..89ae8ee1fb 100644 --- a/src/plugins/platforms/drm/drm_object_connector.h +++ b/src/plugins/platforms/drm/drm_object_connector.h @@ -3,16 +3,21 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2016 Roman Gilg + SPDX-FileCopyrightText: 2021 Xaver Hugl SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef KWIN_DRM_OBJECT_CONNECTOR_H -#define KWIN_DRM_OBJECT_CONNECTOR_H +#pragma once + +#include +#include #include #include "drm_object.h" #include "edid.h" +#include "drm_pointer.h" +#include "abstract_wayland_output.h" namespace KWin { @@ -72,6 +77,19 @@ public: bool isInternal() const; QSize physicalSize() const; + struct Mode { + drmModeModeInfo mode; + QSize size; + uint32_t refreshRate; + }; + const Mode ¤tMode() const; + int currentModeIndex() const; + const QVector &modes(); + void setModeIndex(int index); + void findCurrentMode(drmModeModeInfo currentMode); + + AbstractWaylandOutput::SubPixel subpixel() const; + bool hasOverscan() const; uint32_t overscan() const; void setOverscan(uint32_t overscan, const QSize &modeSize); @@ -83,10 +101,9 @@ private: QVector m_encoders; Edid m_edid; QSize m_physicalSize = QSize(-1, -1); + QVector m_modes; + int m_modeIndex = 0; }; } - -#endif - diff --git a/src/plugins/platforms/drm/drm_object_crtc.cpp b/src/plugins/platforms/drm/drm_object_crtc.cpp index 0fe81131f2..cc5686ac43 100644 --- a/src/plugins/platforms/drm/drm_object_crtc.cpp +++ b/src/plugins/platforms/drm/drm_object_crtc.cpp @@ -108,4 +108,10 @@ bool DrmCrtc::isVrrEnabled() const return false; } +drmModeModeInfo DrmCrtc::queryCurrentMode() +{ + m_crtc.reset(drmModeGetCrtc(gpu()->fd(), id())); + return m_crtc->mode; +} + } diff --git a/src/plugins/platforms/drm/drm_object_crtc.h b/src/plugins/platforms/drm/drm_object_crtc.h index ed9dd93e22..b77ca8ff78 100644 --- a/src/plugins/platforms/drm/drm_object_crtc.h +++ b/src/plugins/platforms/drm/drm_object_crtc.h @@ -64,6 +64,8 @@ public: bool setVrr(bool enable); bool isVrrEnabled() const; + drmModeModeInfo queryCurrentMode(); + private: DrmScopedPointer m_crtc; QSharedPointer m_currentBuffer; diff --git a/src/plugins/platforms/drm/drm_output.cpp b/src/plugins/platforms/drm/drm_output.cpp index 53b53924a8..78d1f9dc00 100644 --- a/src/plugins/platforms/drm/drm_output.cpp +++ b/src/plugins/platforms/drm/drm_output.cpp @@ -193,52 +193,13 @@ void DrmOutput::moveCursor() } } -namespace { -quint64 refreshRateForMode(_drmModeModeInfo *m) -{ - // Calculate higher precision (mHz) refresh rate - // logic based on Weston, see compositor-drm.c - quint64 refreshRate = (m->clock * 1000000LL / m->htotal + m->vtotal / 2) / m->vtotal; - if (m->flags & DRM_MODE_FLAG_INTERLACE) { - refreshRate *= 2; - } - if (m->flags & DRM_MODE_FLAG_DBLSCAN) { - refreshRate /= 2; - } - if (m->vscan > 1) { - refreshRate /= m->vscan; - } - return refreshRate; -} -} - -static AbstractWaylandOutput::SubPixel drmSubPixelToKWinSubPixel(drmModeSubPixel subpixel) -{ - switch (subpixel) { - case DRM_MODE_SUBPIXEL_UNKNOWN: - return AbstractWaylandOutput::SubPixel::Unknown; - case DRM_MODE_SUBPIXEL_NONE: - return AbstractWaylandOutput::SubPixel::None; - case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: - return AbstractWaylandOutput::SubPixel::Horizontal_RGB; - case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: - return AbstractWaylandOutput::SubPixel::Horizontal_BGR; - case DRM_MODE_SUBPIXEL_VERTICAL_RGB: - return AbstractWaylandOutput::SubPixel::Vertical_RGB; - case DRM_MODE_SUBPIXEL_VERTICAL_BGR: - return AbstractWaylandOutput::SubPixel::Vertical_BGR; - default: - Q_UNREACHABLE(); - } -} - -bool DrmOutput::init(drmModeConnector *connector) +bool DrmOutput::init() { if (m_gpu->atomicModeSetting() && !m_primaryPlane) { return false; } - setSubPixelInternal(drmSubPixelToKWinSubPixel(connector->subpixel)); + setSubPixelInternal(m_conn->subpixel()); setInternal(m_conn->isInternal()); setCapabilityInternal(DrmOutput::Capability::Dpms); if (m_conn->hasOverscan()) { @@ -249,7 +210,7 @@ bool DrmOutput::init(drmModeConnector *connector) setCapabilityInternal(Capability::Vrr); setVrrPolicy(RenderLoop::VrrPolicy::Automatic); } - initOutputDevice(connector); + initOutputDevice(); if (!m_gpu->atomicModeSetting() && !m_crtc->blank(this)) { // We use legacy mode and the initial output blank failed. @@ -260,27 +221,25 @@ bool DrmOutput::init(drmModeConnector *connector) return true; } -void DrmOutput::initOutputDevice(drmModeConnector *connector) +void DrmOutput::initOutputDevice() { - // read in mode information - QVector modes; - modes.reserve(connector->count_modes); - for (int i = 0; i < connector->count_modes; ++i) { - // TODO: in AMS here we could read and store for later every mode's blob_id - // would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set - auto *m = &connector->modes[i]; + m_conn->findCurrentMode(m_crtc->queryCurrentMode()); + auto modelist = m_conn->modes(); + QVector modes; + modes.reserve(modelist.count()); + for (int i = 0; i < modelist.count(); ++i) { Mode mode; - if (isCurrentMode(m)) { + if (i == m_conn->currentModeIndex()) { mode.flags |= ModeFlag::Current; } - if (m->type & DRM_MODE_TYPE_PREFERRED) { + if (modelist[i].mode.type & DRM_MODE_TYPE_PREFERRED) { mode.flags |= ModeFlag::Preferred; } mode.id = i; - mode.size = QSize(m->hdisplay, m->vdisplay); - mode.refreshRate = refreshRateForMode(m); + mode.size = modelist[i].size; + mode.refreshRate = modelist[i].refreshRate; modes << mode; } @@ -290,25 +249,6 @@ void DrmOutput::initOutputDevice(drmModeConnector *connector) m_conn->physicalSize(), modes, m_conn->edid()->raw()); } -bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const -{ - return mode->clock == m_mode.clock - && mode->hdisplay == m_mode.hdisplay - && mode->hsync_start == m_mode.hsync_start - && mode->hsync_end == m_mode.hsync_end - && mode->htotal == m_mode.htotal - && mode->hskew == m_mode.hskew - && mode->vdisplay == m_mode.vdisplay - && mode->vsync_start == m_mode.vsync_start - && mode->vsync_end == m_mode.vsync_end - && mode->vtotal == m_mode.vtotal - && mode->vscan == m_mode.vscan - && mode->vrefresh == m_mode.vrefresh - && mode->flags == m_mode.flags - && mode->type == m_mode.type - && qstrcmp(mode->name, m_mode.name) == 0; -} - bool DrmOutput::initCursor(const QSize &cursorSize) { auto createCursor = [this, cursorSize] (int index) { @@ -548,14 +488,12 @@ void DrmOutput::updateTransform(Transform transform) void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate) { - if (m_mode.hdisplay == width && m_mode.vdisplay == height && refreshRateForMode(&m_mode) == refreshRate) { + if (m_conn->currentMode().size == QSize(width, height) && m_conn->currentMode().refreshRate == refreshRate) { return; } - // try to find a fitting mode - DrmScopedPointer connector(drmModeGetConnectorCurrent(m_gpu->fd(), m_conn->id())); - for (int i = 0; i < connector->count_modes; i++) { - auto mode = connector->modes[i]; - if (mode.hdisplay == width && mode.vdisplay == height && refreshRateForMode(&mode) == refreshRate) { + auto modelist = m_conn->modes(); + for (int i = 0; i < modelist.size(); i++) { + if (modelist[i].size == QSize(width, height) && modelist[i].refreshRate == refreshRate) { updateMode(i); return; } @@ -566,17 +504,10 @@ void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate void DrmOutput::updateMode(int modeIndex) { - // get all modes on the connector - DrmScopedPointer connector(drmModeGetConnector(m_gpu->fd(), m_conn->id())); - if (connector->count_modes <= modeIndex) { - // TODO: error? + if (m_conn->currentModeIndex() == modeIndex) { return; } - if (isCurrentMode(&connector->modes[modeIndex])) { - // nothing to do - return; - } - m_mode = connector->modes[modeIndex]; + m_conn->setModeIndex(modeIndex); m_modesetRequested = true; // aspect ratio might need to be adjusted m_conn->setOverscan(m_conn->overscan(), modeSize()); @@ -585,8 +516,8 @@ void DrmOutput::updateMode(int modeIndex) void DrmOutput::setCurrentModeInternal() { - AbstractWaylandOutput::setCurrentModeInternal(QSize(m_mode.hdisplay, m_mode.vdisplay), - refreshRateForMode(&m_mode)); + auto mode = m_conn->currentMode(); + AbstractWaylandOutput::setCurrentModeInternal(mode.size, mode.refreshRate); } void DrmOutput::pageFlipped() @@ -686,7 +617,7 @@ bool DrmOutput::presentAtomically(const QSharedPointer &buffer) qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present."; // go back to previous state if (m_lastWorkingState.valid) { - m_mode = m_lastWorkingState.mode; + m_conn->setModeIndex(m_lastWorkingState.modeIndex); setTransformInternal(m_lastWorkingState.transform); setGlobalPos(m_lastWorkingState.globalPos); if (m_primaryPlane) { @@ -713,14 +644,14 @@ bool DrmOutput::presentAtomically(const QSharedPointer &buffer) } if (wasModeset) { // store current mode set as new good state - m_lastWorkingState.mode = m_mode; + m_lastWorkingState.modeIndex = m_conn->currentModeIndex(); m_lastWorkingState.transform = transform(); m_lastWorkingState.globalPos = globalPos(); if (m_primaryPlane) { m_lastWorkingState.planeTransformations = m_primaryPlane->transformation(); } m_lastWorkingState.valid = true; - m_renderLoop->setRefreshRate(refreshRateForMode(&m_mode)); + m_renderLoop->setRefreshRate(m_conn->currentMode().refreshRate); } m_pageFlipPending = true; return true; @@ -755,7 +686,8 @@ bool DrmOutput::presentLegacy(const QSharedPointer &buffer) bool DrmOutput::setModeLegacy(DrmBuffer *buffer) { uint32_t connId = m_conn->id(); - if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) { + auto mode = m_conn->currentMode().mode; + if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &mode) == 0) { return true; } else { qCWarning(KWIN_DRM) << "Mode setting failed"; @@ -801,7 +733,8 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode) // Do we need to set a new mode? if (m_modesetRequested) { if (m_dpmsModePending == DpmsMode::On) { - if (drmModeCreatePropertyBlob(m_gpu->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) { + auto mode = m_conn->currentMode(); + if (drmModeCreatePropertyBlob(m_gpu->fd(), &mode, sizeof(mode), &m_blobId) != 0) { qCWarning(KWIN_DRM) << "Failed to create property blob"; errorHandler(); return false; @@ -943,7 +876,7 @@ void DrmOutput::setVrr(bool enable) bool DrmOutput::isCursorVisible() { - return m_cursor[m_cursorIndex] && QRect(m_cursorPos, m_cursor[m_cursorIndex]->size()).intersects(QRect(0, 0, m_mode.vdisplay, m_mode.hdisplay)); + return m_cursor[m_cursorIndex] && QRect(m_cursorPos, m_cursor[m_cursorIndex]->size()).intersects(QRect(0, 0, modeSize().width(), modeSize().height())); } DrmBuffer *DrmOutput::currentBuffer() const diff --git a/src/plugins/platforms/drm/drm_output.h b/src/plugins/platforms/drm/drm_output.h index 2fd6eed3ca..24380519c1 100644 --- a/src/plugins/platforms/drm/drm_output.h +++ b/src/plugins/platforms/drm/drm_output.h @@ -49,7 +49,7 @@ public: bool hideCursor(); bool updateCursor(); void moveCursor(); - bool init(drmModeConnector *connector); + bool init(); bool present(const QSharedPointer &buffer, QRegion damagedRegion); void pageFlipped(); @@ -104,7 +104,7 @@ private: bool presentLegacy(const QSharedPointer &buffer); bool setModeLegacy(DrmBuffer *buffer); - void initOutputDevice(drmModeConnector *connector); + void initOutputDevice(); bool isCurrentMode(const drmModeModeInfo *mode) const; @@ -139,7 +139,6 @@ private: DrmConnector *m_conn = nullptr; DrmCrtc *m_crtc = nullptr; bool m_lastGbm = false; - drmModeModeInfo m_mode; DpmsMode m_dpmsModePending = DpmsMode::On; RenderLoop *m_renderLoop; @@ -154,7 +153,7 @@ private: struct { Transform transform; - drmModeModeInfo mode; + int modeIndex = 0; DrmPlane::Transformations planeTransformations; QPoint globalPos; bool valid = false;