From 2340470a9a298e1bdb157a3cd4cc948727d3f657 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Mon, 8 Jul 2024 13:45:43 +0200 Subject: [PATCH] backends/drm: add support for drm plane size hints On a lot of hardware, using bigger plane sizes than necessary means wasting power. This is specifically problematic with the cursor plane, where we so far only had a single fixed size hint through drm caps, even though the hardware often could use a smaller cursor size. This adds support for the per-plane SIZE_HINTS property, which allows us to pick a smaller cursor size when the cursor image fits into it, and should save some power that way. --- src/backends/drm/drm_egl_layer.cpp | 4 ++-- src/backends/drm/drm_egl_layer.h | 2 +- src/backends/drm/drm_pipeline.cpp | 21 +++++++++++++++++++++ src/backends/drm/drm_pipeline.h | 1 + src/backends/drm/drm_plane.cpp | 24 ++++++++++++++++++++++++ src/backends/drm/drm_plane.h | 4 ++++ src/compositor_wayland.cpp | 14 +++++++++++--- src/core/outputlayer.cpp | 4 ++-- src/core/outputlayer.h | 6 +++--- 9 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/backends/drm/drm_egl_layer.cpp b/src/backends/drm/drm_egl_layer.cpp index 4d2f56d69e..66ab7cfee1 100644 --- a/src/backends/drm/drm_egl_layer.cpp +++ b/src/backends/drm/drm_egl_layer.cpp @@ -152,9 +152,9 @@ QHash> EglGbmLayer::supportedDrmFormats() const return m_pipeline->formats(m_type); } -std::optional EglGbmLayer::fixedSize() const +QList EglGbmLayer::recommendedSizes() const { - return m_type == DrmPlane::TypeIndex::Cursor ? std::make_optional(m_pipeline->gpu()->cursorSize()) : std::nullopt; + return m_pipeline->recommendedSizes(m_type); } const ColorPipeline &EglGbmLayer::colorPipeline() const diff --git a/src/backends/drm/drm_egl_layer.h b/src/backends/drm/drm_egl_layer.h index 415f86bcc9..93483a2e88 100644 --- a/src/backends/drm/drm_egl_layer.h +++ b/src/backends/drm/drm_egl_layer.h @@ -36,7 +36,7 @@ public: void releaseBuffers() override; DrmDevice *scanoutDevice() const override; QHash> supportedDrmFormats() const override; - std::optional fixedSize() const override; + QList recommendedSizes() const override; const ColorPipeline &colorPipeline() const override; private: diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 8016069fd3..108217cd09 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -509,6 +509,27 @@ bool DrmPipeline::pruneModifier() } } +QList DrmPipeline::recommendedSizes(DrmPlane::TypeIndex planeType) const +{ + switch (planeType) { + case DrmPlane::TypeIndex::Primary: + if (m_pending.crtc && m_pending.crtc->primaryPlane()) { + return m_pending.crtc->primaryPlane()->recommendedSizes(); + } else { + return QList{}; + } + case DrmPlane::TypeIndex::Cursor: + if (m_pending.crtc && m_pending.crtc->cursorPlane()) { + return m_pending.crtc->cursorPlane()->recommendedSizes(); + } else { + return QList{gpu()->cursorSize()}; + } + case DrmPlane::TypeIndex::Overlay: + return QList{}; + } + Q_UNREACHABLE(); +} + bool DrmPipeline::needsModeset() const { return m_pending.needsModeset; diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index 1b77692ed0..7ec484df2c 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -77,6 +77,7 @@ public: QHash> formats(DrmPlane::TypeIndex planeType) const; bool pruneModifier(); + QList recommendedSizes(DrmPlane::TypeIndex planeType) const; void setOutput(DrmOutput *output); DrmOutput *output() const; diff --git a/src/backends/drm/drm_plane.cpp b/src/backends/drm/drm_plane.cpp index 3eb6e52dec..6a6a211e8c 100644 --- a/src/backends/drm/drm_plane.cpp +++ b/src/backends/drm/drm_plane.cpp @@ -18,6 +18,7 @@ #include "drm_pointer.h" #include +#include namespace KWin { @@ -66,6 +67,7 @@ DrmPlane::DrmPlane(DrmGpu *gpu, uint32_t planeId) , vmHotspotX(this, QByteArrayLiteral("HOTSPOT_X")) , vmHotspotY(this, QByteArrayLiteral("HOTSPOT_Y")) , inFenceFd(this, QByteArrayLiteral("IN_FENCE_FD")) + , sizeHints(this, QByteArrayLiteral("SIZE_HINTS")) { } @@ -97,6 +99,7 @@ bool DrmPlane::updateProperties() vmHotspotX.update(props); vmHotspotY.update(props); inFenceFd.update(props); + sizeHints.update(props); if (!type.isValid() || !srcX.isValid() || !srcY.isValid() || !srcW.isValid() || !srcH.isValid() || !crtcX.isValid() || !crtcY.isValid() || !crtcW.isValid() || !crtcH.isValid() || !fbId.isValid()) { @@ -124,6 +127,22 @@ bool DrmPlane::updateProperties() m_supportedFormats.insert(DRM_FORMAT_XRGB8888, modifiers); } } + m_sizeHints.clear(); + if (sizeHints.isValid() && sizeHints.immutableBlob()) { + // TODO switch to drm_plane_size_hint once we require libdrm 2.4.122 + struct SizeHint + { + uint16_t width; + uint16_t height; + }; + std::span hints(reinterpret_cast(sizeHints.immutableBlob()->data), sizeHints.immutableBlob()->length / sizeof(SizeHint)); + std::ranges::transform(hints, std::back_inserter(m_sizeHints), [](const SizeHint &hint) { + return QSize(hint.width, hint.height); + }); + } + if (m_sizeHints.empty() && type.enumValue() == TypeIndex::Cursor) { + m_sizeHints = {gpu()->cursorSize()}; + } return true; } @@ -201,6 +220,11 @@ bool DrmPlane::supportsTransformation(OutputTransform transform) const { return rotation.isValid() && rotation.hasEnum(outputTransformToPlaneTransform(transform)); } + +QList DrmPlane::recommendedSizes() const +{ + return m_sizeHints; +} } #include "moc_drm_plane.cpp" diff --git a/src/backends/drm/drm_plane.h b/src/backends/drm/drm_plane.h index 6064766ae7..58a082469b 100644 --- a/src/backends/drm/drm_plane.h +++ b/src/backends/drm/drm_plane.h @@ -43,6 +43,8 @@ public: void set(DrmAtomicCommit *commit, const QRect &src, const QRect &dst); + QList recommendedSizes() const; + enum class TypeIndex : uint64_t { Overlay = 0, Primary = 1, @@ -94,12 +96,14 @@ public: DrmProperty vmHotspotX; DrmProperty vmHotspotY; DrmProperty inFenceFd; + DrmProperty sizeHints; private: std::shared_ptr m_current; QHash> m_supportedFormats; uint32_t m_possibleCrtcs; + QList m_sizeHints; }; } diff --git a/src/compositor_wayland.cpp b/src/compositor_wayland.cpp index 6120700470..1e9619c07d 100644 --- a/src/compositor_wayland.cpp +++ b/src/compositor_wayland.cpp @@ -401,11 +401,19 @@ void WaylandCompositor::addOutput(Output *output) } QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize()); QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height())); - if (const auto fixedSize = outputLayer->fixedSize()) { - if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) { + const auto recommendedSizes = outputLayer->recommendedSizes(); + if (!recommendedSizes.empty()) { + auto bigEnough = recommendedSizes | std::views::filter([bufferSize](const auto &size) { + return size.width() >= bufferSize.width() && size.height() >= bufferSize.height(); + }); + const auto it = std::ranges::min_element(bigEnough, [](const auto &left, const auto &right) { + return left.width() * left.height() < right.width() * right.height(); + }); + if (it == bigEnough.end()) { + // no size found, this most likely won't work return false; } - bufferSize = *fixedSize; + bufferSize = *it; nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize()); } outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize)); diff --git a/src/core/outputlayer.cpp b/src/core/outputlayer.cpp index 5a0329fe5f..7b5ecf7b16 100644 --- a/src/core/outputlayer.cpp +++ b/src/core/outputlayer.cpp @@ -37,9 +37,9 @@ void OutputLayer::setHotspot(const QPointF &hotspot) m_hotspot = hotspot; } -std::optional OutputLayer::fixedSize() const +QList OutputLayer::recommendedSizes() const { - return std::nullopt; + return {}; } QRegion OutputLayer::repaints() const diff --git a/src/core/outputlayer.h b/src/core/outputlayer.h index 2c42ce34c9..4eb65915bf 100644 --- a/src/core/outputlayer.h +++ b/src/core/outputlayer.h @@ -42,10 +42,10 @@ public: void setHotspot(const QPointF &hotspot); /** - * For most drm drivers, the buffer used for the cursor has to have a fixed size. - * If such a fixed size is required by the backend, this function should return it + * For some layers it can be beneficial to use specific sizes only. + * This returns those specific sizes, if present */ - virtual std::optional fixedSize() const; + virtual QList recommendedSizes() const; QRegion repaints() const; void resetRepaints();