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.
This commit is contained in:
parent
b68cd3110a
commit
5a22deda3b
14 changed files with 766 additions and 678 deletions
|
@ -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 ¤tMode() 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)
|
||||
drmModeModeInfo DrmCrtc::queryCurrentMode()
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include <libdrm/drm_mode.h>
|
||||
|
||||
#include "drm_gpu.h"
|
||||
#include "drm_pipeline.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -45,6 +47,7 @@ DrmOutput::~DrmOutput()
|
|||
{
|
||||
Q_ASSERT(!m_pageFlipPending);
|
||||
teardown();
|
||||
delete m_pipeline;
|
||||
}
|
||||
|
||||
RenderLoop *DrmOutput::renderLoop() const
|
||||
|
@ -59,15 +62,12 @@ void DrmOutput::teardown()
|
|||
}
|
||||
m_deleted = true;
|
||||
hideCursor();
|
||||
m_crtc->blank(this);
|
||||
|
||||
if (m_primaryPlane) {
|
||||
// TODO: when having multiple planes, also clean up these
|
||||
m_primaryPlane->setCurrent(nullptr);
|
||||
}
|
||||
|
||||
m_cursor[0].reset(nullptr);
|
||||
m_cursor[1].reset(nullptr);
|
||||
if (!m_pageFlipPending) {
|
||||
deleteLater();
|
||||
} //else will be deleted in the page flip handler
|
||||
|
@ -82,35 +82,23 @@ void DrmOutput::releaseBuffers()
|
|||
m_primaryPlane->setNext(nullptr);
|
||||
}
|
||||
|
||||
bool DrmOutput::hideCursor()
|
||||
bool DrmOutput::initCursor(const QSize &cursorSize)
|
||||
{
|
||||
return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), 0, 0, 0) == 0;
|
||||
m_cursor = QSharedPointer<DrmDumbBuffer>::create(m_gpu, cursorSize);
|
||||
if (!m_cursor->map(QImage::Format_ARGB32_Premultiplied)) {
|
||||
return false;
|
||||
}
|
||||
return updateCursor();
|
||||
}
|
||||
|
||||
bool DrmOutput::showCursor(DrmDumbBuffer *c)
|
||||
bool DrmOutput::hideCursor()
|
||||
{
|
||||
const QSize &s = c->size();
|
||||
return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0;
|
||||
return m_pipeline->setCursor(nullptr);
|
||||
}
|
||||
|
||||
bool DrmOutput::showCursor()
|
||||
{
|
||||
if (m_deleted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool ret = showCursor(m_cursor[m_cursorIndex].data());
|
||||
if (!ret) {
|
||||
qCDebug(KWIN_DRM) << "DrmOutput::showCursor(DrmDumbBuffer) failed";
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (m_hasNewCursor) {
|
||||
m_cursorIndex = (m_cursorIndex + 1) % 2;
|
||||
m_hasNewCursor = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return m_pipeline->setCursor(m_cursor);
|
||||
}
|
||||
|
||||
static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite)
|
||||
|
@ -129,32 +117,31 @@ bool DrmOutput::updateCursor()
|
|||
return false;
|
||||
}
|
||||
const Cursor *cursor = Cursors::self()->currentCursor();
|
||||
if (!cursor) {
|
||||
hideCursor();
|
||||
return true;
|
||||
}
|
||||
const QImage cursorImage = cursor->image();
|
||||
if (cursorImage.isNull()) {
|
||||
return false;
|
||||
hideCursor();
|
||||
return true;
|
||||
}
|
||||
|
||||
QImage *c = m_cursor[m_cursorIndex]->image();
|
||||
QImage *c = m_cursor->image();
|
||||
c->setDevicePixelRatio(scale());
|
||||
|
||||
if (!isCursorSpriteCompatible(c, &cursorImage)) {
|
||||
// If the cursor image is too big, fall back to rendering the software cursor.
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hasNewCursor = true;
|
||||
c->fill(Qt::transparent);
|
||||
|
||||
QPainter p;
|
||||
p.begin(c);
|
||||
p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform());
|
||||
p.drawImage(QPoint(0, 0), cursorImage);
|
||||
p.end();
|
||||
|
||||
return true;
|
||||
return m_pipeline->setCursor(m_cursor);
|
||||
}
|
||||
|
||||
void DrmOutput::moveCursor()
|
||||
bool DrmOutput::moveCursor()
|
||||
{
|
||||
Cursor *cursor = Cursors::self()->currentCursor();
|
||||
const QMatrix4x4 hotspotMatrix = logicalToNativeMatrix(cursor->rect(), scale(), transform());
|
||||
|
@ -163,89 +150,66 @@ void DrmOutput::moveCursor()
|
|||
QPoint pos = monitorMatrix.map(cursor->pos());
|
||||
pos -= hotspotMatrix.map(cursor->hotspot());
|
||||
|
||||
drmModeMoveCursor(m_gpu->fd(), m_crtc->id(), pos.x(), pos.y());
|
||||
return m_pipeline->moveCursor(pos);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static AbstractWaylandOutput::SubPixel drmSubPixelToKWinSubPixel(drmModeSubPixel subpixel)
|
||||
{
|
||||
switch (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();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::init(drmModeConnector *connector)
|
||||
bool DrmOutput::init()
|
||||
{
|
||||
if (m_gpu->atomicModeSetting() && !m_primaryPlane) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setSubPixelInternal(drmSubPixelToKWinSubPixel(connector->subpixel));
|
||||
setSubPixelInternal(m_conn->subpixel());
|
||||
setInternal(m_conn->isInternal());
|
||||
setCapabilityInternal(DrmOutput::Capability::Dpms);
|
||||
initOutputDevice(connector);
|
||||
setCapabilityInternal(Capability::Dpms);
|
||||
initOutputDevice();
|
||||
|
||||
if (!m_gpu->atomicModeSetting() && !m_crtc->blank(this)) {
|
||||
// We use legacy mode and the initial output blank failed.
|
||||
return false;
|
||||
}
|
||||
m_pipeline = new DrmPipeline(this, m_gpu, m_conn, m_crtc, m_primaryPlane, m_cursorPlane);
|
||||
updateMode(0);
|
||||
|
||||
// renderloop will be un-inhibited when updating DPMS
|
||||
m_renderLoop->inhibit();
|
||||
m_dpmsEnabled = false;
|
||||
setDpmsMode(DpmsMode::On);
|
||||
return true;
|
||||
return m_dpmsEnabled;
|
||||
}
|
||||
|
||||
void DrmOutput::initOutputDevice(drmModeConnector *connector)
|
||||
static bool checkIfEqual(_drmModeModeInfo one, _drmModeModeInfo two) {
|
||||
// for some reason a few values are 0
|
||||
// thus they're ignored here
|
||||
return one.clock == two.clock
|
||||
&& one.hdisplay == two.hdisplay
|
||||
&& one.hsync_start == two.hsync_start
|
||||
&& one.hsync_end == two.hsync_end
|
||||
&& one.htotal == two.htotal
|
||||
&& one.hskew == two.hskew
|
||||
&& one.vdisplay == two.vdisplay
|
||||
&& one.vsync_start == two.vsync_start
|
||||
&& one.vsync_end == two.vsync_end
|
||||
&& one.vtotal == two.vtotal
|
||||
&& one.vscan == two.vscan
|
||||
&& one.vrefresh == two.vrefresh;
|
||||
}
|
||||
|
||||
void DrmOutput::initOutputDevice()
|
||||
{
|
||||
// read in mode information
|
||||
QVector<Mode> modes;
|
||||
modes.reserve(connector->count_modes);
|
||||
for (int i = 0; i < connector->count_modes; ++i) {
|
||||
// TODO: in AMS here we could read and store for later every mode's blob_id
|
||||
// would simplify isCurrentMode(..) and presentAtomically(..) in case of mode set
|
||||
auto *m = &connector->modes[i];
|
||||
auto modelist = m_conn->modes();
|
||||
auto currentMode = m_crtc->queryCurrentMode();
|
||||
for (int i = 0; i < modelist.count(); i++) {
|
||||
const auto &m = modelist[i];
|
||||
|
||||
Mode mode;
|
||||
if (isCurrentMode(m)) {
|
||||
if (checkIfEqual(m.mode, currentMode)) {
|
||||
mode.flags |= ModeFlag::Current;
|
||||
m_conn->setModeIndex(i);
|
||||
}
|
||||
if (m->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
if (m.mode.type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode.flags |= ModeFlag::Preferred;
|
||||
}
|
||||
|
||||
mode.id = i;
|
||||
mode.size = QSize(m->hdisplay, m->vdisplay);
|
||||
mode.refreshRate = refreshRateForMode(m);
|
||||
mode.size = m.size;
|
||||
mode.refreshRate = m.refreshRate;
|
||||
modes << mode;
|
||||
}
|
||||
|
||||
|
@ -255,178 +219,50 @@ void DrmOutput::initOutputDevice(drmModeConnector *connector)
|
|||
m_conn->physicalSize(), modes, m_conn->edid()->raw());
|
||||
}
|
||||
|
||||
bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const
|
||||
{
|
||||
return mode->clock == m_mode.clock
|
||||
&& mode->hdisplay == m_mode.hdisplay
|
||||
&& mode->hsync_start == m_mode.hsync_start
|
||||
&& mode->hsync_end == m_mode.hsync_end
|
||||
&& mode->htotal == m_mode.htotal
|
||||
&& mode->hskew == m_mode.hskew
|
||||
&& mode->vdisplay == m_mode.vdisplay
|
||||
&& mode->vsync_start == m_mode.vsync_start
|
||||
&& mode->vsync_end == m_mode.vsync_end
|
||||
&& mode->vtotal == m_mode.vtotal
|
||||
&& mode->vscan == m_mode.vscan
|
||||
&& mode->vrefresh == m_mode.vrefresh
|
||||
&& mode->flags == m_mode.flags
|
||||
&& mode->type == m_mode.type
|
||||
&& qstrcmp(mode->name, m_mode.name) == 0;
|
||||
}
|
||||
|
||||
bool DrmOutput::initCursor(const QSize &cursorSize)
|
||||
{
|
||||
auto createCursor = [this, cursorSize] (int index) {
|
||||
m_cursor[index].reset(new DrmDumbBuffer(m_gpu, cursorSize));
|
||||
if (!m_cursor[index]->map(QImage::Format_ARGB32_Premultiplied)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!createCursor(0) || !createCursor(1)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmOutput::updateEnablement(bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
m_dpmsModePending = DpmsMode::On;
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
atomicEnable();
|
||||
} else {
|
||||
if (dpmsLegacyApply()) {
|
||||
m_backend->enableOutput(this, true);
|
||||
}
|
||||
}
|
||||
if (m_dpmsEnabled == enable) {
|
||||
m_backend->enableOutput(this, enable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_pipeline->setEnablement(enable)) {
|
||||
m_backend->enableOutput(this, enable);
|
||||
if (enable) {
|
||||
m_renderLoop->inhibit();
|
||||
} else {
|
||||
m_renderLoop->uninhibit();
|
||||
}
|
||||
m_dpmsEnabled = enable;
|
||||
} else {
|
||||
m_dpmsModePending = DpmsMode::Off;
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
atomicDisable();
|
||||
} else {
|
||||
if (dpmsLegacyApply()) {
|
||||
m_backend->enableOutput(this, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::atomicEnable()
|
||||
{
|
||||
m_modesetRequested = true;
|
||||
|
||||
if (m_atomicOffPending) {
|
||||
Q_ASSERT(m_pageFlipPending);
|
||||
m_atomicOffPending = false;
|
||||
}
|
||||
m_backend->enableOutput(this, true);
|
||||
dpmsFinishOn();
|
||||
|
||||
if (Compositor *compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::atomicDisable()
|
||||
{
|
||||
m_modesetRequested = true;
|
||||
|
||||
m_backend->enableOutput(this, false);
|
||||
m_atomicOffPending = true;
|
||||
if (!m_pageFlipPending) {
|
||||
dpmsAtomicOff();
|
||||
qCWarning(KWIN_DRM) << "Setting enablement to" << enable << "failed!" << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::setDpmsMode(DpmsMode mode)
|
||||
{
|
||||
if (!m_conn->dpms() || !isEnabled()) {
|
||||
bool newDpmsEnable = mode == DpmsMode::On;
|
||||
if (newDpmsEnable == m_dpmsEnabled) {
|
||||
qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged.";
|
||||
AbstractWaylandOutput::setDpmsModeInternal(mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == m_dpmsModePending) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_dpmsModePending = mode;
|
||||
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
m_modesetRequested = true;
|
||||
if (mode == DpmsMode::On) {
|
||||
if (m_atomicOffPending) {
|
||||
Q_ASSERT(m_pageFlipPending);
|
||||
m_atomicOffPending = false;
|
||||
if (m_pipeline->setEnablement(newDpmsEnable)) {
|
||||
m_dpmsEnabled = newDpmsEnable;
|
||||
AbstractWaylandOutput::setDpmsModeInternal(mode);
|
||||
if (m_dpmsEnabled) {
|
||||
m_renderLoop->uninhibit();
|
||||
if (Compositor *compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
dpmsFinishOn();
|
||||
} else {
|
||||
m_atomicOffPending = true;
|
||||
if (!m_pageFlipPending) {
|
||||
dpmsAtomicOff();
|
||||
}
|
||||
m_renderLoop->inhibit();
|
||||
}
|
||||
} else {
|
||||
dpmsLegacyApply();
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed!" << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::dpmsFinishOn()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On.";
|
||||
|
||||
m_backend->checkOutputsAreOn();
|
||||
m_crtc->blank(this);
|
||||
m_renderLoop->uninhibit();
|
||||
if (Compositor *compositor = Compositor::self()) {
|
||||
compositor->addRepaintFull();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::dpmsFinishOff()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off.";
|
||||
|
||||
if (isEnabled()) {
|
||||
m_backend->createDpmsFilter();
|
||||
}
|
||||
m_renderLoop->inhibit();
|
||||
}
|
||||
|
||||
static uint64_t kwinDpmsModeToDrmDpmsMode(AbstractWaylandOutput::DpmsMode dpmsMode)
|
||||
{
|
||||
switch (dpmsMode) {
|
||||
case AbstractWaylandOutput::DpmsMode::On:
|
||||
return DRM_MODE_DPMS_ON;
|
||||
case AbstractWaylandOutput::DpmsMode::Standby:
|
||||
return DRM_MODE_DPMS_STANDBY;
|
||||
case AbstractWaylandOutput::DpmsMode::Suspend:
|
||||
return DRM_MODE_DPMS_SUSPEND;
|
||||
case AbstractWaylandOutput::DpmsMode::Off:
|
||||
return DRM_MODE_DPMS_OFF;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::dpmsLegacyApply()
|
||||
{
|
||||
if (drmModeConnectorSetProperty(m_gpu->fd(), m_conn->id(),
|
||||
m_conn->dpms()->propId(),
|
||||
kwinDpmsModeToDrmDpmsMode(m_dpmsModePending)) < 0) {
|
||||
m_dpmsModePending = dpmsMode();
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed";
|
||||
return false;
|
||||
}
|
||||
if (m_dpmsModePending == DpmsMode::On) {
|
||||
dpmsFinishOn();
|
||||
} else {
|
||||
dpmsFinishOff();
|
||||
}
|
||||
setDpmsModeInternal(m_dpmsModePending);
|
||||
return true;
|
||||
}
|
||||
|
||||
DrmPlane::Transformations outputToPlaneTransform(DrmOutput::Transform transform)
|
||||
{
|
||||
using OutTrans = DrmOutput::Transform;
|
||||
|
@ -472,7 +308,7 @@ void DrmOutput::updateTransform(Transform transform)
|
|||
|| transform == Transform::Flipped90
|
||||
|| transform == Transform::Rotated270
|
||||
|| transform == Transform::Flipped270;
|
||||
|
||||
const auto ¤tTransform = m_primaryPlane->transformation();
|
||||
if (!qEnvironmentVariableIsSet("KWIN_DRM_SW_ROTATIONS_ONLY") &&
|
||||
(m_primaryPlane->supportedTransformations() & planeTransform) &&
|
||||
!isPortrait) {
|
||||
|
@ -480,27 +316,28 @@ void DrmOutput::updateTransform(Transform transform)
|
|||
} else {
|
||||
m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate0);
|
||||
}
|
||||
if (!m_pipeline->test()) {
|
||||
m_primaryPlane->setTransformation(currentTransform);
|
||||
}
|
||||
}
|
||||
m_modesetRequested = true;
|
||||
|
||||
// show cursor only if is enabled, i.e if pointer device is presentP
|
||||
if (!m_backend->isCursorHidden() && !m_backend->usesSoftwareCursor()) {
|
||||
// the cursor might need to get rotated
|
||||
updateCursor();
|
||||
showCursor();
|
||||
updateCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate)
|
||||
{
|
||||
if (m_mode.hdisplay == width && m_mode.vdisplay == height && m_mode.vrefresh == refreshRate) {
|
||||
if (m_conn->currentMode().size == QSize(width, height) && m_conn->currentMode().refreshRate == refreshRate) {
|
||||
return;
|
||||
}
|
||||
// try to find a fitting mode
|
||||
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnectorCurrent(m_gpu->fd(), m_conn->id()));
|
||||
for (int i = 0; i < connector->count_modes; i++) {
|
||||
auto mode = connector->modes[i];
|
||||
if (mode.hdisplay == width && mode.vdisplay == height && mode.vrefresh == refreshRate) {
|
||||
auto modelist = m_conn->modes();
|
||||
for (int i = 0; i < modelist.count(); i++) {
|
||||
if (modelist[i].size == QSize(width, height) && modelist[i].refreshRate == refreshRate) {
|
||||
updateMode(i);
|
||||
return;
|
||||
}
|
||||
|
@ -511,339 +348,69 @@ void DrmOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate
|
|||
|
||||
void DrmOutput::updateMode(int modeIndex)
|
||||
{
|
||||
// get all modes on the connector
|
||||
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_gpu->fd(), m_conn->id()));
|
||||
if (connector->count_modes <= modeIndex) {
|
||||
// TODO: error?
|
||||
const auto &modelist = m_conn->modes();
|
||||
if (modeIndex >= modelist.count()) {
|
||||
qCWarning(KWIN_DRM, "Mode %d for output %s doesn't exist", modeIndex, qPrintable(uuid().toString()));
|
||||
return;
|
||||
}
|
||||
if (isCurrentMode(&connector->modes[modeIndex])) {
|
||||
// nothing to do
|
||||
return;
|
||||
const auto &mode = modelist[modeIndex];
|
||||
QSize srcSize = mode.size;
|
||||
if (hardwareTransforms() &&
|
||||
(transform() == Transform::Rotated270
|
||||
|| transform() == Transform::Rotated90
|
||||
|| transform() == Transform::Flipped90
|
||||
|| transform() == Transform::Flipped270)) {
|
||||
srcSize = srcSize.transposed();
|
||||
}
|
||||
m_mode = connector->modes[modeIndex];
|
||||
m_modesetRequested = true;
|
||||
setCurrentModeInternal();
|
||||
}
|
||||
|
||||
void DrmOutput::setCurrentModeInternal()
|
||||
{
|
||||
AbstractWaylandOutput::setCurrentModeInternal(QSize(m_mode.hdisplay, m_mode.vdisplay),
|
||||
refreshRateForMode(&m_mode));
|
||||
if (m_pipeline->modeset(srcSize, mode.mode)) {
|
||||
m_conn->setModeIndex(modeIndex);
|
||||
qCDebug(KWIN_DRM, "Modeset successful on %s (%dx%d@%f)", qPrintable(name()), modeSize().width(), modeSize().height(), refreshRate() * 0.001);
|
||||
} else {
|
||||
qCWarning(KWIN_DRM, "Modeset failed on %s (%dx%d@%f)! %s", qPrintable(name()), modeSize().width(), modeSize().height(), refreshRate() * 0.001, strerror(errno));
|
||||
}
|
||||
// this must be done even if it fails, for the initial modeset
|
||||
const auto ¤tMode = m_conn->currentMode();
|
||||
AbstractWaylandOutput::setCurrentModeInternal(currentMode.size, currentMode.refreshRate);
|
||||
m_renderLoop->setRefreshRate(currentMode.refreshRate);
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlipped()
|
||||
{
|
||||
// In legacy mode we might get a page flip through a blank.
|
||||
Q_ASSERT(m_pageFlipPending || !m_gpu->atomicModeSetting());
|
||||
m_pageFlipPending = false;
|
||||
|
||||
if (m_deleted) {
|
||||
qCDebug(KWIN_DRM) << "delete-pageflip";
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_crtc) {
|
||||
return;
|
||||
}
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
for (DrmPlane *p : m_nextPlanesFlipList) {
|
||||
p->flipBuffer();
|
||||
m_primaryPlane->flipBuffer();
|
||||
if (m_cursorPlane) {
|
||||
m_cursorPlane->flipBuffer();
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
} else {
|
||||
m_crtc->flipBuffer();
|
||||
}
|
||||
|
||||
if (m_atomicOffPending) {
|
||||
dpmsAtomicOff();
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
if (!buffer || buffer->bufferId() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (m_dpmsModePending != DpmsMode::On) {
|
||||
return false;
|
||||
}
|
||||
return m_gpu->atomicModeSetting() ? presentAtomically(buffer) : presentLegacy(buffer);
|
||||
}
|
||||
|
||||
bool DrmOutput::dpmsAtomicOff()
|
||||
{
|
||||
m_atomicOffPending = false;
|
||||
|
||||
// TODO: With multiple planes: deactivate all of them here
|
||||
m_primaryPlane->setNext(nullptr);
|
||||
m_nextPlanesFlipList << m_primaryPlane;
|
||||
|
||||
if (!doAtomicCommit(AtomicCommitMode::Test)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic test commit to Dpms Off failed. Aborting.";
|
||||
return false;
|
||||
}
|
||||
if (!doAtomicCommit(AtomicCommitMode::Real)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic commit to Dpms Off failed. This should have never happened! Aborting.";
|
||||
return false;
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
dpmsFinishOff();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmOutput::presentAtomically(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
if (!m_backend->session()->isActive()) {
|
||||
qCWarning(KWIN_DRM) << "Refusing to present output because session is inactive";
|
||||
qCWarning(KWIN_DRM) << "session not active!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_pageFlipPending) {
|
||||
qCWarning(KWIN_DRM) << "Page not yet flipped.";
|
||||
qCWarning(KWIN_DRM) << "page not flipped yet!";
|
||||
return false;
|
||||
}
|
||||
|
||||
#if HAVE_EGL_STREAMS
|
||||
if (m_gpu->useEglStreams() && !m_modesetRequested) {
|
||||
// EglStreamBackend queues normal page flips through EGL,
|
||||
// modesets are still performed through DRM-KMS
|
||||
if (m_pipeline->present(buffer)) {
|
||||
m_pageFlipPending = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_primaryPlane->setNext(buffer);
|
||||
m_nextPlanesFlipList << m_primaryPlane;
|
||||
|
||||
if (!doAtomicCommit(AtomicCommitMode::Test)) {
|
||||
//TODO: When we use planes for layered rendering, fallback to renderer instead. Also for direct scanout?
|
||||
//TODO: Probably should undo setNext and reset the flip list
|
||||
qCDebug(KWIN_DRM) << "Atomic test commit failed. Aborting present.";
|
||||
// go back to previous state
|
||||
if (m_lastWorkingState.valid) {
|
||||
m_mode = m_lastWorkingState.mode;
|
||||
setTransformInternal(m_lastWorkingState.transform);
|
||||
setGlobalPos(m_lastWorkingState.globalPos);
|
||||
if (m_primaryPlane) {
|
||||
m_primaryPlane->setTransformation(m_lastWorkingState.planeTransformations);
|
||||
}
|
||||
m_modesetRequested = true;
|
||||
if (!m_backend->isCursorHidden()) {
|
||||
// the cursor might need to get rotated
|
||||
updateCursor();
|
||||
showCursor();
|
||||
}
|
||||
setCurrentModeInternal();
|
||||
emit screens()->changed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const bool wasModeset = m_modesetRequested;
|
||||
if (!doAtomicCommit(AtomicCommitMode::Real)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic commit failed. This should have never happened! Aborting present.";
|
||||
//TODO: Probably should undo setNext and reset the flip list
|
||||
return false;
|
||||
}
|
||||
if (wasModeset) {
|
||||
// store current mode set as new good state
|
||||
m_lastWorkingState.mode = m_mode;
|
||||
m_lastWorkingState.transform = transform();
|
||||
m_lastWorkingState.globalPos = globalPos();
|
||||
if (m_primaryPlane) {
|
||||
m_lastWorkingState.planeTransformations = m_primaryPlane->transformation();
|
||||
}
|
||||
m_lastWorkingState.valid = true;
|
||||
m_renderLoop->setRefreshRate(refreshRateForMode(&m_mode));
|
||||
}
|
||||
m_pageFlipPending = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmOutput::presentLegacy(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
if (m_crtc->next()) {
|
||||
return false;
|
||||
}
|
||||
if (!m_backend->session()->isActive()) {
|
||||
m_crtc->setNext(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do we need to set a new mode first?
|
||||
if (!m_crtc->current() || m_crtc->current()->needsModeChange(buffer.get())) {
|
||||
if (!setModeLegacy(buffer.get())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const bool ok = drmModePageFlip(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
|
||||
if (ok) {
|
||||
m_crtc->setNext(buffer);
|
||||
m_pageFlipPending = true;
|
||||
} else {
|
||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
|
||||
{
|
||||
uint32_t connId = m_conn->id();
|
||||
if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) {
|
||||
return true;
|
||||
} else {
|
||||
qCWarning(KWIN_DRM) << "Mode setting failed";
|
||||
qCCritical(KWIN_DRM) << "present failed! This never should've happened!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
|
||||
{
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
|
||||
auto errorHandler = [this, mode, req] () {
|
||||
if (mode == AtomicCommitMode::Test) {
|
||||
// TODO: when we later test overlay planes, make sure we change only the right stuff back
|
||||
}
|
||||
if (req) {
|
||||
drmModeAtomicFree(req);
|
||||
}
|
||||
|
||||
if (dpmsMode() != m_dpmsModePending) {
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed";
|
||||
m_dpmsModePending = dpmsMode();
|
||||
if (dpmsMode() != DpmsMode::On) {
|
||||
dpmsFinishOff();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: see above, rework later for overlay planes!
|
||||
for (DrmPlane *p : m_nextPlanesFlipList) {
|
||||
p->setNext(nullptr);
|
||||
}
|
||||
m_nextPlanesFlipList.clear();
|
||||
|
||||
};
|
||||
|
||||
if (!req) {
|
||||
qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
// Do we need to set a new mode?
|
||||
if (m_modesetRequested) {
|
||||
if (m_dpmsModePending == DpmsMode::On) {
|
||||
if (drmModeCreatePropertyBlob(m_gpu->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Failed to create property blob";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!atomicReqModesetPopulate(req, m_dpmsModePending == DpmsMode::On)){
|
||||
qCWarning(KWIN_DRM) << "Failed to populate Atomic Modeset";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
}
|
||||
|
||||
if (mode == AtomicCommitMode::Real) {
|
||||
if (m_dpmsModePending == DpmsMode::On) {
|
||||
if (!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) {
|
||||
// TODO: Evaluating this condition should only be necessary, as long as we expect older kernels than 4.10.
|
||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||
}
|
||||
|
||||
#if HAVE_EGL_STREAMS
|
||||
if (!m_gpu->useEglStreams())
|
||||
// EglStreamBackend uses the NV_output_drm_flip_event EGL extension
|
||||
// to register the flip event through eglStreamConsumerAcquireAttribNV
|
||||
#endif
|
||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||
}
|
||||
} else {
|
||||
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
// TODO: Make sure when we use more than one plane at a time, that we go through this list in the right order.
|
||||
for (int i = m_nextPlanesFlipList.size() - 1; 0 <= i; i-- ) {
|
||||
DrmPlane *p = m_nextPlanesFlipList[i];
|
||||
ret &= p->atomicPopulate(req);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
qCWarning(KWIN_DRM) << "Failed to populate atomic planes. Abort atomic commit!";
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drmModeAtomicCommit(m_gpu->fd(), req, flags, this)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic request failed to commit: " << strerror(errno);
|
||||
errorHandler();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode == AtomicCommitMode::Real && (flags & DRM_MODE_ATOMIC_ALLOW_MODESET)) {
|
||||
qCDebug(KWIN_DRM) << "Atomic Modeset successful.";
|
||||
m_modesetRequested = false;
|
||||
setDpmsModeInternal(m_dpmsModePending);
|
||||
}
|
||||
|
||||
drmModeAtomicFree(req);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmOutput::atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
const QSize mSize = modeSize();
|
||||
const QSize bufferSize = m_primaryPlane->next() ? m_primaryPlane->next()->size() : pixelSize();
|
||||
const QSize sourceSize = hardwareTransforms() ? bufferSize : mSize;
|
||||
QRect targetRect = QRect(QPoint(0, 0), mSize);
|
||||
if (mSize != sourceSize) {
|
||||
targetRect.setSize(sourceSize.scaled(mSize, Qt::AspectRatioMode::KeepAspectRatio));
|
||||
targetRect.setX((mSize.width() - targetRect.width()) / 2);
|
||||
targetRect.setY((mSize.height() - targetRect.height()) / 2);
|
||||
}
|
||||
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcX, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcY, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcW, sourceSize.width() << 16);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcH, sourceSize.height() << 16);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcX, targetRect.x());
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcY, targetRect.y());
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcW, targetRect.width());
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcH, targetRect.height());
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcId, m_crtc->id());
|
||||
} else {
|
||||
m_primaryPlane->setCurrent(nullptr);
|
||||
m_primaryPlane->setNext(nullptr);
|
||||
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcX, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcY, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcW, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::SrcH, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcX, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcY, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcW, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcH, 0);
|
||||
m_primaryPlane->setValue(DrmPlane::PropertyIndex::CrtcId, 0);
|
||||
}
|
||||
m_conn->setValue(DrmConnector::PropertyIndex::CrtcId, enable ? m_crtc->id() : 0);
|
||||
m_crtc->setValue(DrmCrtc::PropertyIndex::ModeId, enable ? m_blobId : 0);
|
||||
m_crtc->setValue(DrmCrtc::PropertyIndex::Active, enable);
|
||||
|
||||
bool ret = true;
|
||||
ret &= m_conn->atomicPopulate(req);
|
||||
ret &= m_crtc->atomicPopulate(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int DrmOutput::gammaRampSize() const
|
||||
{
|
||||
return m_crtc->gammaRampSize();
|
||||
|
@ -851,7 +418,7 @@ int DrmOutput::gammaRampSize() const
|
|||
|
||||
bool DrmOutput::setGammaRamp(const GammaRamp &gamma)
|
||||
{
|
||||
return m_crtc->setGammaRamp(gamma);
|
||||
return m_pipeline->setGammaRamp(gamma);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
void initOutputDevice();
|
||||
|
||||
enum class AtomicCommitMode {
|
||||
Test,
|
||||
Real
|
||||
};
|
||||
bool doAtomicCommit(AtomicCommitMode mode);
|
||||
|
||||
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;
|
||||
DrmConnector *m_conn = nullptr;
|
||||
DrmCrtc *m_crtc = nullptr;
|
||||
bool m_lastGbm = false;
|
||||
drmModeModeInfo m_mode;
|
||||
DpmsMode m_dpmsModePending = DpmsMode::On;
|
||||
RenderLoop *m_renderLoop;
|
||||
|
||||
uint32_t m_blobId = 0;
|
||||
DrmPlane *m_primaryPlane = nullptr;
|
||||
DrmPlane *m_cursorPlane = nullptr;
|
||||
QVector<DrmPlane*> m_nextPlanesFlipList;
|
||||
bool m_pageFlipPending = false;
|
||||
bool m_atomicOffPending = false;
|
||||
bool m_modesetRequested = true;
|
||||
DrmConnector *m_conn = nullptr;
|
||||
DrmCrtc *m_crtc = nullptr;
|
||||
|
||||
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;
|
||||
RenderLoop *m_renderLoop;
|
||||
DrmPipeline *m_pipeline = nullptr;
|
||||
|
||||
bool m_dpmsEnabled = true;
|
||||
QSharedPointer<DrmDumbBuffer> m_cursor;
|
||||
bool m_firstCommit = true;
|
||||
bool m_pageFlipPending = false;
|
||||
bool m_deleted = false;
|
||||
};
|
||||
|
||||
|
|
337
src/plugins/platforms/drm/drm_pipeline.cpp
Normal file
337
src/plugins/platforms/drm/drm_pipeline.cpp
Normal file
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
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;
|
||||
const auto &mode = m_connector->currentMode();
|
||||
m_mode.mode = mode.mode;
|
||||
m_mode.sourceSize = mode.size;
|
||||
m_mode.enabled = true;
|
||||
m_mode.blobId = -1;// = keep mode
|
||||
}
|
||||
|
||||
DrmPipeline::~DrmPipeline()
|
||||
{
|
||||
if (m_mode.blobId > 0) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), m_mode.blobId);
|
||||
}
|
||||
if (m_gamma.blobId > 0) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), m_gamma.blobId);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
if (m_gpu->useEglStreams() && !m_mode.changed && !m_gamma.changed) {
|
||||
// EglStreamBackend queues normal page flips through EGL,
|
||||
// modesets etc are performed through DRM-KMS
|
||||
return true;
|
||||
}
|
||||
setPrimaryBuffer(buffer);
|
||||
bool result = m_gpu->atomicModeSetting() ? atomicCommit(false) : presentLegacy();
|
||||
if (result) {
|
||||
m_mode.changed = false;
|
||||
m_gamma.changed = false;
|
||||
} else {
|
||||
qCWarning(KWIN_DRM) << "Present failed!" << strerror(errno);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DrmPipeline::test()
|
||||
{
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
return atomicCommit(true);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::atomicCommit(bool testOnly)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
if (!req) {
|
||||
qCDebug(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
if (testOnly) {
|
||||
checkTestBuffer();
|
||||
}
|
||||
m_primaryPlane->setNext(testOnly ? m_testBuffer : m_primaryBuffer);
|
||||
for (int i = 0; i < m_overlayPlanes.count(); i++) {
|
||||
m_overlayPlanes[i]->setNext(testOnly ? m_testBuffer : m_overlayBuffers[i]);
|
||||
}
|
||||
bool result = populateAtomicValues(req, flags);
|
||||
if (result && drmModeAtomicCommit(m_gpu->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, m_pageflipUserData)) {
|
||||
result = false;
|
||||
}
|
||||
if (!testOnly && result && drmModeAtomicCommit(m_gpu->fd(), req, flags, m_pageflipUserData)) {
|
||||
qCCritical(KWIN_DRM) << "Atomic commit failed! This never should've happened!" << strerror(errno);
|
||||
result = false;
|
||||
}
|
||||
drmModeAtomicFree(req);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DrmPipeline::populateAtomicValues(drmModeAtomicReq* req, uint32_t &flags)
|
||||
{
|
||||
if (!m_gpu->useEglStreams() && m_mode.enabled) {
|
||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||
}
|
||||
if (m_mode.changed) {
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
} else {
|
||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||
}
|
||||
|
||||
m_connector->setValue(DrmConnector::PropertyIndex::CrtcId, m_mode.enabled ? m_crtc->id() : 0);
|
||||
if (!m_connector->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic populate for connector failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_crtc->setValue(DrmCrtc::PropertyIndex::ModeId, m_mode.enabled ? m_mode.blobId : 0);
|
||||
m_crtc->setValue(DrmCrtc::PropertyIndex::Active, m_mode.enabled ? 1 : 0);
|
||||
m_crtc->setValue(DrmCrtc::PropertyIndex::Gamma_LUT, m_mode.enabled ? m_gamma.blobId : 0);
|
||||
if (!m_crtc->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic populate for crtc failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
QSize modesize = QSize(m_mode.mode.hdisplay, m_mode.mode.vdisplay);
|
||||
m_primaryPlane->setScaled(m_primaryPlane->next() ? m_primaryPlane->next()->size() : modesize, modesize, m_crtc->id(), m_mode.enabled);
|
||||
if (!m_primaryPlane->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic populate for primary plane failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_cursor.plane) {
|
||||
const QSize &size = m_cursor.buffer ? m_cursor.buffer->size() : QSize(0, 0);
|
||||
m_cursor.plane->setNext(m_cursor.buffer);
|
||||
m_cursor.plane->set(size, m_cursor.pos, size, m_crtc->id(), m_mode.enabled && m_cursor.buffer != nullptr);
|
||||
if (!m_cursor.plane->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic populate for cursor plane failed!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &overlay : qAsConst(m_overlayPlanes)) {
|
||||
overlay->setScaled(overlay->next() ? overlay->next()->size() : modesize, modesize, m_crtc->id(), m_mode.enabled);
|
||||
if (!overlay->atomicPopulate(req)) {
|
||||
qCWarning(KWIN_DRM) << "Atomic populate for overlay plane failed!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::presentLegacy()
|
||||
{
|
||||
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
|
||||
m_crtc->setNext(m_primaryBuffer);
|
||||
if (drmModePageFlip(m_gpu->fd(), m_crtc->id(), m_primaryBuffer ? m_primaryBuffer->bufferId() : 0, flags, m_pageflipUserData)) {
|
||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::addOverlayPlane(DrmPlane *plane)
|
||||
{
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
m_overlayPlanes << plane;
|
||||
if (!atomicCommit(true)) {
|
||||
qCWarning(KWIN_DRM) << "Could not add overlay plane!";
|
||||
m_overlayPlanes.removeOne(plane);
|
||||
return false;
|
||||
}
|
||||
m_overlayBuffers << nullptr;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmPipeline::setPrimaryBuffer(const QSharedPointer<DrmBuffer> &buffer)
|
||||
{
|
||||
m_primaryBuffer = buffer;
|
||||
}
|
||||
|
||||
bool DrmPipeline::modeset(QSize source, drmModeModeInfo mode)
|
||||
{
|
||||
auto oldMode = m_mode;
|
||||
m_mode.sourceSize = source;
|
||||
m_mode.mode = mode;
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
m_mode.changed = true;
|
||||
if (drmModeCreatePropertyBlob(m_gpu->fd(), &mode, sizeof(drmModeModeInfo), &m_mode.blobId)) {
|
||||
qCWarning(KWIN_DRM) << "Failed to create property blob for mode!" << strerror(errno);
|
||||
m_mode = oldMode;
|
||||
return false;
|
||||
}
|
||||
if (!atomicCommit(true)) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), m_mode.blobId);
|
||||
m_mode = oldMode;
|
||||
return false;
|
||||
}
|
||||
if (oldMode.blobId) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), oldMode.blobId);
|
||||
}
|
||||
} else {
|
||||
checkTestBuffer();
|
||||
uint32_t connId = m_connector->id();
|
||||
if (drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), m_testBuffer->bufferId(), 0, 0, &connId, 1, &mode)) {
|
||||
m_mode = oldMode;
|
||||
qCWarning(KWIN_DRM) << "Modeset failed:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer)
|
||||
{
|
||||
auto oldCursor = m_cursor;
|
||||
m_cursor.buffer = buffer;
|
||||
if (m_gpu->atomicModeSetting() && m_cursor.plane) {
|
||||
if (!atomicCommit(true)) {
|
||||
qCWarning(KWIN_DRM) << "Could not set cursor!";
|
||||
m_cursor = oldCursor;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const QSize &s = buffer ? buffer->size() : QSize(0, 0);
|
||||
if (drmModeSetCursor(m_gpu->fd(), m_crtc->id(), m_cursor.buffer ? m_cursor.buffer->handle() : 0, s.width(), s.height())) {
|
||||
m_cursor = oldCursor;
|
||||
qCWarning(KWIN_DRM) << "Could not set cursor:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::moveCursor(QPoint pos)
|
||||
{
|
||||
auto cursor = m_cursor;
|
||||
m_cursor.pos = pos;
|
||||
if (m_gpu->atomicModeSetting() && m_cursor.plane) {
|
||||
if (!atomicCommit(true)) {
|
||||
m_cursor = cursor;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (drmModeMoveCursor(m_gpu->fd(), m_crtc->id(), m_cursor.pos.x(), m_cursor.pos.y())) {
|
||||
m_cursor = cursor;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::setEnablement(bool enabled)
|
||||
{
|
||||
auto oldMode = m_mode;
|
||||
m_mode.enabled = enabled;
|
||||
setPrimaryBuffer(nullptr);
|
||||
if (m_gpu->atomicModeSetting()) {
|
||||
m_mode.changed = true;
|
||||
// immediately commit if disabling as there will be no present
|
||||
if (!atomicCommit(enabled)) {
|
||||
m_mode = oldMode;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!m_connector->dpms()) {
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed: dpms property missing!";
|
||||
m_mode = oldMode;
|
||||
return false;
|
||||
}
|
||||
if (drmModeConnectorSetProperty(m_gpu->fd(), m_connector->id(), m_connector->dpms()->propId(), m_mode.enabled ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF) < 0) {
|
||||
qCWarning(KWIN_DRM) << "Setting DPMS failed";
|
||||
m_mode = oldMode;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DrmPipeline::setGammaRamp(const GammaRamp &ramp)
|
||||
{
|
||||
// Apparently there are old Intel iGPUs that don't have full support
|
||||
// for setting the gamma ramp with AMS -> fall back to legacy in that case
|
||||
if (m_gpu->atomicModeSetting() && m_crtc->hasGammaProp()) {
|
||||
auto oldGamma = m_gamma;
|
||||
m_gamma.changed = true;
|
||||
|
||||
struct drm_color_lut *gamma = new drm_color_lut[ramp.size()];
|
||||
for (uint32_t i = 0; i < ramp.size(); i++) {
|
||||
gamma[i].red = ramp.red()[i];
|
||||
gamma[i].green = ramp.green()[i];
|
||||
gamma[i].blue = ramp.blue()[i];
|
||||
}
|
||||
bool result = !drmModeCreatePropertyBlob(m_gpu->fd(), gamma, ramp.size() * sizeof(struct drm_color_lut), &m_gamma.blobId);
|
||||
delete[] gamma;
|
||||
if (!result) {
|
||||
qCWarning(KWIN_DRM) << "Could not create gamma LUT property blob" << strerror(errno);
|
||||
m_gamma = oldGamma;
|
||||
return false;
|
||||
}
|
||||
if (!atomicCommit(true)) {
|
||||
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), m_gamma.blobId);
|
||||
m_gamma = oldGamma;
|
||||
return false;
|
||||
}
|
||||
if (oldGamma.blobId) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), oldGamma.blobId);
|
||||
}
|
||||
} else {
|
||||
uint16_t *red = const_cast<uint16_t*>(ramp.red());
|
||||
uint16_t *green = const_cast<uint16_t*>(ramp.green());
|
||||
uint16_t *blue = const_cast<uint16_t*>(ramp.blue());
|
||||
bool result = !drmModeCrtcSetGamma(m_gpu->fd(), m_crtc->id(), ramp.size(), red, green, blue);
|
||||
if (!result) {
|
||||
qCWarning(KWIN_DRM) << "setting gamma failed!" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmPipeline::checkTestBuffer()
|
||||
{
|
||||
if (m_testBuffer && m_testBuffer->size() == m_mode.sourceSize) {
|
||||
return;
|
||||
}
|
||||
m_testBuffer = m_gpu->createTestbuffer(m_mode.sourceSize);
|
||||
}
|
||||
|
||||
}
|
94
src/plugins/platforms/drm/drm_pipeline.h
Normal file
94
src/plugins/platforms/drm/drm_pipeline.h
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
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
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
namespace KWin {
|
||||
|
||||
class DrmGpu;
|
||||
class DrmConnector;
|
||||
class DrmCrtc;
|
||||
class DrmPlane;
|
||||
class DrmBuffer;
|
||||
class DrmDumbBuffer;
|
||||
class GammaRamp;
|
||||
|
||||
class DrmPipeline
|
||||
{
|
||||
public:
|
||||
DrmPipeline(void *pageflipUserData, DrmGpu *gpu, DrmConnector *conn, DrmCrtc *crtc, DrmPlane *primaryPlane, DrmPlane *cursorPlane);
|
||||
~DrmPipeline();
|
||||
|
||||
/**
|
||||
* tests the pending commit first and commits it if the test passes
|
||||
* if the test fails, there is a guarantee for no lasting changes
|
||||
*/
|
||||
bool present(const QSharedPointer<DrmBuffer> &buffer);
|
||||
|
||||
/**
|
||||
* tests the pending commit
|
||||
* always returns true in legacy mode!
|
||||
*/
|
||||
bool test();
|
||||
|
||||
bool modeset(QSize source, drmModeModeInfo mode);
|
||||
bool setCursor(const QSharedPointer<DrmDumbBuffer> &buffer);
|
||||
bool setEnablement(bool enable);
|
||||
bool setGammaRamp(const GammaRamp &ramp);
|
||||
|
||||
void setPrimaryBuffer(const QSharedPointer<DrmBuffer> &buffer);
|
||||
bool moveCursor(QPoint pos);
|
||||
|
||||
bool addOverlayPlane(DrmPlane *plane);
|
||||
|
||||
private:
|
||||
bool atomicCommit(bool testOnly);
|
||||
bool populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);
|
||||
bool presentLegacy();
|
||||
void checkTestBuffer();
|
||||
|
||||
void *m_pageflipUserData = nullptr;
|
||||
DrmGpu *m_gpu = nullptr;
|
||||
DrmConnector *m_connector = nullptr;
|
||||
DrmCrtc *m_crtc = nullptr;
|
||||
|
||||
DrmPlane *m_primaryPlane = nullptr;
|
||||
QSharedPointer<DrmBuffer> m_primaryBuffer;
|
||||
QSharedPointer<DrmBuffer> m_testBuffer;
|
||||
|
||||
QVector<DrmPlane*> m_overlayPlanes;
|
||||
QVector<QSharedPointer<DrmBuffer>> m_overlayBuffers;
|
||||
|
||||
struct {
|
||||
bool changed = false;
|
||||
bool enabled = true;
|
||||
QSize sourceSize = QSize(-1, -1);
|
||||
drmModeModeInfo mode;
|
||||
uint32_t blobId = 0;
|
||||
} m_mode;
|
||||
struct {
|
||||
DrmPlane *plane = nullptr;
|
||||
QPoint pos = QPoint(100, 100);
|
||||
QSharedPointer<DrmDumbBuffer> buffer;
|
||||
} m_cursor;
|
||||
struct {
|
||||
bool changed = false;
|
||||
uint32_t blobId = 0;
|
||||
} m_gamma;
|
||||
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue