backends/drm: Use DrmOutput to represent non-desktop outputs too

The main motivation behind this change is to make the drm backend
multi-purpose. That's it, to make it suitable for implementing all kinds
of compositors. At the moment, there's an artificial split between
"desktop" and "non-desktop" outputs, i.e. VR headsets, which stands in
the way of that and moving the remaining wayland code out of the drm
backend for better layering and architecture.
This commit is contained in:
Vlad Zahorodnii 2022-07-11 12:08:39 +03:00
parent 1dddf271d2
commit 8dabeb4709
11 changed files with 96 additions and 160 deletions

View file

@ -17,7 +17,6 @@ set(DRM_SOURCES
drm_pipeline_legacy.cpp
drm_abstract_output.cpp
drm_virtual_output.cpp
drm_lease_output.cpp
drm_qpainter_layer.cpp
egl_gbm_backend.cpp
egl_gbm_layer.cpp

View file

@ -13,7 +13,6 @@
#include "abstract_egl_backend.h"
#include "drm_backend.h"
#include "drm_layer.h"
#include "drm_lease_output.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
@ -116,9 +115,9 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device
DrmGpu::~DrmGpu()
{
const auto leaseOutputs = m_leaseOutputs;
const auto leaseOutputs = m_nonDesktopOutputs;
for (const auto &output : leaseOutputs) {
removeLeaseOutput(output);
removeNonDesktopOutput(output);
}
delete m_leaseDevice;
waitIdle();
@ -232,7 +231,7 @@ bool DrmGpu::updateOutputs()
// In principle these things are supposed to be detected through the wayland protocol.
// In practice SteamVR doesn't always behave correctly
auto lessees = drmModeListLessees(m_fd);
for (const auto &leaseOutput : qAsConst(m_leaseOutputs)) {
for (const auto &leaseOutput : qAsConst(m_nonDesktopOutputs)) {
if (leaseOutput->lease()) {
bool leaseActive = false;
for (uint i = 0; i < lessees->count; i++) {
@ -271,24 +270,24 @@ bool DrmGpu::updateOutputs()
for (auto it = m_connectors.begin(); it != m_connectors.end();) {
DrmConnector *conn = it->get();
const auto output = findOutput(conn->id());
const auto leaseOutput = findLeaseOutput(conn->id());
const auto nonDesktopOutput = findNonDesktopOutput(conn->id());
const bool stillExists = existing.contains(conn);
if (!stillExists || !conn->isConnected()) {
if (output) {
removeOutput(output);
} else if (leaseOutput) {
removeLeaseOutput(leaseOutput);
} else if (nonDesktopOutput) {
removeNonDesktopOutput(nonDesktopOutput);
}
conn->disable();
} else if (!output && !leaseOutput) {
} else if (!output && !nonDesktopOutput) {
qCDebug(KWIN_DRM, "New %soutput on GPU %s: %s", conn->isNonDesktop() ? "non-desktop " : "", qPrintable(m_devNode), qPrintable(conn->modelName()));
const auto pipeline = conn->pipeline();
m_pipelines << pipeline;
if (conn->isNonDesktop()) {
auto leaseOutput = new DrmLeaseOutput(pipeline, m_leaseDevice);
m_leaseOutputs << leaseOutput;
auto output = new DrmOutput(pipeline, m_leaseDevice);
m_nonDesktopOutputs << output;
} else {
auto output = new DrmOutput(pipeline);
auto output = new DrmOutput(pipeline, m_leaseDevice);
m_drmOutputs << output;
m_outputs << output;
addedOutputs << output;
@ -416,7 +415,7 @@ DrmPipeline::Error DrmGpu::testPendingConfiguration()
QVector<DrmCrtc *> crtcs;
// only change resources that aren't currently leased away
for (const auto &conn : m_connectors) {
bool isLeased = std::any_of(m_leaseOutputs.cbegin(), m_leaseOutputs.cend(), [&conn](const auto output) {
bool isLeased = std::any_of(m_nonDesktopOutputs.cbegin(), m_nonDesktopOutputs.cend(), [&conn](const auto output) {
return output->lease() && output->pipeline()->connector() == conn.get();
});
if (!isLeased) {
@ -424,7 +423,7 @@ DrmPipeline::Error DrmGpu::testPendingConfiguration()
}
}
for (const auto &crtc : m_crtcs) {
bool isLeased = std::any_of(m_leaseOutputs.cbegin(), m_leaseOutputs.cend(), [&crtc](const auto output) {
bool isLeased = std::any_of(m_nonDesktopOutputs.cbegin(), m_nonDesktopOutputs.cend(), [&crtc](const auto output) {
return output->lease() && output->pipeline()->crtc() == crtc.get();
});
if (!isLeased) {
@ -624,12 +623,12 @@ void DrmGpu::removeVirtualOutput(DrmVirtualOutput *output)
}
}
DrmLeaseOutput *DrmGpu::findLeaseOutput(quint32 connector)
DrmOutput *DrmGpu::findNonDesktopOutput(quint32 connector)
{
auto it = std::find_if(m_leaseOutputs.constBegin(), m_leaseOutputs.constEnd(), [connector](DrmLeaseOutput *o) {
auto it = std::find_if(m_nonDesktopOutputs.constBegin(), m_nonDesktopOutputs.constEnd(), [connector](DrmOutput *o) {
return o->pipeline()->connector()->id() == connector;
});
if (it != m_leaseOutputs.constEnd()) {
if (it != m_nonDesktopOutputs.constEnd()) {
return *it;
}
return nullptr;
@ -638,11 +637,11 @@ DrmLeaseOutput *DrmGpu::findLeaseOutput(quint32 connector)
void DrmGpu::handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest)
{
QVector<uint32_t> objects;
QVector<DrmLeaseOutput *> outputs;
QVector<DrmOutput *> outputs;
const auto connectors = leaseRequest->connectors();
for (KWaylandServer::DrmLeaseConnectorV1Interface *connector : connectors) {
if (DrmLeaseOutput *output = findLeaseOutput(connector->id())) {
if (DrmOutput *output = findNonDesktopOutput(connector->id())) {
if (output->lease()) {
continue; // already leased
}
@ -679,7 +678,7 @@ void DrmGpu::handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease)
{
const auto connectors = lease->connectors();
for (KWaylandServer::DrmLeaseConnectorV1Interface *connector : connectors) {
if (DrmLeaseOutput *output = findLeaseOutput(connector->id())) {
if (DrmOutput *output = findNonDesktopOutput(connector->id())) {
output->leaseEnded();
}
}
@ -687,10 +686,10 @@ void DrmGpu::handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease)
drmModeRevokeLease(m_fd, lease->lesseeId());
}
void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output)
void DrmGpu::removeNonDesktopOutput(DrmOutput *output)
{
qCDebug(KWIN_DRM) << "Removing leased output" << output;
m_leaseOutputs.removeOne(output);
m_nonDesktopOutputs.removeOne(output);
m_pipelines.removeOne(output->pipeline());
output->pipeline()->setLayers(nullptr, nullptr);
delete output;
@ -759,7 +758,7 @@ bool DrmGpu::needsModeset() const
bool DrmGpu::maybeModeset()
{
auto pipelines = m_pipelines;
for (const auto &output : qAsConst(m_leaseOutputs)) {
for (const auto &output : qAsConst(m_nonDesktopOutputs)) {
if (output->lease()) {
pipelines.removeOne(output->pipeline());
}

View file

@ -41,7 +41,6 @@ class DrmPlane;
class DrmBackend;
class EglGbmBackend;
class DrmAbstractOutput;
class DrmLeaseOutput;
class DrmRenderBackend;
class DrmGpu : public QObject
@ -94,9 +93,9 @@ Q_SIGNALS:
private:
void dispatchEvents();
DrmOutput *findOutput(quint32 connector);
DrmLeaseOutput *findLeaseOutput(quint32 connector);
DrmOutput *findNonDesktopOutput(quint32 connector);
void removeOutput(DrmOutput *output);
void removeLeaseOutput(DrmLeaseOutput *output);
void removeNonDesktopOutput(DrmOutput *output);
void initDrmResources();
void waitIdle();
@ -129,7 +128,7 @@ private:
QVector<DrmOutput *> m_drmOutputs;
QVector<DrmAbstractOutput *> m_outputs;
QVector<DrmLeaseOutput *> m_leaseOutputs;
QVector<DrmOutput *> m_nonDesktopOutputs;
KWaylandServer::DrmLeaseDeviceV1Interface *m_leaseDevice = nullptr;
QSocketNotifier *m_socketNotifier = nullptr;

View file

@ -1,80 +0,0 @@
/*
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_lease_output.h"
#include "drm_layer.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "drm_object_plane.h"
#include "drm_pipeline.h"
#include "wayland/drmleasedevice_v1_interface.h"
#include "logging.h"
#include <QTimer>
namespace KWin
{
DrmLeaseOutput::DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice)
: m_pipeline(pipeline)
{
const DrmConnector *connector = pipeline->connector();
qCDebug(KWIN_DRM) << "offering connector" << connector->id() << "for lease";
m_offer = std::make_unique<KWaylandServer::DrmLeaseConnectorV1Interface>(
leaseDevice,
connector->id(),
connector->modelName(),
QStringLiteral("%1 %2").arg(connector->edid()->manufacturerString(), connector->modelName()));
}
DrmLeaseOutput::~DrmLeaseOutput()
{
qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id();
}
bool DrmLeaseOutput::addLeaseObjects(QVector<uint32_t> &objectList)
{
if (!m_pipeline->crtc()) {
qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
return false;
}
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
objectList << m_pipeline->connector()->id();
objectList << m_pipeline->crtc()->id();
if (m_pipeline->crtc()->primaryPlane()) {
objectList << m_pipeline->crtc()->primaryPlane()->id();
}
return true;
}
void DrmLeaseOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
{
m_lease = lease;
}
void DrmLeaseOutput::leaseEnded()
{
qCDebug(KWIN_DRM) << "ended lease for connector" << m_pipeline->connector()->id();
m_lease = nullptr;
}
KWaylandServer::DrmLeaseV1Interface *DrmLeaseOutput::lease() const
{
return m_lease;
}
DrmPipeline *DrmLeaseOutput::pipeline() const
{
return m_pipeline;
}
}

View file

@ -1,46 +0,0 @@
/*
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 "wayland/drmleasedevice_v1_interface.h"
#include <QObject>
#include <QVector>
namespace KWin
{
class DrmConnector;
class DrmPipeline;
/**
* a DrmLeaseOutput represents a non-desktop output (usually a VR headset)
* that is not used directly by the compositor but is instead leased out to
* applications (usually VR compositors) that drive the output themselves
*/
class DrmLeaseOutput
{
public:
DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
~DrmLeaseOutput();
bool addLeaseObjects(QVector<uint32_t> &objectList);
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
void leaseEnded();
KWaylandServer::DrmLeaseV1Interface *lease() const;
DrmPipeline *pipeline() const;
private:
DrmPipeline *m_pipeline;
std::unique_ptr<KWaylandServer::DrmLeaseConnectorV1Interface> m_offer;
KWaylandServer::DrmLeaseV1Interface *m_lease = nullptr;
};
}

View file

@ -28,6 +28,7 @@
#include "renderloop_p.h"
#include "scene.h"
#include "session.h"
#include "wayland/drmleasedevice_v1_interface.h"
// Qt
#include <QCryptographicHash>
#include <QMatrix4x4>
@ -42,7 +43,7 @@
namespace KWin
{
DrmOutput::DrmOutput(DrmPipeline *pipeline)
DrmOutput::DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice)
: DrmAbstractOutput(pipeline->connector()->gpu())
, m_pipeline(pipeline)
, m_connector(pipeline->connector())
@ -78,6 +79,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
.subPixel = conn->subpixel(),
.capabilities = capabilities,
.internal = conn->isInternal(),
.nonDesktop = conn->isNonDesktop(),
});
const QList<std::shared_ptr<OutputMode>> modes = getModes();
@ -93,9 +95,17 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
setDrmDpmsMode(DpmsMode::Off);
});
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor);
connect(Cursors::self(), &Cursors::hiddenChanged, this, &DrmOutput::updateCursor);
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor);
if (conn->isNonDesktop()) {
m_offer = std::make_unique<KWaylandServer::DrmLeaseConnectorV1Interface>(
leaseDevice,
conn->id(),
conn->modelName(),
QStringLiteral("%1 %2").arg(conn->edid()->manufacturerString(), conn->modelName()));
} else {
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor);
connect(Cursors::self(), &Cursors::hiddenChanged, this, &DrmOutput::updateCursor);
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor);
}
}
DrmOutput::~DrmOutput()
@ -103,6 +113,40 @@ DrmOutput::~DrmOutput()
m_pipeline->setOutput(nullptr);
}
bool DrmOutput::addLeaseObjects(QVector<uint32_t> &objectList)
{
Q_ASSERT(m_offer);
if (!m_pipeline->crtc()) {
qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
return false;
}
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
objectList << m_pipeline->connector()->id();
objectList << m_pipeline->crtc()->id();
if (m_pipeline->crtc()->primaryPlane()) {
objectList << m_pipeline->crtc()->primaryPlane()->id();
}
return true;
}
void DrmOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
{
Q_ASSERT(m_offer);
m_lease = lease;
}
void DrmOutput::leaseEnded()
{
Q_ASSERT(m_offer);
qCDebug(KWIN_DRM) << "ended lease for connector" << m_pipeline->connector()->id();
m_lease = nullptr;
}
KWaylandServer::DrmLeaseV1Interface *DrmOutput::lease() const
{
return m_lease;
}
void DrmOutput::updateCursor()
{
static bool valid;

View file

@ -21,6 +21,13 @@
#include <chrono>
#include <xf86drmMode.h>
namespace KWaylandServer
{
class DrmLeaseConnectorV1Interface;
class DrmLeaseDeviceV1Interface;
class DrmLeaseV1Interface;
}
namespace KWin
{
@ -35,7 +42,7 @@ class KWIN_EXPORT DrmOutput : public DrmAbstractOutput
{
Q_OBJECT
public:
DrmOutput(DrmPipeline *pipeline);
DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
~DrmOutput() override;
DrmConnector *connector() const;
@ -53,6 +60,11 @@ public:
void updateCursor();
void moveCursor();
KWaylandServer::DrmLeaseV1Interface *lease() const;
bool addLeaseObjects(QVector<uint32_t> &objectList);
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
void leaseEnded();
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation) override;
private:
@ -73,6 +85,8 @@ private:
bool m_cursorTextureDirty = true;
std::unique_ptr<GLTexture> m_cursorTexture;
QTimer m_turnOffTimer;
std::unique_ptr<KWaylandServer::DrmLeaseConnectorV1Interface> m_offer;
KWaylandServer::DrmLeaseV1Interface *m_lease = nullptr;
};
}

View file

@ -240,7 +240,7 @@ EGLConfig EglGbmBackend::config(uint32_t format) const
std::shared_ptr<DrmPipelineLayer> EglGbmBackend::createPrimaryLayer(DrmPipeline *pipeline)
{
if (pipeline->output()) {
if (!pipeline->output()->isNonDesktop()) {
return std::make_shared<EglGbmLayer>(this, pipeline);
} else {
return std::make_shared<DrmLeaseEglGbmLayer>(pipeline);

View file

@ -46,7 +46,7 @@ OutputLayer *DrmQPainterBackend::primaryLayer(Output *output)
std::shared_ptr<DrmPipelineLayer> DrmQPainterBackend::createPrimaryLayer(DrmPipeline *pipeline)
{
if (pipeline->output()) {
if (!pipeline->output()->isNonDesktop()) {
return std::make_shared<DrmQPainterLayer>(pipeline);
} else {
return std::make_shared<DrmLeaseQPainterLayer>(pipeline);

View file

@ -399,6 +399,11 @@ bool Output::isPlaceholder() const
return m_information.placeholder;
}
bool Output::isNonDesktop() const
{
return m_information.nonDesktop;
}
Output::RgbRange Output::rgbRange() const
{
return m_rgbRange;

View file

@ -239,6 +239,7 @@ public:
RgbRange rgbRange() const;
bool isPlaceholder() const;
bool isNonDesktop() const;
virtual void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation);
@ -310,6 +311,7 @@ protected:
Capabilities capabilities;
bool internal = false;
bool placeholder = false;
bool nonDesktop = false;
};
void setInformation(const Information &information);