platforms/drm: Move ownership of mode blob to connector mode

The main motivation behind this change is to move management of drm
blobs out of property wrappers in specialized wrappers to simplify state
management with blobs.

Connector mode blobs are created on demand.
This commit is contained in:
Vlad Zahorodnii 2021-09-28 11:30:57 +03:00 committed by Xaver Hugl
parent 1b5009ae2b
commit 02bb276ebf
5 changed files with 123 additions and 66 deletions

View file

@ -22,6 +22,64 @@
namespace KWin 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) DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId)
: DrmObject(gpu, connectorId, { : DrmObject(gpu, connectorId, {
PropertyDefinition(QByteArrayLiteral("CRTC_ID"), Requirement::Required), PropertyDefinition(QByteArrayLiteral("CRTC_ID"), Requirement::Required),
@ -55,25 +113,9 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId)
} }
} }
DrmConnector::~DrmConnector() = default; DrmConnector::~DrmConnector()
namespace {
quint64 refreshRateForMode(_drmModeModeInfo *m)
{ {
// Calculate higher precision (mHz) refresh rate qDeleteAll(m_modes);
// 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;
}
} }
bool DrmConnector::init() bool DrmConnector::init()
@ -137,7 +179,7 @@ QSize DrmConnector::physicalSize() const
return m_physicalSize; return m_physicalSize;
} }
const DrmConnector::Mode &DrmConnector::currentMode() const DrmConnectorMode *DrmConnector::currentMode() const
{ {
return m_modes[m_modeIndex]; return m_modes[m_modeIndex];
} }
@ -147,7 +189,7 @@ int DrmConnector::currentModeIndex() const
return m_modeIndex; return m_modeIndex;
} }
const QVector<DrmConnector::Mode> &DrmConnector::modes() QVector<DrmConnectorMode *> DrmConnector::modes()
{ {
return m_modes; return m_modes;
} }
@ -157,26 +199,26 @@ void DrmConnector::setModeIndex(int index)
m_modeIndex = 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 return one->clock == two->clock
&& one.hdisplay == two.hdisplay && one->hdisplay == two->hdisplay
&& one.hsync_start == two.hsync_start && one->hsync_start == two->hsync_start
&& one.hsync_end == two.hsync_end && one->hsync_end == two->hsync_end
&& one.htotal == two.htotal && one->htotal == two->htotal
&& one.hskew == two.hskew && one->hskew == two->hskew
&& one.vdisplay == two.vdisplay && one->vdisplay == two->vdisplay
&& one.vsync_start == two.vsync_start && one->vsync_start == two->vsync_start
&& one.vsync_end == two.vsync_end && one->vsync_end == two->vsync_end
&& one.vtotal == two.vtotal && one->vtotal == two->vtotal
&& one.vscan == two.vscan && one->vscan == two->vscan
&& one.vrefresh == two.vrefresh; && one->vrefresh == two->vrefresh;
} }
void DrmConnector::findCurrentMode(drmModeModeInfo currentMode) void DrmConnector::findCurrentMode(drmModeModeInfo currentMode)
{ {
for (int i = 0; i < m_modes.count(); i++) { for (int i = 0; i < m_modes.count(); i++) {
if (checkIfEqual(m_modes[i].mode, currentMode)) { if (checkIfEqual(m_modes[i]->nativeMode(), &currentMode)) {
m_modeIndex = i; m_modeIndex = i;
return; return;
} }
@ -259,16 +301,12 @@ bool DrmConnector::needsModeset() const
void DrmConnector::updateModes() void DrmConnector::updateModes()
{ {
qDeleteAll(m_modes);
m_modes.clear(); m_modes.clear();
// reload modes // reload modes
for (int i = 0; i < m_conn->count_modes; i++) { for (int i = 0; i < m_conn->count_modes; i++) {
auto mode = m_conn->modes[i]; m_modes.append(new DrmConnectorMode(this, m_conn->modes[i]));
Mode m;
m.mode = mode;
m.size = QSize(mode.hdisplay, mode.vdisplay);
m.refreshRate = refreshRateForMode(&mode);
m_modes << m;
} }
} }

View file

@ -23,6 +23,30 @@ namespace KWin
{ {
class DrmPipeline; 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 class DrmConnector : public DrmObject
{ {
@ -66,14 +90,9 @@ public:
QString modelName() const; QString modelName() const;
QSize physicalSize() const; QSize physicalSize() const;
struct Mode { DrmConnectorMode *currentMode() const;
drmModeModeInfo mode;
QSize size;
uint32_t refreshRate;
};
const Mode &currentMode() const;
int currentModeIndex() const; int currentModeIndex() const;
const QVector<Mode> &modes(); QVector<DrmConnectorMode *> modes();
void setModeIndex(int index); void setModeIndex(int index);
void findCurrentMode(drmModeModeInfo currentMode); void findCurrentMode(drmModeModeInfo currentMode);
void updateModes(); void updateModes();
@ -92,7 +111,7 @@ private:
QVector<uint32_t> m_encoders; QVector<uint32_t> m_encoders;
Edid m_edid; Edid m_edid;
QSize m_physicalSize = QSize(-1, -1); QSize m_physicalSize = QSize(-1, -1);
QVector<Mode> m_modes; QVector<DrmConnectorMode *> m_modes;
int m_modeIndex = 0; int m_modeIndex = 0;
friend QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj); friend QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj);

View file

@ -156,7 +156,7 @@ DrmPlane *DrmCrtc::cursorPlane() const
void DrmCrtc::disable() void DrmCrtc::disable()
{ {
setPending(PropertyIndex::Active, 0); setPending(PropertyIndex::Active, 0);
setPendingBlob(PropertyIndex::ModeId, nullptr, sizeof(drmModeModeInfo)); setPending(PropertyIndex::ModeId, 0);
} }
} }

View file

@ -42,7 +42,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
{ {
m_pipeline->setOutput(this); m_pipeline->setOutput(this);
auto conn = m_pipeline->connector(); auto conn = m_pipeline->connector();
m_renderLoop->setRefreshRate(conn->currentMode().refreshRate); m_renderLoop->setRefreshRate(conn->currentMode()->refreshRate());
setSubPixelInternal(conn->subpixel()); setSubPixelInternal(conn->subpixel());
setInternal(conn->isInternal()); setInternal(conn->isInternal());
setCapabilityInternal(DrmOutput::Capability::Dpms); setCapabilityInternal(DrmOutput::Capability::Dpms);
@ -192,7 +192,7 @@ QVector<AbstractWaylandOutput::Mode> DrmOutput::getModes() const
bool modeFound = false; bool modeFound = false;
QVector<Mode> modes; QVector<Mode> modes;
auto conn = m_pipeline->connector(); auto conn = m_pipeline->connector();
auto modelist = conn->modes(); QVector<DrmConnectorMode *> modelist = conn->modes();
modes.reserve(modelist.count()); modes.reserve(modelist.count());
for (int i = 0; i < modelist.count(); ++i) { for (int i = 0; i < modelist.count(); ++i) {
@ -201,13 +201,13 @@ QVector<AbstractWaylandOutput::Mode> DrmOutput::getModes() const
mode.flags |= ModeFlag::Current; mode.flags |= ModeFlag::Current;
modeFound = true; 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.flags |= ModeFlag::Preferred;
} }
mode.id = i; mode.id = i;
mode.size = modelist[i].size; mode.size = modelist[i]->size();
mode.refreshRate = modelist[i].refreshRate; mode.refreshRate = modelist[i]->refreshRate();
modes << mode; modes << mode;
} }
if (!modeFound) { if (!modeFound) {
@ -353,8 +353,8 @@ void DrmOutput::updateModes()
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) { if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
m_pipeline->applyPendingChanges(); m_pipeline->applyPendingChanges();
auto mode = m_pipeline->connector()->currentMode(); auto mode = m_pipeline->connector()->currentMode();
setCurrentModeInternal(mode.size, mode.refreshRate); setCurrentModeInternal(mode->size(), mode->refreshRate());
m_renderLoop->setRefreshRate(mode.refreshRate); m_renderLoop->setRefreshRate(mode->refreshRate());
} else { } else {
qCWarning(KWIN_DRM) << "Setting changed mode failed!"; qCWarning(KWIN_DRM) << "Setting changed mode failed!";
m_pipeline->revertPendingChanges(); m_pipeline->revertPendingChanges();
@ -440,7 +440,7 @@ bool DrmOutput::queueChanges(const WaylandOutputConfig &config)
auto modelist = m_connector->modes(); auto modelist = m_connector->modes();
int index = -1; int index = -1;
for (int i = 0; i < modelist.size(); i++) { 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; index = i;
break; break;
} }
@ -471,8 +471,8 @@ void DrmOutput::applyQueuedChanges(const WaylandOutputConfig &config)
m_connector->setModeIndex(m_pipeline->pending.modeIndex); m_connector->setModeIndex(m_pipeline->pending.modeIndex);
auto mode = m_connector->currentMode(); auto mode = m_connector->currentMode();
setCurrentModeInternal(mode.size, mode.refreshRate); setCurrentModeInternal(mode->size(), mode->refreshRate());
m_renderLoop->setRefreshRate(mode.refreshRate); m_renderLoop->setRefreshRate(mode->refreshRate());
setOverscanInternal(m_pipeline->pending.overscan); setOverscanInternal(m_pipeline->pending.overscan);
setRgbRangeInternal(m_pipeline->pending.rgbRange); setRgbRangeInternal(m_pipeline->pending.rgbRange);
setVrrPolicy(props->vrrPolicy); setVrrPolicy(props->vrrPolicy);

View file

@ -259,7 +259,7 @@ bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
flags |= DRM_MODE_PAGE_FLIP_EVENT; flags |= DRM_MODE_PAGE_FLIP_EVENT;
} }
if (pending.crtc) { 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()->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->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr);
pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive); 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->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()->setPending(DrmPlane::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
pending.crtc->primaryPlane()->setTransformation(DrmPlane::Transformation::Rotate0); 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(); m_formats = pending.crtc->primaryPlane()->formats();
} }
@ -418,7 +418,7 @@ bool DrmPipeline::legacyModeset()
{ {
auto mode = m_connector->modes()[pending.modeIndex]; auto mode = m_connector->modes()[pending.modeIndex];
uint32_t connId = m_connector->id(); 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); qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
pending = m_next; pending = m_next;
m_primaryBuffer = m_oldTestBuffer; m_primaryBuffer = m_oldTestBuffer;
@ -438,14 +438,14 @@ QSize DrmPipeline::sourceSize() const
{ {
auto mode = m_connector->modes()[pending.modeIndex]; auto mode = m_connector->modes()[pending.modeIndex];
if (pending.transformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) { 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 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 QPoint DrmPipeline::cursorPos() const