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);
|
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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue