Commit 5a22deda authored by Xaver Hugl's avatar Xaver Hugl
Browse files

platforms/drm: Introduce DrmPipeline

DrmPipeline is what now contains all the drm bits related to
modesetting and presentation, instead of that being in DrmOutput.
This gives a lot more freedom for managing drm resources and
enables far better usage of the atomic API with guaranteed
immutability for failed tests.
parent b68cd311
......@@ -13,6 +13,7 @@ set(DRM_SOURCES
drm_gpu.cpp
egl_multi_backend.cpp
abstract_egl_drm_backend.cpp
drm_pipeline.cpp
)
if (HAVE_GBM)
......
......@@ -155,11 +155,13 @@ void DrmBackend::reactivate()
if (!usesSoftwareCursor()) {
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
DrmOutput *o = *it;
// only relevant in atomic mode
o->m_modesetRequested = true;
o->m_crtc->blank(o);
o->showCursor();
o->moveCursor();
if (o->isEnabled()) {
o->updateMode(o->connector()->currentModeIndex());
o->showCursor();
o->moveCursor();
} else {
o->updateEnablement(false);
}
}
}
......@@ -178,7 +180,6 @@ void DrmBackend::deactivate()
}
for (DrmOutput *output : qAsConst(m_outputs)) {
output->hideCursor();
output->renderLoop()->inhibit();
}
......@@ -431,15 +432,16 @@ QString DrmBackend::generateOutputConfigurationUuid() const
void DrmBackend::enableOutput(DrmOutput *output, bool enable)
{
bool enabled = m_enabledOutputs.contains(output);
if (enabled == enable) {
return;
}
if (enable) {
Q_ASSERT(!m_enabledOutputs.contains(output));
m_enabledOutputs << output;
emit output->gpu()->outputEnabled(output);
emit outputEnabled(output);
} else {
Q_ASSERT(m_enabledOutputs.contains(output));
m_enabledOutputs.removeOne(output);
Q_ASSERT(!m_enabledOutputs.contains(output));
emit output->gpu()->outputDisabled(output);
emit outputDisabled(output);
}
......@@ -520,7 +522,11 @@ void DrmBackend::updateCursor()
qCDebug(KWIN_DRM) << "Failed to show cursor on output" << output->name();
break;
}
output->moveCursor();
success = output->moveCursor();
if (!success) {
qCDebug(KWIN_DRM) << "Failed to move cursor on output" << output->name();
break;
}
}
setSoftwareCursor(!success);
......
......@@ -259,12 +259,10 @@ bool DrmGpu::updateOutputs()
DrmOutput *output = new DrmOutput(this->m_backend, this);
output->m_conn = con;
output->m_crtc = crtc;
output->m_mode = connector->modes[0];
output->m_primaryPlane = getCompatiblePlane(DrmPlane::TypeIndex::Primary, crtc);
output->m_cursorPlane = getCompatiblePlane(DrmPlane::TypeIndex::Cursor, crtc);
qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name << output->m_mode.hdisplay << output->m_mode.vdisplay;
if (!output->init(connector.data())) {
qCDebug(KWIN_DRM) << "For new output use mode" << con->currentMode().mode.name;
if (!output->init()) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id();
delete output;
continue;
......@@ -413,4 +411,20 @@ void DrmGpu::dispatchEvents()
drmHandleEvent(m_fd, &context);
}
QSharedPointer<DrmBuffer> DrmGpu::createTestbuffer(const QSize &size)
{
#if HAVE_GBM
if (m_gbmDevice) {
gbm_bo *bo = gbm_bo_create(m_gbmDevice, size.width(), size.height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT);
if (bo) {
auto buffer = QSharedPointer<DrmGbmBuffer>::create(this, bo, nullptr);
if (buffer->bufferId()) {
return buffer;
}
}
}
#endif
return QSharedPointer<DrmDumbBuffer>::create(this, size);
}
}
......@@ -98,6 +98,8 @@ public:
void waitIdle();
QSharedPointer<DrmBuffer> createTestbuffer(const QSize &size);
Q_SIGNALS:
void outputAdded(DrmOutput *output);
void outputRemoved(DrmOutput *output);
......
......@@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -29,8 +30,31 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId)
DrmConnector::~DrmConnector() = default;
namespace {
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;
}
}
bool DrmConnector::init()
{
if (!m_conn->count_modes) {
return false;
}
qCDebug(KWIN_DRM) << "Creating connector" << id();
if (!initProps({
......@@ -72,16 +96,26 @@ bool DrmConnector::init()
m_physicalSize = overwriteSize;
}
// init modes
for (int i = 0; i < m_conn->count_modes; i++) {
auto mode = m_conn->modes[i];
Mode m;
m.mode = mode;
m.size = QSize(mode.hdisplay, mode.vdisplay);
m.refreshRate = refreshRateForMode(&mode);
m_modes << m;
}
return true;
}
bool DrmConnector::isConnected()
{
DrmScopedPointer<drmModeConnector> con(drmModeGetConnector(gpu()->fd(), id()));
if (!con) {
m_conn.reset(drmModeGetConnector(gpu()->fd(), id()));
if (!m_conn) {
return false;
}
return con->connection == DRM_MODE_CONNECTED;
return m_conn->connection == DRM_MODE_CONNECTED;
}
static QHash<int, QByteArray> s_connectorNames = {
......@@ -132,5 +166,46 @@ QSize DrmConnector::physicalSize() const
return m_physicalSize;
}
const DrmConnector::Mode &DrmConnector::currentMode() const
{
return m_modes[m_modeIndex];
}
int DrmConnector::currentModeIndex() const
{
return m_modeIndex;
}
const QVector<DrmConnector::Mode> &DrmConnector::modes()
{
return m_modes;
}
void DrmConnector::setModeIndex(int index)
{
m_modeIndex = index;
}
AbstractWaylandOutput::SubPixel DrmConnector::subpixel() const
{
switch (m_conn->subpixel) {
case DRM_MODE_SUBPIXEL_UNKNOWN:
return AbstractWaylandOutput::SubPixel::Unknown;
case DRM_MODE_SUBPIXEL_NONE:
return AbstractWaylandOutput::SubPixel::None;
case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
return AbstractWaylandOutput::SubPixel::Horizontal_RGB;
case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
return AbstractWaylandOutput::SubPixel::Horizontal_BGR;
case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
return AbstractWaylandOutput::SubPixel::Vertical_RGB;
case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
return AbstractWaylandOutput::SubPixel::Vertical_BGR;
default:
Q_UNREACHABLE();
}
}
}
......@@ -3,16 +3,21 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_DRM_OBJECT_CONNECTOR_H
#define KWIN_DRM_OBJECT_CONNECTOR_H
#pragma once
#include <QPoint>
#include <QSize>
#include <QSize>
#include "drm_object.h"
#include "edid.h"
#include "drm_pointer.h"
#include "abstract_wayland_output.h"
namespace KWin
{
......@@ -61,15 +66,31 @@ public:
bool isInternal() const;
QSize physicalSize() const;
struct Mode {
drmModeModeInfo mode;
QSize size;
uint32_t refreshRate;
};
/**
* until the actual current mode is set with setMode(int)
* this will always return the first mode
*/
const Mode &currentMode() const;
int currentModeIndex() const;
const QVector<Mode> &modes();
void setModeIndex(int index);
AbstractWaylandOutput::SubPixel subpixel() const;
private:
DrmScopedPointer<drmModeConnector> m_conn;
QVector<uint32_t> m_encoders;
Edid m_edid;
QSize m_physicalSize = QSize(-1, -1);
QVector<Mode> m_modes;
int m_modeIndex = 0;
};
}
#endif
......@@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2016 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -33,6 +34,8 @@ bool DrmCrtc::init()
return initProps({
PropertyDefinition(QByteArrayLiteral("MODE_ID")),
PropertyDefinition(QByteArrayLiteral("ACTIVE")),
PropertyDefinition(QByteArrayLiteral("GAMMA_LUT")),
PropertyDefinition(QByteArrayLiteral("GAMMA_LUT_SIZE")),
}, DRM_MODE_OBJECT_CRTC);
}
......@@ -40,45 +43,12 @@ void DrmCrtc::flipBuffer()
{
m_currentBuffer = m_nextBuffer;
m_nextBuffer = nullptr;
delete m_blackBuffer;
m_blackBuffer = nullptr;
}
bool DrmCrtc::blank(DrmOutput *output)
{
if (gpu()->atomicModeSetting()) {
return false;
}
if (!m_blackBuffer) {
DrmDumbBuffer *blackBuffer = new DrmDumbBuffer(gpu(), output->pixelSize());
if (!blackBuffer->map()) {
delete blackBuffer;
return false;
}
blackBuffer->image()->fill(Qt::black);
m_blackBuffer = blackBuffer;
}
if (output->setModeLegacy(m_blackBuffer)) {
m_currentBuffer = nullptr;
m_nextBuffer = nullptr;
return true;
}
return false;
}
bool DrmCrtc::setGammaRamp(const GammaRamp &gamma)
drmModeModeInfo DrmCrtc::queryCurrentMode()
{
uint16_t *red = const_cast<uint16_t *>(gamma.red());
uint16_t *green = const_cast<uint16_t *>(gamma.green());
uint16_t *blue = const_cast<uint16_t *>(gamma.blue());
const bool isError = drmModeCrtcSetGamma(gpu()->fd(), id(),
gamma.size(), red, green, blue);
return !isError;
m_crtc.reset(drmModeGetCrtc(gpu()->fd(), id()));
return m_crtc->mode;
}
}
......@@ -13,6 +13,8 @@
#include <QSharedPointer>
#include "drm_pointer.h"
namespace KWin
{
......@@ -31,7 +33,9 @@ public:
enum class PropertyIndex : uint32_t {
ModeId = 0,
Active,
Active = 1,
Gamma_LUT = 2,
Gamma_LUT_size = 3,
Count
};
......@@ -53,19 +57,26 @@ public:
}
void flipBuffer();
bool blank(DrmOutput *output);
int gammaRampSize() const {
if (const auto &prop = m_props.at(static_cast<int>(PropertyIndex::Gamma_LUT_size))) {
return prop->value();
}
return m_crtc->gamma_size;
}
bool setGammaRamp(const GammaRamp &gamma);
bool hasGammaProp() const {
return m_props.at(static_cast<int>(PropertyIndex::Gamma_LUT));
}
drmModeModeInfo queryCurrentMode();
private:
DrmScopedPointer<drmModeCrtc> m_crtc;
int m_pipeIndex;
QSharedPointer<DrmBuffer> m_currentBuffer;
QSharedPointer<DrmBuffer> m_nextBuffer;
DrmDumbBuffer *m_blackBuffer = nullptr;
int m_pipeIndex;
};
}
......
......@@ -118,4 +118,30 @@ void DrmPlane::flipBuffer()
m_next = nullptr;
}
void DrmPlane::setScaled(const QSize &srcSize, const QSize &modeSize, int crtcId, bool enable)
{
QPoint targetPos = QPoint(0, 0);
QSize targetSize = modeSize;
if (modeSize != srcSize) {
targetSize = srcSize.scaled(modeSize, Qt::AspectRatioMode::KeepAspectRatio);
targetPos.setX((modeSize.width() - targetSize.width()) / 2);
targetPos.setY((modeSize.height() - targetSize.height()) / 2);
}
set(srcSize, targetPos, targetSize, crtcId, enable);
}
void DrmPlane::set(const QSize &src, const QPoint &dstPos, const QSize &dstSize, int crtcId, bool enable)
{
setValue(PropertyIndex::SrcX, 0);
setValue(PropertyIndex::SrcY, 0);
setValue(PropertyIndex::SrcW, src.width() << 16);
setValue(PropertyIndex::SrcH, src.height() << 16);
setValue(PropertyIndex::CrtcX, enable ? dstPos.x() : 0);
setValue(PropertyIndex::CrtcY, enable ? dstPos.y() : 0);
setValue(PropertyIndex::CrtcW, dstSize.width());
setValue(PropertyIndex::CrtcH, dstSize.height());
setValue(PropertyIndex::CrtcId, enable ? crtcId : 0);
setValue(PropertyIndex::FbId, (m_next && enable) ? m_next->bufferId() : 0);
}
}
......@@ -90,6 +90,9 @@ public:
return m_supportedTransformations;
}
void setScaled(const QSize &srcSize, const QSize &modeSize, int crtcId, bool enable);
void set(const QSize &src, const QPoint &dstPos, const QSize &dstSize, int crtcId, bool enable);
private:
QSharedPointer<DrmBuffer> m_current;
QSharedPointer<DrmBuffer> m_next;
......
This diff is collapsed.
......@@ -3,6 +3,7 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -32,6 +33,7 @@ class DrmConnector;
class DrmCrtc;
class Cursor;
class DrmGpu;
class DrmPipeline;
class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput
{
......@@ -42,25 +44,21 @@ public:
RenderLoop *renderLoop() const override;
bool init();
///queues deleting the output after a page flip has completed.
void teardown();
void releaseBuffers();
bool showCursor(DrmDumbBuffer *buffer);
bool showCursor();
bool hideCursor();
bool updateCursor();
void moveCursor();
bool init(drmModeConnector *connector);
bool present(const QSharedPointer<DrmBuffer> &buffer);
void pageFlipped();
bool isDpmsEnabled() const {
// We care for current as well as pending mode in order to allow first present in AMS.
return m_dpmsModePending == DpmsMode::On;
}
bool updateCursor();
bool moveCursor();
bool showCursor();
bool hideCursor();
DpmsMode dpmsModePending() const {
return m_dpmsModePending;
bool isDpmsEnabled() const {
return m_dpmsEnabled;
}
const DrmCrtc *crtc() const {
......@@ -94,36 +92,12 @@ private:
// and save the connector ids in the DrmCrtc instance.
DrmOutput(DrmBackend *backend, DrmGpu* gpu);
bool presentAtomically(const QSharedPointer<DrmBuffer> &buffer);
enum class AtomicCommitMode {
Test,
Real
};
bool doAtomicCommit(AtomicCommitMode mode);
void initOutputDevice();
bool presentLegacy(const QSharedPointer<DrmBuffer> &buffer);
bool setModeLegacy(DrmBuffer *buffer);
void initOutputDevice(drmModeConnector *connector);
bool isCurrentMode(const drmModeModeInfo *mode) const;
void atomicEnable();
void atomicDisable();
void updateEnablement(bool enable) override;
bool dpmsAtomicOff();
bool dpmsLegacyApply();
void dpmsFinishOn();
void dpmsFinishOff();
bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable);
void setDpmsMode(DpmsMode mode) override;
void updateMode(int modeIndex) override;
void updateMode(uint32_t width, uint32_t height, uint32_t refreshRate);
void setCurrentModeInternal();
void updateTransform(Transform transform) override;
int gammaRampSize() const override;
......@@ -131,31 +105,18 @@ private:
DrmBackend *m_backend;
DrmGpu *m_gpu;
DrmPlane *m_primaryPlane = nullptr;
DrmPlane *m_cursorPlane = nullptr;
DrmConnector *m_conn = nullptr;
DrmCrtc *m_crtc = nullptr;
bool m_lastGbm = false;
drmModeModeInfo m_mode;
DpmsMode m_dpmsModePending = DpmsMode::On;
RenderLoop *m_renderLoop;
DrmPipeline *m_pipeline = nullptr;
uint32_t m_blobId = 0;
DrmPlane *m_primaryPlane = nullptr;
DrmPlane *m_cursorPlane = nullptr;
QVector<DrmPlane*> m_nextPlanesFlipList;
bool m_dpmsEnabled = true;
QSharedPointer<DrmDumbBuffer> m_cursor;
bool m_firstCommit = true;
bool m_pageFlipPending = false;
bool m_atomicOffPending = false;
bool m_modesetRequested = true;
struct {
Transform transform;
drmModeModeInfo mode;
DrmPlane::Transformations planeTransformations;
QPoint globalPos;
bool valid = false;
} m_lastWorkingState;
QScopedPointer<DrmDumbBuffer> m_cursor[2];
int m_cursorIndex = 0;
bool m_hasNewCursor = false;
bool m_deleted = false;
};
......
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_pipeline.h"
#include "logging.h"
#include "drm_gpu.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
#include "drm_buffer.h"
#include "cursor.h"
#include "session.h"
#include "abstract_output.h"
#include <errno.h>
namespace KWin
{
DrmPipeline::DrmPipeline(void *pageflipUserData, DrmGpu *gpu, DrmConnector *conn, DrmCrtc *crtc, DrmPlane *primaryPlane, DrmPlane *cursorPlane)
: m_pageflipUserData(pageflipUserData), m_gpu(gpu), m_connector(conn), m_crtc(crtc), m_primaryPlane(primaryPlane)
{
m_cursor.plane = cursorPlane;