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:
parent
2296f1ac3b
commit
2340470a9a
9 changed files with 69 additions and 11 deletions
|
@ -152,9 +152,9 @@ QHash<uint32_t, QList<uint64_t>> EglGbmLayer::supportedDrmFormats() const
|
|||
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
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
void releaseBuffers() override;
|
||||
DrmDevice *scanoutDevice() 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;
|
||||
|
||||
private:
|
||||
|
|
|
@ -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
|
||||
{
|
||||
return m_pending.needsModeset;
|
||||
|
|
|
@ -77,6 +77,7 @@ public:
|
|||
|
||||
QHash<uint32_t, QList<uint64_t>> formats(DrmPlane::TypeIndex planeType) const;
|
||||
bool pruneModifier();
|
||||
QList<QSize> recommendedSizes(DrmPlane::TypeIndex planeType) const;
|
||||
|
||||
void setOutput(DrmOutput *output);
|
||||
DrmOutput *output() const;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "drm_pointer.h"
|
||||
|
||||
#include <drm_fourcc.h>
|
||||
#include <ranges>
|
||||
|
||||
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<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;
|
||||
}
|
||||
|
||||
|
@ -201,6 +220,11 @@ bool DrmPlane::supportsTransformation(OutputTransform transform) const
|
|||
{
|
||||
return rotation.isValid() && rotation.hasEnum(outputTransformToPlaneTransform(transform));
|
||||
}
|
||||
|
||||
QList<QSize> DrmPlane::recommendedSizes() const
|
||||
{
|
||||
return m_sizeHints;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_drm_plane.cpp"
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
|
||||
void set(DrmAtomicCommit *commit, const QRect &src, const QRect &dst);
|
||||
|
||||
QList<QSize> 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<DrmFramebuffer> m_current;
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> m_supportedFormats;
|
||||
uint32_t m_possibleCrtcs;
|
||||
QList<QSize> m_sizeHints;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -37,9 +37,9 @@ void OutputLayer::setHotspot(const QPointF &hotspot)
|
|||
m_hotspot = hotspot;
|
||||
}
|
||||
|
||||
std::optional<QSize> OutputLayer::fixedSize() const
|
||||
QList<QSize> OutputLayer::recommendedSizes() const
|
||||
{
|
||||
return std::nullopt;
|
||||
return {};
|
||||
}
|
||||
|
||||
QRegion OutputLayer::repaints() const
|
||||
|
|
|
@ -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<QSize> fixedSize() const;
|
||||
virtual QList<QSize> recommendedSizes() const;
|
||||
|
||||
QRegion repaints() const;
|
||||
void resetRepaints();
|
||||
|
|
Loading…
Reference in a new issue