diff --git a/src/backends/drm/drm_object_connector.cpp b/src/backends/drm/drm_object_connector.cpp index 959942cb09..1e7de2bc11 100644 --- a/src/backends/drm/drm_object_connector.cpp +++ b/src/backends/drm/drm_object_connector.cpp @@ -22,6 +22,64 @@ namespace KWin { +static 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; +} + +DrmConnectorMode::DrmConnectorMode(DrmConnector *connector, drmModeModeInfo nativeMode) + : m_connector(connector) + , m_nativeMode(nativeMode) + , m_size(nativeMode.hdisplay, nativeMode.vdisplay) + , m_refreshRate(refreshRateForMode(&nativeMode)) +{ +} + +DrmConnectorMode::~DrmConnectorMode() +{ + if (m_blobId) { + drmModeDestroyPropertyBlob(m_connector->gpu()->fd(), m_blobId); + m_blobId = 0; + } +} + +drmModeModeInfo *DrmConnectorMode::nativeMode() +{ + return &m_nativeMode; +} + +QSize DrmConnectorMode::size() const +{ + return m_size; +} + +uint32_t DrmConnectorMode::refreshRate() const +{ + return m_refreshRate; +} + +uint32_t DrmConnectorMode::blobId() +{ + if (!m_blobId) { + if (drmModeCreatePropertyBlob(m_connector->gpu()->fd(), &m_nativeMode, sizeof(m_nativeMode), &m_blobId) != 0) { + qCWarning(KWIN_DRM) << "Failed to create connector mode blob:" << strerror(errno); + } + } + return m_blobId; +} + DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId) : DrmObject(gpu, connectorId, { PropertyDefinition(QByteArrayLiteral("CRTC_ID"), Requirement::Required), @@ -55,25 +113,9 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId) } } -DrmConnector::~DrmConnector() = default; - -namespace { -quint64 refreshRateForMode(_drmModeModeInfo *m) +DrmConnector::~DrmConnector() { - // 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; -} + qDeleteAll(m_modes); } bool DrmConnector::init() @@ -137,7 +179,7 @@ QSize DrmConnector::physicalSize() const return m_physicalSize; } -const DrmConnector::Mode &DrmConnector::currentMode() const +DrmConnectorMode *DrmConnector::currentMode() const { return m_modes[m_modeIndex]; } @@ -147,7 +189,7 @@ int DrmConnector::currentModeIndex() const return m_modeIndex; } -const QVector &DrmConnector::modes() +QVector DrmConnector::modes() { return m_modes; } @@ -157,26 +199,26 @@ void DrmConnector::setModeIndex(int index) m_modeIndex = index; } -static bool checkIfEqual(drmModeModeInfo one, drmModeModeInfo two) +static bool checkIfEqual(const drmModeModeInfo *one, const 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; + 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)) { + if (checkIfEqual(m_modes[i]->nativeMode(), ¤tMode)) { m_modeIndex = i; return; } @@ -259,16 +301,12 @@ bool DrmConnector::needsModeset() const void DrmConnector::updateModes() { + qDeleteAll(m_modes); m_modes.clear(); // reload 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; + m_modes.append(new DrmConnectorMode(this, m_conn->modes[i])); } } diff --git a/src/backends/drm/drm_object_connector.h b/src/backends/drm/drm_object_connector.h index 873ed46e0d..75805f6a36 100644 --- a/src/backends/drm/drm_object_connector.h +++ b/src/backends/drm/drm_object_connector.h @@ -23,6 +23,30 @@ namespace KWin { class DrmPipeline; +class DrmConnector; + +/** + * The DrmConnectorMode class represents a native mode and the associated blob. + */ +class DrmConnectorMode +{ +public: + DrmConnectorMode(DrmConnector *connector, drmModeModeInfo nativeMode); + ~DrmConnectorMode(); + + uint32_t blobId(); + + drmModeModeInfo *nativeMode(); + QSize size() const; + uint32_t refreshRate() const; + +private: + DrmConnector *m_connector; + drmModeModeInfo m_nativeMode; + QSize m_size; + uint32_t m_refreshRate; + uint32_t m_blobId = 0; +}; class DrmConnector : public DrmObject { @@ -66,14 +90,9 @@ public: QString modelName() const; QSize physicalSize() const; - struct Mode { - drmModeModeInfo mode; - QSize size; - uint32_t refreshRate; - }; - const Mode ¤tMode() const; + DrmConnectorMode *currentMode() const; int currentModeIndex() const; - const QVector &modes(); + QVector modes(); void setModeIndex(int index); void findCurrentMode(drmModeModeInfo currentMode); void updateModes(); @@ -92,7 +111,7 @@ private: QVector m_encoders; Edid m_edid; QSize m_physicalSize = QSize(-1, -1); - QVector m_modes; + QVector m_modes; int m_modeIndex = 0; friend QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj); diff --git a/src/backends/drm/drm_object_crtc.cpp b/src/backends/drm/drm_object_crtc.cpp index 79da043223..2e5dd54ca0 100644 --- a/src/backends/drm/drm_object_crtc.cpp +++ b/src/backends/drm/drm_object_crtc.cpp @@ -156,7 +156,7 @@ DrmPlane *DrmCrtc::cursorPlane() const void DrmCrtc::disable() { setPending(PropertyIndex::Active, 0); - setPendingBlob(PropertyIndex::ModeId, nullptr, sizeof(drmModeModeInfo)); + setPending(PropertyIndex::ModeId, 0); } } diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 76bbb09631..101d5f5e01 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -42,7 +42,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) { m_pipeline->setOutput(this); auto conn = m_pipeline->connector(); - m_renderLoop->setRefreshRate(conn->currentMode().refreshRate); + m_renderLoop->setRefreshRate(conn->currentMode()->refreshRate()); setSubPixelInternal(conn->subpixel()); setInternal(conn->isInternal()); setCapabilityInternal(DrmOutput::Capability::Dpms); @@ -192,7 +192,7 @@ QVector DrmOutput::getModes() const bool modeFound = false; QVector modes; auto conn = m_pipeline->connector(); - auto modelist = conn->modes(); + QVector modelist = conn->modes(); modes.reserve(modelist.count()); for (int i = 0; i < modelist.count(); ++i) { @@ -201,13 +201,13 @@ QVector DrmOutput::getModes() const mode.flags |= ModeFlag::Current; modeFound = true; } - if (modelist[i].mode.type & DRM_MODE_TYPE_PREFERRED) { + if (modelist[i]->nativeMode()->type & DRM_MODE_TYPE_PREFERRED) { mode.flags |= ModeFlag::Preferred; } mode.id = i; - mode.size = modelist[i].size; - mode.refreshRate = modelist[i].refreshRate; + mode.size = modelist[i]->size(); + mode.refreshRate = modelist[i]->refreshRate(); modes << mode; } if (!modeFound) { @@ -353,8 +353,8 @@ void DrmOutput::updateModes() if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) { m_pipeline->applyPendingChanges(); auto mode = m_pipeline->connector()->currentMode(); - setCurrentModeInternal(mode.size, mode.refreshRate); - m_renderLoop->setRefreshRate(mode.refreshRate); + setCurrentModeInternal(mode->size(), mode->refreshRate()); + m_renderLoop->setRefreshRate(mode->refreshRate()); } else { qCWarning(KWIN_DRM) << "Setting changed mode failed!"; m_pipeline->revertPendingChanges(); @@ -440,7 +440,7 @@ bool DrmOutput::queueChanges(const WaylandOutputConfig &config) auto modelist = m_connector->modes(); int index = -1; for (int i = 0; i < modelist.size(); i++) { - if (modelist[i].size == props->modeSize && modelist[i].refreshRate == props->refreshRate) { + if (modelist[i]->size() == props->modeSize && modelist[i]->refreshRate() == props->refreshRate) { index = i; break; } @@ -471,8 +471,8 @@ void DrmOutput::applyQueuedChanges(const WaylandOutputConfig &config) m_connector->setModeIndex(m_pipeline->pending.modeIndex); auto mode = m_connector->currentMode(); - setCurrentModeInternal(mode.size, mode.refreshRate); - m_renderLoop->setRefreshRate(mode.refreshRate); + setCurrentModeInternal(mode->size(), mode->refreshRate()); + m_renderLoop->setRefreshRate(mode->refreshRate()); setOverscanInternal(m_pipeline->pending.overscan); setRgbRangeInternal(m_pipeline->pending.rgbRange); setVrrPolicy(props->vrrPolicy); diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 4119fac249..9711b044c4 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -259,7 +259,7 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags) flags |= DRM_MODE_PAGE_FLIP_EVENT; } if (pending.crtc) { - auto modeSize = m_connector->modes()[pending.modeIndex].size; + auto modeSize = m_connector->modes()[pending.modeIndex]->size(); pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : modeSize, QPoint(0, 0), modeSize); pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr); pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive); @@ -362,11 +362,11 @@ void DrmPipeline::prepareModeset() } pending.crtc->setPending(DrmCrtc::PropertyIndex::Active, activePending()); - pending.crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, activePending() ? &mode.mode : nullptr, sizeof(drmModeModeInfo)); + pending.crtc->setPending(DrmCrtc::PropertyIndex::ModeId, activePending() ? mode->blobId() : 0); pending.crtc->primaryPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0); pending.crtc->primaryPlane()->setTransformation(DrmPlane::Transformation::Rotate0); - pending.crtc->primaryPlane()->set(QPoint(0, 0), sourceSize(), QPoint(0, 0), mode.size); + pending.crtc->primaryPlane()->set(QPoint(0, 0), sourceSize(), QPoint(0, 0), mode->size()); m_formats = pending.crtc->primaryPlane()->formats(); } @@ -418,7 +418,7 @@ bool DrmPipeline::legacyModeset() { auto mode = m_connector->modes()[pending.modeIndex]; uint32_t connId = m_connector->id(); - if (!checkTestBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, &mode.mode) != 0) { + if (!checkTestBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, mode->nativeMode()) != 0) { qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno); pending = m_next; m_primaryBuffer = m_oldTestBuffer; @@ -438,14 +438,14 @@ QSize DrmPipeline::sourceSize() const { auto mode = m_connector->modes()[pending.modeIndex]; if (pending.transformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) { - return mode.size.transposed(); + return mode->size().transposed(); } - return mode.size; + return mode->size(); } bool DrmPipeline::isCursorVisible() const { - return pending.crtc && pending.crtc->isCursorVisible(QRect(QPoint(0, 0), m_connector->currentMode().size)); + return pending.crtc && pending.crtc->isCursorVisible(QRect(QPoint(0, 0), m_connector->currentMode()->size())); } QPoint DrmPipeline::cursorPos() const