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.
This commit is contained in:
Xaver Hugl 2024-07-08 13:45:43 +02:00
parent 2296f1ac3b
commit 2340470a9a
9 changed files with 69 additions and 11 deletions

View file

@ -152,9 +152,9 @@ QHash<uint32_t, QList<uint64_t>> EglGbmLayer::supportedDrmFormats() const
return m_pipeline->formats(m_type); return m_pipeline->formats(m_type);
} }
std::optional<QSize> EglGbmLayer::fixedSize() const QList<QSize> 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 const ColorPipeline &EglGbmLayer::colorPipeline() const

View file

@ -36,7 +36,7 @@ public:
void releaseBuffers() override; void releaseBuffers() override;
DrmDevice *scanoutDevice() const override; DrmDevice *scanoutDevice() const override;
QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override; QHash<uint32_t, QList<uint64_t>> supportedDrmFormats() const override;
std::optional<QSize> fixedSize() const override; QList<QSize> recommendedSizes() const override;
const ColorPipeline &colorPipeline() const override; const ColorPipeline &colorPipeline() const override;
private: private:

View file

@ -509,6 +509,27 @@ bool DrmPipeline::pruneModifier()
} }
} }
QList<QSize> 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<QSize>{};
}
case DrmPlane::TypeIndex::Cursor:
if (m_pending.crtc && m_pending.crtc->cursorPlane()) {
return m_pending.crtc->cursorPlane()->recommendedSizes();
} else {
return QList<QSize>{gpu()->cursorSize()};
}
case DrmPlane::TypeIndex::Overlay:
return QList<QSize>{};
}
Q_UNREACHABLE();
}
bool DrmPipeline::needsModeset() const bool DrmPipeline::needsModeset() const
{ {
return m_pending.needsModeset; return m_pending.needsModeset;

View file

@ -77,6 +77,7 @@ public:
QHash<uint32_t, QList<uint64_t>> formats(DrmPlane::TypeIndex planeType) const; QHash<uint32_t, QList<uint64_t>> formats(DrmPlane::TypeIndex planeType) const;
bool pruneModifier(); bool pruneModifier();
QList<QSize> recommendedSizes(DrmPlane::TypeIndex planeType) const;
void setOutput(DrmOutput *output); void setOutput(DrmOutput *output);
DrmOutput *output() const; DrmOutput *output() const;

View file

@ -18,6 +18,7 @@
#include "drm_pointer.h" #include "drm_pointer.h"
#include <drm_fourcc.h> #include <drm_fourcc.h>
#include <ranges>
namespace KWin namespace KWin
{ {
@ -66,6 +67,7 @@ DrmPlane::DrmPlane(DrmGpu *gpu, uint32_t planeId)
, vmHotspotX(this, QByteArrayLiteral("HOTSPOT_X")) , vmHotspotX(this, QByteArrayLiteral("HOTSPOT_X"))
, vmHotspotY(this, QByteArrayLiteral("HOTSPOT_Y")) , vmHotspotY(this, QByteArrayLiteral("HOTSPOT_Y"))
, inFenceFd(this, QByteArrayLiteral("IN_FENCE_FD")) , inFenceFd(this, QByteArrayLiteral("IN_FENCE_FD"))
, sizeHints(this, QByteArrayLiteral("SIZE_HINTS"))
{ {
} }
@ -97,6 +99,7 @@ bool DrmPlane::updateProperties()
vmHotspotX.update(props); vmHotspotX.update(props);
vmHotspotY.update(props); vmHotspotY.update(props);
inFenceFd.update(props); inFenceFd.update(props);
sizeHints.update(props);
if (!type.isValid() || !srcX.isValid() || !srcY.isValid() || !srcW.isValid() || !srcH.isValid() if (!type.isValid() || !srcX.isValid() || !srcY.isValid() || !srcW.isValid() || !srcH.isValid()
|| !crtcX.isValid() || !crtcY.isValid() || !crtcW.isValid() || !crtcH.isValid() || !fbId.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_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<SizeHint> hints(reinterpret_cast<SizeHint *>(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; return true;
} }
@ -201,6 +220,11 @@ bool DrmPlane::supportsTransformation(OutputTransform transform) const
{ {
return rotation.isValid() && rotation.hasEnum(outputTransformToPlaneTransform(transform)); return rotation.isValid() && rotation.hasEnum(outputTransformToPlaneTransform(transform));
} }
QList<QSize> DrmPlane::recommendedSizes() const
{
return m_sizeHints;
}
} }
#include "moc_drm_plane.cpp" #include "moc_drm_plane.cpp"

View file

@ -43,6 +43,8 @@ public:
void set(DrmAtomicCommit *commit, const QRect &src, const QRect &dst); void set(DrmAtomicCommit *commit, const QRect &src, const QRect &dst);
QList<QSize> recommendedSizes() const;
enum class TypeIndex : uint64_t { enum class TypeIndex : uint64_t {
Overlay = 0, Overlay = 0,
Primary = 1, Primary = 1,
@ -94,12 +96,14 @@ public:
DrmProperty vmHotspotX; DrmProperty vmHotspotX;
DrmProperty vmHotspotY; DrmProperty vmHotspotY;
DrmProperty inFenceFd; DrmProperty inFenceFd;
DrmProperty sizeHints;
private: private:
std::shared_ptr<DrmFramebuffer> m_current; std::shared_ptr<DrmFramebuffer> m_current;
QHash<uint32_t, QList<uint64_t>> m_supportedFormats; QHash<uint32_t, QList<uint64_t>> m_supportedFormats;
uint32_t m_possibleCrtcs; uint32_t m_possibleCrtcs;
QList<QSize> m_sizeHints;
}; };
} }

View file

@ -401,11 +401,19 @@ void WaylandCompositor::addOutput(Output *output)
} }
QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize()); QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize());
QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height())); QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height()));
if (const auto fixedSize = outputLayer->fixedSize()) { const auto recommendedSizes = outputLayer->recommendedSizes();
if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) { 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; return false;
} }
bufferSize = *fixedSize; bufferSize = *it;
nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize()); nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize());
} }
outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize)); outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize));

View file

@ -37,9 +37,9 @@ void OutputLayer::setHotspot(const QPointF &hotspot)
m_hotspot = hotspot; m_hotspot = hotspot;
} }
std::optional<QSize> OutputLayer::fixedSize() const QList<QSize> OutputLayer::recommendedSizes() const
{ {
return std::nullopt; return {};
} }
QRegion OutputLayer::repaints() const QRegion OutputLayer::repaints() const

View file

@ -42,10 +42,10 @@ public:
void setHotspot(const QPointF &hotspot); void setHotspot(const QPointF &hotspot);
/** /**
* For most drm drivers, the buffer used for the cursor has to have a fixed size. * For some layers it can be beneficial to use specific sizes only.
* If such a fixed size is required by the backend, this function should return it * This returns those specific sizes, if present
*/ */
virtual std::optional<QSize> fixedSize() const; virtual QList<QSize> recommendedSizes() const;
QRegion repaints() const; QRegion repaints() const;
void resetRepaints(); void resetRepaints();