Multi-GPU output support on Wayland

This commit sets up udev and the DrmBackend to list and use all GPUs.

BUG: 425586
BUG: 417323
master
Xaver Hugl 2020-10-05 21:05:55 +00:00 committed by David Edmundson
parent 1f6e615ec0
commit b50f747876
18 changed files with 635 additions and 424 deletions

View File

@ -11,6 +11,7 @@ set(DRM_SOURCES
logging.cpp
scene_qpainter_drm_backend.cpp
screens_drm.cpp
drm_gpu.cpp
)
if (HAVE_GBM)

View File

@ -47,6 +47,8 @@
#include <xf86drmMode.h>
#include <libdrm/drm_mode.h>
#include "drm_gpu.h"
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
@ -77,21 +79,12 @@ DrmBackend::DrmBackend(QObject *parent)
DrmBackend::~DrmBackend()
{
#if HAVE_GBM
if (m_gbmDevice) {
gbm_device_destroy(m_gbmDevice);
}
#endif
if (m_fd >= 0) {
if (m_gpus.size() > 0) {
// wait for pageflips
while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
qDeleteAll(m_planes);
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
close(m_fd);
qDeleteAll(m_gpus);
}
}
@ -243,71 +236,45 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u
void DrmBackend::openDrm()
{
connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate);
UdevDevice::Ptr device = m_udev->primaryGpu();
if (!device) {
std::vector<UdevDevice::Ptr> devices = m_udev->listGPUs();
if (devices.size() == 0) {
qCWarning(KWIN_DRM) << "Did not find a GPU";
return;
}
m_devNode = qEnvironmentVariableIsSet("KWIN_DRM_DEVICE_NODE") ? qgetenv("KWIN_DRM_DEVICE_NODE") : QByteArray(device->devNode());
int fd = LogindIntegration::self()->takeDevice(m_devNode.constData());
if (fd < 0) {
qCWarning(KWIN_DRM) << "failed to open drm device at" << m_devNode;
return;
}
m_fd = fd;
m_active = true;
QSocketNotifier *notifier = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this,
[this] {
if (!LogindIntegration::self()->isActiveSession()) {
return;
}
drmEventContext e;
memset(&e, 0, sizeof e);
e.version = KWIN_DRM_EVENT_CONTEXT_VERSION;
e.page_flip_handler = pageFlipHandler;
drmHandleEvent(m_fd, &e);
for (unsigned int gpu_index = 0; gpu_index < devices.size(); gpu_index++) {
auto device = std::move(devices.at(gpu_index));
auto devNode = QByteArray(device->devNode());
int fd = LogindIntegration::self()->takeDevice(devNode.constData());
if (fd < 0) {
qCWarning(KWIN_DRM) << "failed to open drm device at" << devNode;
return;
}
);
m_drmId = device->sysNum();
m_active = true;
QSocketNotifier *notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(notifier, &QSocketNotifier::activated, this,
[fd] {
if (!LogindIntegration::self()->isActiveSession()) {
return;
}
drmEventContext e;
memset(&e, 0, sizeof e);
e.version = KWIN_DRM_EVENT_CONTEXT_VERSION;
e.page_flip_handler = pageFlipHandler;
drmHandleEvent(fd, &e);
}
);
DrmGpu *gpu = new DrmGpu(this, devNode, fd, device->sysNum());
connect(gpu, &DrmGpu::outputAdded, this, &DrmBackend::addOutput);
connect(gpu, &DrmGpu::outputRemoved, this, &DrmBackend::removeOutput);
m_gpus.append(gpu);
break;
}
// trying to activate Atomic Mode Setting (this means also Universal Planes)
if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS")) {
if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) {
qCDebug(KWIN_DRM) << "Using Atomic Mode Setting.";
m_atomicModeSetting = true;
DrmScopedPointer<drmModePlaneRes> planeResources(drmModeGetPlaneResources(m_fd));
if (!planeResources) {
qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode";
m_atomicModeSetting = false;
}
if (m_atomicModeSetting) {
qCDebug(KWIN_DRM) << "Number of planes:" << planeResources->count_planes;
// create the plane objects
for (unsigned int i = 0; i < planeResources->count_planes; ++i) {
DrmScopedPointer<drmModePlane> kplane(drmModeGetPlane(m_fd, planeResources->planes[i]));
DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd);
if (p->atomicInit()) {
m_planes << p;
if (p->type() == DrmPlane::TypeIndex::Overlay) {
m_overlayPlanes << p;
}
} else {
delete p;
}
}
if (m_planes.isEmpty()) {
qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode";
m_atomicModeSetting = false;
}
}
} else {
qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode.";
}
for (auto gpu : m_gpus)
gpu->tryAMS();
}
initCursor();
@ -317,7 +284,7 @@ void DrmBackend::openDrm()
if (m_outputs.isEmpty()) {
qCDebug(KWIN_DRM) << "No connected outputs found on startup.";
}
// setup udevMonitor
if (m_udevMonitor) {
m_udevMonitor->filterSubsystemDevType("drm");
@ -330,7 +297,14 @@ void DrmBackend::openDrm()
if (!device) {
return;
}
if (device->sysNum() != m_drmId) {
bool drm = false;
for (auto gpu : m_gpus) {
if (gpu->drmId() == device->sysNum()) {
drm = true;
break;
}
}
if (!drm) {
return;
}
if (device->hasProperty("HOTPLUG", "1")) {
@ -346,176 +320,34 @@ void DrmBackend::openDrm()
setReady(true);
}
void DrmBackend::addOutput(DrmOutput *o)
{
m_outputs.append(o);
m_enabledOutputs.append(o);
emit o->gpu()->outputEnabled(o);
}
void DrmBackend::removeOutput(DrmOutput *o)
{
emit o->gpu()->outputDisabled(o);
m_outputs.removeOne(o);
m_enabledOutputs.removeOne(o);
}
bool DrmBackend::updateOutputs()
{
if (m_fd < 0) {
if (m_gpus.size() == 0) {
return false;
}
DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
if (!resources) {
qCWarning(KWIN_DRM) << "drmModeGetResources failed";
return false;
}
auto oldConnectors = m_connectors;
for (int i = 0; i < resources->count_connectors; ++i) {
const uint32_t currentConnector = resources->connectors[i];
auto it = std::find_if(m_connectors.constBegin(), m_connectors.constEnd(), [currentConnector] (DrmConnector *c) { return c->id() == currentConnector; });
if (it == m_connectors.constEnd()) {
auto c = new DrmConnector(currentConnector, m_fd);
if (m_atomicModeSetting && !c->atomicInit()) {
delete c;
continue;
}
m_connectors << c;
} else {
oldConnectors.removeOne(*it);
}
}
auto oldCrtcs = m_crtcs;
for (int i = 0; i < resources->count_crtcs; ++i) {
const uint32_t currentCrtc = resources->crtcs[i];
auto it = std::find_if(m_crtcs.constBegin(), m_crtcs.constEnd(), [currentCrtc] (DrmCrtc *c) { return c->id() == currentCrtc; });
if (it == m_crtcs.constEnd()) {
auto c = new DrmCrtc(currentCrtc, this, i);
if (m_atomicModeSetting && !c->atomicInit()) {
delete c;
continue;
}
m_crtcs << c;
} else {
oldCrtcs.removeOne(*it);
}
}
for (auto c : qAsConst(oldConnectors)) {
m_connectors.removeOne(c);
}
for (auto c : qAsConst(oldCrtcs)) {
m_crtcs.removeOne(c);
}
QVector<DrmOutput*> connectedOutputs;
QVector<DrmConnector*> pendingConnectors;
// split up connected connectors in already or not yet assigned ones
for (DrmConnector *con : qAsConst(m_connectors)) {
if (!con->isConnected()) {
continue;
}
if (DrmOutput *o = findOutput(con->id())) {
connectedOutputs << o;
} else {
pendingConnectors << con;
}
}
// check for outputs which got removed
QVector<DrmOutput*> removedOutputs;
auto it = m_outputs.begin();
while (it != m_outputs.end()) {
if (connectedOutputs.contains(*it)) {
it++;
continue;
}
DrmOutput *removed = *it;
it = m_outputs.erase(it);
m_enabledOutputs.removeOne(removed);
emit outputRemoved(removed);
removedOutputs.append(removed);
}
// now check new connections
for (DrmConnector *con : qAsConst(pendingConnectors)) {
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_fd, con->id()));
if (!connector) {
continue;
}
if (connector->count_modes == 0) {
continue;
}
bool outputDone = false;
QVector<uint32_t> encoders = con->encoders();
for (auto encId : qAsConst(encoders)) {
DrmScopedPointer<drmModeEncoder> encoder(drmModeGetEncoder(m_fd, encId));
if (!encoder) {
continue;
}
for (DrmCrtc *crtc : qAsConst(m_crtcs)) {
if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) {
continue;
}
// check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode)
auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(),
[crtc] (DrmOutput *o) {
return o->m_crtc == crtc;
}
);
if (it != connectedOutputs.constEnd()) {
continue;
}
// we found a suitable encoder+crtc
// TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning
DrmScopedPointer<drmModeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id()));
if (!modeCrtc) {
continue;
}
DrmOutput *output = new DrmOutput(this);
con->setOutput(output);
output->m_conn = con;
crtc->setOutput(output);
output->m_crtc = crtc;
if (modeCrtc->mode_valid) {
output->m_mode = modeCrtc->mode;
} else {
output->m_mode = connector->modes[0];
}
qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name;
if (!output->init(connector.data())) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id();
delete output;
continue;
}
if (!output->initCursor(m_cursorSize)) {
setSoftWareCursor(true);
}
qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid();
connectedOutputs << output;
emit outputAdded(output);
outputDone = true;
break;
}
if (outputDone) {
break;
}
}
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
m_outputs = connectedOutputs;
m_enabledOutputs = connectedOutputs;
for (auto gpu : m_gpus)
gpu->updateOutputs();
std::sort(m_outputs.begin(), m_outputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
readOutputsConfiguration();
updateOutputsEnabled();
if (!m_outputs.isEmpty()) {
emit screensQueried();
}
for(DrmOutput* removedOutput : removedOutputs) {
removedOutput->teardown();
removedOutput->m_crtc = nullptr;
removedOutput->m_conn = nullptr;
}
qDeleteAll(oldConnectors);
qDeleteAll(oldCrtcs);
return true;
}
@ -574,12 +406,12 @@ void DrmBackend::enableOutput(DrmOutput *output, bool enable)
if (enable) {
Q_ASSERT(!m_enabledOutputs.contains(output));
m_enabledOutputs << output;
emit outputAdded(output);
emit output->gpu()->outputEnabled(output);
} else {
Q_ASSERT(m_enabledOutputs.contains(output));
m_enabledOutputs.removeOne(output);
Q_ASSERT(!m_enabledOutputs.contains(output));
emit outputRemoved(output);
emit output->gpu()->outputDisabled(output);
}
updateOutputsEnabled();
checkOutputsAreOn();
@ -640,6 +472,7 @@ void DrmBackend::initCursor()
if (m_cursorEnabled) {
if (!(*it)->showCursor()) {
setSoftWareCursor(true);
break;
}
} else {
(*it)->hideCursor();
@ -649,15 +482,17 @@ void DrmBackend::initCursor()
);
uint64_t capability = 0;
QSize cursorSize;
if (drmGetCap(m_fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) {
cursorSize.setWidth(capability);
} else {
cursorSize.setWidth(64);
cursorSize.setWidth(64);
for (auto gpu : m_gpus) {
if (drmGetCap(gpu->fd(), DRM_CAP_CURSOR_WIDTH, &capability) == 0) {
cursorSize.setWidth(capability);
}
}
if (drmGetCap(m_fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) {
cursorSize.setHeight(capability);
} else {
cursorSize.setHeight(64);
cursorSize.setHeight(64);
for (auto gpu : m_gpus) {
if (drmGetCap(gpu->fd(), DRM_CAP_CURSOR_HEIGHT, &capability) == 0) {
cursorSize.setHeight(capability);
}
}
m_cursorSize = cursorSize;
// now we have screens and can set cursors, so start tracking
@ -735,7 +570,7 @@ Screens *DrmBackend::createScreens(QObject *parent)
QPainterBackend *DrmBackend::createQPainterBackend()
{
m_deleteBufferAfterPageFlip = false;
return new DrmQPainterBackend(this);
return new DrmQPainterBackend(this, m_gpus.at(0));
}
OpenGLBackend *DrmBackend::createOpenGLBackend()
@ -743,32 +578,18 @@ OpenGLBackend *DrmBackend::createOpenGLBackend()
#if HAVE_EGL_STREAMS
if (m_useEglStreams) {
m_deleteBufferAfterPageFlip = false;
return new EglStreamBackend(this);
return new EglStreamBackend(this, m_gpus.at(0));
}
#endif
#if HAVE_GBM
m_deleteBufferAfterPageFlip = true;
return new EglGbmBackend(this);
return new EglGbmBackend(this, m_gpus.at(0));
#else
return Platform::createOpenGLBackend();
#endif
}
DrmDumbBuffer *DrmBackend::createBuffer(const QSize &size)
{
DrmDumbBuffer *b = new DrmDumbBuffer(m_fd, size);
return b;
}
#if HAVE_GBM
DrmSurfaceBuffer *DrmBackend::createBuffer(const std::shared_ptr<GbmSurface> &surface)
{
DrmSurfaceBuffer *b = new DrmSurfaceBuffer(m_fd, surface);
return b;
}
#endif
void DrmBackend::updateOutputsEnabled()
{
bool enabled = false;
@ -801,7 +622,9 @@ QString DrmBackend::supportInformation() const
s.nospace();
s << "Name: " << "DRM" << Qt::endl;
s << "Active: " << m_active << Qt::endl;
s << "Atomic Mode Setting: " << m_atomicModeSetting << Qt::endl;
for (int g = 0; g < m_gpus.size(); g++) {
s << "Atomic Mode Setting on GPU " << g << ": " << m_gpus.at(g)->atomicModeSetting() << Qt::endl;
}
#if HAVE_EGL_STREAMS
s << "Using EGL Streams: " << m_useEglStreams << Qt::endl;
#endif
@ -811,7 +634,10 @@ QString DrmBackend::supportInformation() const
DmaBufTexture *DrmBackend::createDmaBufTexture(const QSize &size)
{
#if HAVE_GBM
return GbmDmaBuf::createBuffer(size, m_gbmDevice);
// gpu_index is a fixed 0 here
// as the first GPU is assumed to always be the one used for scene rendering
// and this function is only used for Pipewire
return GbmDmaBuf::createBuffer(size, m_gpus.at(0)->gbmDevice());
#else
return nullptr;
#endif

View File

@ -43,6 +43,7 @@ class DrmCrtc;
class DrmConnector;
class GbmSurface;
class Cursor;
class DrmGpu;
class KWIN_EXPORT DrmBackend : public Platform
{
@ -60,16 +61,9 @@ public:
void init() override;
void prepareShutdown() override;
DrmDumbBuffer *createBuffer(const QSize &size);
#if HAVE_GBM
DrmSurfaceBuffer *createBuffer(const std::shared_ptr<GbmSurface> &surface);
#endif
bool present(DrmBuffer *buffer, DrmOutput *output);
int fd() const {
return m_fd;
}
Outputs outputs() const override;
Outputs enabledOutputs() const override;
QVector<DrmOutput*> drmOutputs() const {
@ -81,13 +75,6 @@ public:
void enableOutput(DrmOutput *output, bool enable);
QVector<DrmPlane*> planes() const {
return m_planes;
}
QVector<DrmPlane*> overlayPlanes() const {
return m_overlayPlanes;
}
void createDpmsFilter();
void checkOutputsAreOn();
@ -95,21 +82,6 @@ public:
bool deleteBufferAfterPageFlip() const {
return m_deleteBufferAfterPageFlip;
}
// returns use of AMS, default is not/legacy
bool atomicModeSetting() const {
return m_atomicModeSetting;
}
void setGbmDevice(gbm_device *device) {
m_gbmDevice = device;
}
gbm_device *gbmDevice() const {
return m_gbmDevice;
}
QByteArray devNode() const {
return m_devNode;
}
#if HAVE_EGL_STREAMS
bool useEglStreams() const {
@ -128,22 +100,13 @@ public:
public Q_SLOTS:
void turnOutputsOn();
Q_SIGNALS:
/**
* Emitted whenever an output is removed/disabled
*/
void outputRemoved(KWin::DrmOutput *output);
/**
* Emitted whenever an output is added/enabled
*/
void outputAdded(KWin::DrmOutput *output);
protected:
void doHideCursor() override;
void doShowCursor() override;
private:
friend class DrmGpu;
void addOutput(DrmOutput* output);
void removeOutput(DrmOutput* output);
static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data);
void openDrm();
void activate(bool active);
@ -161,32 +124,22 @@ private:
void updateOutputsEnabled();
QScopedPointer<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor;
int m_fd = -1;
int m_drmId = 0;
// all crtcs
QVector<DrmCrtc*> m_crtcs;
// all connectors
QVector<DrmConnector*> m_connectors;
// active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
// active and enabled pipelines (above + wl_output)
QVector<DrmOutput*> m_enabledOutputs;
bool m_deleteBufferAfterPageFlip;
bool m_atomicModeSetting = false;
bool m_cursorEnabled = false;
QSize m_cursorSize;
int m_pageFlipsPending = 0;
bool m_active = false;
QByteArray m_devNode;
#if HAVE_EGL_STREAMS
bool m_useEglStreams = false;
#endif
// all available planes: primarys, cursors and overlays
QVector<DrmPlane*> m_planes;
QVector<DrmPlane*> m_overlayPlanes;
QVector<DrmGpu*> m_gpus;
QScopedPointer<DpmsInputEventFilter> m_dpmsFilter;
gbm_device *m_gbmDevice = nullptr;
};

View File

@ -0,0 +1,273 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_gpu.h"
#include "drm_backend.h"
#include "drm_output.h"
#include "drm_object_connector.h"
#include "drm_object_crtc.h"
#include "abstract_egl_backend.h"
#include "logging.h"
#if HAVE_GBM
#include "egl_gbm_backend.h"
#include <gbm.h>
#include "gbm_dmabuf.h"
#endif
// system
#include <algorithm>
#include <unistd.h>
// drm
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <libdrm/drm_mode.h>
namespace KWin
{
DrmGpu::DrmGpu(DrmBackend *backend, QByteArray devNode, int fd, int drmId) : m_backend(backend), m_devNode(devNode), m_fd(fd), m_drmId(drmId), m_atomicModeSetting(false), m_useEglStreams(false), m_gbmDevice(nullptr)
{
}
DrmGpu::~DrmGpu()
{
#if HAVE_GBM
gbm_device_destroy(m_gbmDevice);
#endif
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
qDeleteAll(m_planes);
close(m_fd);
}
void DrmGpu::tryAMS()
{
m_atomicModeSetting = false;
if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) {
bool ams = true;
QVector<DrmPlane*> planes, overlayPlanes;
DrmScopedPointer<drmModePlaneRes> planeResources(drmModeGetPlaneResources(m_fd));
if (!planeResources) {
qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode on GPU " << m_devNode;
ams = false;
}
if (ams) {
qCDebug(KWIN_DRM) << "Using Atomic Mode Setting on gpu" << m_devNode;
qCDebug(KWIN_DRM) << "Number of planes on GPU" << m_devNode << ":" << planeResources->count_planes;
// create the plane objects
for (unsigned int i = 0; i < planeResources->count_planes; ++i) {
DrmScopedPointer<drmModePlane> kplane(drmModeGetPlane(m_fd, planeResources->planes[i]));
DrmPlane *p = new DrmPlane(kplane->plane_id, m_fd);
if (p->atomicInit()) {
planes << p;
if (p->type() == DrmPlane::TypeIndex::Overlay) {
overlayPlanes << p;
}
} else {
delete p;
}
}
if (planes.isEmpty()) {
qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode on GPU " << m_devNode;
ams = false;
}
}
if (!ams) {
for (auto p : planes) {
delete p;
}
planes.clear();
overlayPlanes.clear();
}
m_atomicModeSetting = ams;
m_planes = planes;
m_overlayPlanes = overlayPlanes;
} else {
qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode on GPU" << m_devNode;
}
}
bool DrmGpu::updateOutputs()
{
auto oldConnectors = m_connectors;
auto oldCrtcs = m_crtcs;
DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
if (!resources) {
qCWarning(KWIN_DRM) << "drmModeGetResources failed";
return false;
}
for (int i = 0; i < resources->count_connectors; ++i) {
const uint32_t currentConnector = resources->connectors[i];
auto it = std::find_if(m_connectors.constBegin(), m_connectors.constEnd(), [currentConnector] (DrmConnector *c) { return c->id() == currentConnector; });
if (it == m_connectors.constEnd()) {
auto c = new DrmConnector(currentConnector, m_fd);
if (m_atomicModeSetting && !c->atomicInit()) {
delete c;
continue;
}
m_connectors << c;
} else {
oldConnectors.removeOne(*it);
}
}
for (int i = 0; i < resources->count_crtcs; ++i) {
const uint32_t currentCrtc = resources->crtcs[i];
auto it = std::find_if(m_crtcs.constBegin(), m_crtcs.constEnd(), [currentCrtc] (DrmCrtc *c) { return c->id() == currentCrtc; });
if (it == m_crtcs.constEnd()) {
auto c = new DrmCrtc(currentCrtc, m_backend, this, i);
if (m_atomicModeSetting && !c->atomicInit()) {
delete c;
continue;
}
m_crtcs << c;
} else {
oldCrtcs.removeOne(*it);
}
}
for (auto c : qAsConst(oldConnectors)) {
m_connectors.removeOne(c);
}
for (auto c : qAsConst(oldCrtcs)) {
m_crtcs.removeOne(c);
}
QVector<DrmOutput*> connectedOutputs;
QVector<DrmConnector*> pendingConnectors;
// split up connected connectors in already or not yet assigned ones
for (DrmConnector *con : qAsConst(m_connectors)) {
if (!con->isConnected()) {
continue;
}
if (DrmOutput *o = findOutput(con->id())) {
connectedOutputs << o;
} else {
pendingConnectors << con;
}
}
// check for outputs which got removed
QVector<DrmOutput*> removedOutputs;
auto it = m_outputs.begin();
while (it != m_outputs.end()) {
if (connectedOutputs.contains(*it)) {
it++;
continue;
}
DrmOutput *removed = *it;
it = m_outputs.erase(it);
removedOutputs.append(removed);
}
for (DrmConnector *con : qAsConst(pendingConnectors)) {
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_fd, con->id()));
if (!connector) {
continue;
}
if (connector->count_modes == 0) {
continue;
}
bool outputDone = false;
QVector<uint32_t> encoders = con->encoders();
for (auto encId : qAsConst(encoders)) {
DrmScopedPointer<drmModeEncoder> encoder(drmModeGetEncoder(m_fd, encId));
if (!encoder) {
continue;
}
for (DrmCrtc *crtc : qAsConst(m_crtcs)) {
if (!(encoder->possible_crtcs & (1 << crtc->resIndex()))) {
continue;
}
// check if crtc isn't used yet -- currently we don't allow multiple outputs on one crtc (cloned mode)
auto it = std::find_if(connectedOutputs.constBegin(), connectedOutputs.constEnd(),
[crtc] (DrmOutput *o) {
return o->m_crtc == crtc;
}
);
if (it != connectedOutputs.constEnd()) {
continue;
}
// we found a suitable encoder+crtc
// TODO: we could avoid these lib drm calls if we store all struct data in DrmCrtc and DrmConnector in the beginning
DrmScopedPointer<drmModeCrtc> modeCrtc(drmModeGetCrtc(m_fd, crtc->id()));
if (!modeCrtc) {
continue;
}
DrmOutput *output = new DrmOutput(this->m_backend, this);
con->setOutput(output);
output->m_conn = con;
crtc->setOutput(output);
output->m_crtc = crtc;
if (modeCrtc->mode_valid) {
output->m_mode = modeCrtc->mode;
} else {
output->m_mode = connector->modes[0];
}
qCDebug(KWIN_DRM) << "For new output use mode " << output->m_mode.name;
if (!output->init(connector.data())) {
qCWarning(KWIN_DRM) << "Failed to create output for connector " << con->id();
delete output;
continue;
}
if (!output->initCursor(m_backend->m_cursorSize)) {
m_backend->setSoftWareCursor(true);
}
qCDebug(KWIN_DRM) << "Found new output with uuid" << output->uuid();
connectedOutputs << output;
emit outputAdded(output);
outputDone = true;
break;
}
if (outputDone) {
break;
}
}
}
std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
m_outputs = connectedOutputs;
for(DrmOutput *removedOutput : removedOutputs) {
emit outputRemoved(removedOutput);
removedOutput->teardown();
removedOutput->m_crtc = nullptr;
removedOutput->m_conn = nullptr;
}
qDeleteAll(oldConnectors);
qDeleteAll(oldCrtcs);
return true;
}
DrmOutput *DrmGpu::findOutput(quint32 connector)
{
auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) {
return o->m_conn->id() == connector;
});
if (it != m_outputs.constEnd()) {
return *it;
}
return nullptr;
}
}

View File

@ -0,0 +1,111 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2020 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef DRM_GPU_H
#define DRM_GPU_H
#include <qobject.h>
#include <QVector>
#include "drm_buffer.h"
struct gbm_device;
namespace KWin
{
class DrmOutput;
class DrmPlane;
class DrmCrtc;
class DrmConnector;
class DrmBackend;
class AbstractEglBackend;
class DrmGpu : public QObject
{
Q_OBJECT
public:
DrmGpu(DrmBackend *backend, QByteArray devNode, int fd, int drmId);
~DrmGpu();
// getters
QVector<DrmOutput*> outputs() const {
return m_outputs;
}
int fd() const {
return m_fd;
}
int drmId() const {
return m_drmId;
}
bool atomicModeSetting() const {
return m_atomicModeSetting;
}
QByteArray devNode() const {
return m_devNode;
}
gbm_device *gbmDevice() const {
return m_gbmDevice;
}
QVector<DrmPlane*> planes() const {
return m_planes;
}
void setGbmDevice(gbm_device *d) {
m_gbmDevice = d;
}
DrmDumbBuffer *createBuffer(const QSize &size) const {
return new DrmDumbBuffer(m_fd, size);
}
Q_SIGNALS:
void outputAdded(DrmOutput *output);
void outputRemoved(DrmOutput *output);
void outputEnabled(DrmOutput *output);
void outputDisabled(DrmOutput *output);
protected:
friend class DrmBackend;
void tryAMS();
bool updateOutputs();
private:
DrmOutput *findOutput(quint32 connector);
DrmBackend* const m_backend;
const QByteArray m_devNode;
const int m_fd;
const int m_drmId;
bool m_atomicModeSetting;
bool m_useEglStreams;
gbm_device* m_gbmDevice;
// all available planes: primarys, cursors and overlays
QVector<DrmPlane*> m_planes;
QVector<DrmPlane*> m_overlayPlanes;
// crtcs
QVector<DrmCrtc*> m_crtcs;
// connectors
QVector<DrmConnector*> m_connectors;
// active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
};
}
#endif // DRM_GPU_H

View File

@ -12,16 +12,18 @@
#include "drm_buffer.h"
#include "drm_pointer.h"
#include "logging.h"
#include "drm_gpu.h"
namespace KWin
{
DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex)
: DrmObject(crtc_id, backend->fd()),
DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, DrmGpu *gpu, int resIndex)
: DrmObject(crtc_id, gpu->fd()),
m_resIndex(resIndex),
m_backend(backend)
m_backend(backend),
m_gpu(gpu)
{
DrmScopedPointer<drmModeCrtc> modeCrtc(drmModeGetCrtc(backend->fd(), crtc_id));
DrmScopedPointer<drmModeCrtc> modeCrtc(drmModeGetCrtc(gpu->fd(), crtc_id));
if (modeCrtc) {
m_gammaRampSize = modeCrtc->gamma_size;
}
@ -81,12 +83,12 @@ bool DrmCrtc::blank()
return false;
}
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
return false;
}
if (!m_blackBuffer) {
DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize());
DrmDumbBuffer *blackBuffer = m_gpu->createBuffer(m_output->pixelSize());
if (!blackBuffer->map()) {
delete blackBuffer;
return false;
@ -113,7 +115,7 @@ bool DrmCrtc::setGammaRamp(const GammaRamp &gamma)
uint16_t *green = const_cast<uint16_t *>(gamma.green());
uint16_t *blue = const_cast<uint16_t *>(gamma.blue());
const bool isError = drmModeCrtcSetGamma(m_backend->fd(), m_id,
const bool isError = drmModeCrtcSetGamma(m_gpu->fd(), m_id,
gamma.size(), red, green, blue);
return !isError;

View File

@ -18,11 +18,12 @@ class DrmBackend;
class DrmBuffer;
class DrmDumbBuffer;
class GammaRamp;
class DrmGpu;
class DrmCrtc : public DrmObject
{
public:
DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex);
DrmCrtc(uint32_t crtc_id, DrmBackend *backend, DrmGpu *gpu, int resIndex);
~DrmCrtc() override;
@ -57,6 +58,10 @@ public:
return m_gammaRampSize;
}
bool setGammaRamp(const GammaRamp &gamma);
DrmGpu *gpu() {
return m_gpu;
}
private:
int m_resIndex;
@ -66,6 +71,7 @@ private:
DrmBuffer *m_nextBuffer = nullptr;
DrmDumbBuffer *m_blackBuffer = nullptr;
DrmBackend *m_backend;
DrmGpu *m_gpu;
};
}

View File

@ -36,12 +36,15 @@
#include <xf86drmMode.h>
#include <libdrm/drm_mode.h>
#include "drm_gpu.h"
namespace KWin
{
DrmOutput::DrmOutput(DrmBackend *backend)
DrmOutput::DrmOutput(DrmBackend *backend, DrmGpu *gpu)
: AbstractWaylandOutput(backend)
, m_backend(backend)
, m_gpu(gpu)
{
}
@ -96,13 +99,13 @@ void DrmOutput::releaseGbm()
bool DrmOutput::hideCursor()
{
return drmModeSetCursor(m_backend->fd(), m_crtc->id(), 0, 0, 0) == 0;
return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), 0, 0, 0) == 0;
}
bool DrmOutput::showCursor(DrmDumbBuffer *c)
{
const QSize &s = c->size();
return drmModeSetCursor(m_backend->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0;
return drmModeSetCursor(m_gpu->fd(), m_crtc->id(), c->handle(), s.width(), s.height()) == 0;
}
bool DrmOutput::showCursor()
@ -118,6 +121,7 @@ bool DrmOutput::showCursor()
const bool ret = showCursor(m_cursor[m_cursorIndex].data());
if (!ret) {
qCDebug(KWIN_DRM) << "DrmOutput::showCursor(DrmDumbBuffer) failed";
return ret;
}
@ -164,7 +168,7 @@ void DrmOutput::updateCursor()
p.end();
}
void DrmOutput::moveCursor(Cursor* cursor, const QPoint &globalPos)
void DrmOutput::moveCursor(Cursor *cursor, const QPoint &globalPos)
{
const QMatrix4x4 hotspotMatrix = matrixDisplay(cursor->image().size());
@ -194,7 +198,7 @@ void DrmOutput::moveCursor(Cursor* cursor, const QPoint &globalPos)
}
pos *= scale();
pos -= hotspotMatrix.map(cursor->hotspot());
drmModeMoveCursor(m_backend->fd(), m_crtc->id(), pos.x(), pos.y());
drmModeMoveCursor(m_gpu->fd(), m_crtc->id(), pos.x(), pos.y());
}
static QHash<int, QByteArray> s_connectorNames = {
@ -244,7 +248,7 @@ bool DrmOutput::init(drmModeConnector *connector)
initEdid(connector);
initDpms(connector);
initUuid();
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
if (!initPrimaryPlane()) {
return false;
}
@ -255,7 +259,7 @@ bool DrmOutput::init(drmModeConnector *connector)
setDpmsSupported(true);
initOutputDevice(connector);
if (!m_backend->atomicModeSetting() && !m_crtc->blank()) {
if (!m_gpu->atomicModeSetting() && !m_crtc->blank()) {
// We use legacy mode and the initial output blank failed.
return false;
}
@ -361,12 +365,12 @@ void DrmOutput::initEdid(drmModeConnector *connector)
{
DrmScopedPointer<drmModePropertyBlobRes> edid;
for (int i = 0; i < connector->count_props; ++i) {
DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_backend->fd(), connector->props[i]));
DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_gpu->fd(), connector->props[i]));
if (!property) {
continue;
}
if ((property->flags & DRM_MODE_PROP_BLOB) && qstrcmp(property->name, "EDID") == 0) {
edid.reset(drmModeGetPropertyBlob(m_backend->fd(), connector->prop_values[i]));
edid.reset(drmModeGetPropertyBlob(m_gpu->fd(), connector->prop_values[i]));
}
}
if (!edid) {
@ -381,8 +385,8 @@ void DrmOutput::initEdid(drmModeConnector *connector)
bool DrmOutput::initPrimaryPlane()
{
for (int i = 0; i < m_backend->planes().size(); ++i) {
DrmPlane* p = m_backend->planes()[i];
for (int i = 0; i < m_gpu->planes().size(); ++i) {
DrmPlane *p = m_gpu->planes()[i];
if (!p) {
continue;
}
@ -409,8 +413,8 @@ bool DrmOutput::initPrimaryPlane()
bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs layer support in general first)
{
for (int i = 0; i < m_backend->planes().size(); ++i) {
DrmPlane* p = m_backend->planes()[i];
for (int i = 0; i < m_gpu->planes().size(); ++i) {
DrmPlane *p = m_gpu->planes()[i];
if (!p) {
continue;
}
@ -437,7 +441,7 @@ bool DrmOutput::initCursorPlane() // TODO: Add call in init (but needs lay
bool DrmOutput::initCursor(const QSize &cursorSize)
{
auto createCursor = [this, cursorSize] (int index) {
m_cursor[index].reset(m_backend->createBuffer(cursorSize));
m_cursor[index].reset(m_gpu->createBuffer(cursorSize));
if (!m_cursor[index]->map(QImage::Format_ARGB32_Premultiplied)) {
return false;
}
@ -452,7 +456,7 @@ bool DrmOutput::initCursor(const QSize &cursorSize)
void DrmOutput::initDpms(drmModeConnector *connector)
{
for (int i = 0; i < connector->count_props; ++i) {
DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_backend->fd(), connector->props[i]));
DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_gpu->fd(), connector->props[i]));
if (!property) {
continue;
}
@ -467,7 +471,7 @@ void DrmOutput::updateEnablement(bool enable)
{
if (enable) {
m_dpmsModePending = DpmsMode::On;
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
atomicEnable();
} else {
if (dpmsLegacyApply()) {
@ -477,7 +481,7 @@ void DrmOutput::updateEnablement(bool enable)
} else {
m_dpmsModePending = DpmsMode::Off;
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
atomicDisable();
} else {
if (dpmsLegacyApply()) {
@ -563,7 +567,7 @@ void DrmOutput::updateDpms(KWaylandServer::OutputInterface::DpmsMode mode)
m_dpmsModePending = drmMode;
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
m_modesetRequested = true;
if (drmMode == DpmsMode::On) {
if (m_atomicOffPending) {
@ -589,7 +593,7 @@ void DrmOutput::dpmsFinishOn()
waylandOutput()->setDpmsMode(toWaylandDpmsMode(DpmsMode::On));
m_backend->checkOutputsAreOn();
if (!m_backend->atomicModeSetting()) {
if (!m_gpu->atomicModeSetting()) {
m_crtc->blank();
}
if (Compositor *compositor = Compositor::self()) {
@ -611,7 +615,7 @@ void DrmOutput::dpmsFinishOff()
bool DrmOutput::dpmsLegacyApply()
{
if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(),
if (drmModeConnectorSetProperty(m_gpu->fd(), m_conn->id(),
m_dpms->prop_id, uint64_t(m_dpmsModePending)) < 0) {
m_dpmsModePending = m_dpmsMode;
qCWarning(KWIN_DRM) << "Setting DPMS failed";
@ -693,7 +697,7 @@ void DrmOutput::updateTransform(Transform transform)
void DrmOutput::updateMode(int modeIndex)
{
// get all modes on the connector
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_backend->fd(), m_conn->id()));
DrmScopedPointer<drmModeConnector> connector(drmModeGetConnector(m_gpu->fd(), m_conn->id()));
if (connector->count_modes <= modeIndex) {
// TODO: error?
return;
@ -716,7 +720,7 @@ void DrmOutput::setWaylandMode()
void DrmOutput::pageFlipped()
{
// In legacy mode we might get a page flip through a blank.
Q_ASSERT(m_pageFlipPending || !m_backend->atomicModeSetting());
Q_ASSERT(m_pageFlipPending || !m_gpu->atomicModeSetting());
m_pageFlipPending = false;
if (m_deleted) {
@ -730,7 +734,7 @@ void DrmOutput::pageFlipped()
// Egl based surface buffers get destroyed, QPainter based dumb buffers not
// TODO: split up DrmOutput in two for dumb and egl/gbm surface buffer compatible subclasses completely?
if (m_backend->deleteBufferAfterPageFlip()) {
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
if (!m_primaryPlane->next()) {
// on manual vt switch
// TODO: when we later use overlay planes it might happen, that we have a page flip with only
@ -755,7 +759,7 @@ void DrmOutput::pageFlipped()
m_crtc->flipBuffer();
}
} else {
if (m_backend->atomicModeSetting()){
if (m_gpu->atomicModeSetting()){
for (DrmPlane *p : m_nextPlanesFlipList) {
p->flipBuffer();
}
@ -776,7 +780,7 @@ bool DrmOutput::present(DrmBuffer *buffer)
if (m_dpmsModePending != DpmsMode::On) {
return false;
}
if (m_backend->atomicModeSetting()) {
if (m_gpu->atomicModeSetting()) {
return presentAtomically(buffer);
} else {
return presentLegacy(buffer);
@ -826,7 +830,7 @@ bool DrmOutput::presentAtomically(DrmBuffer *buffer)
return true;
}
#endif
m_primaryPlane->setNext(buffer);
m_nextPlanesFlipList << m_primaryPlane;
@ -890,7 +894,7 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer)
return false;
}
}
const bool ok = drmModePageFlip(m_backend->fd(), m_crtc->id(), buffer->bufferId(), DRM_MODE_PAGE_FLIP_EVENT, this) == 0;
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);
} else {
@ -902,7 +906,7 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer)
bool DrmOutput::setModeLegacy(DrmBuffer *buffer)
{
uint32_t connId = m_conn->id();
if (drmModeSetCrtc(m_backend->fd(), m_crtc->id(), buffer->bufferId(), 0, 0, &connId, 1, &m_mode) == 0) {
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";
@ -939,17 +943,16 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
};
if (!req) {
qCWarning(KWIN_DRM) << "DRM: couldn't allocate atomic request";
errorHandler();
return false;
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_backend->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) {
if (drmModeCreatePropertyBlob(m_gpu->fd(), &m_mode, sizeof(m_mode), &m_blobId) != 0) {
qCWarning(KWIN_DRM) << "Failed to create property blob";
errorHandler();
return false;
@ -994,8 +997,8 @@ bool DrmOutput::doAtomicCommit(AtomicCommitMode mode)
return false;
}
if (drmModeAtomicCommit(m_backend->fd(), req, flags, this)) {
qCWarning(KWIN_DRM) << "Atomic request failed to commit:" << strerror(errno);
if (drmModeAtomicCommit(m_gpu->fd(), req, flags, this)) {
qCDebug(KWIN_DRM) << "Atomic request failed to commit: " << strerror(errno);
errorHandler();
return false;
}
@ -1073,7 +1076,7 @@ bool DrmOutput::setGammaRamp(const GammaRamp &gamma)
}
QDebug& operator<<(QDebug& s, const KWin::DrmOutput* output)
QDebug& operator<<(QDebug& s, const KWin::DrmOutput *output)
{
if (!output)
return s.nospace() << "DrmOutput()";

View File

@ -31,6 +31,7 @@ class DrmPlane;
class DrmConnector;
class DrmCrtc;
class Cursor;
class DrmGpu;
class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput
{
@ -45,7 +46,7 @@ public:
bool showCursor();
bool hideCursor();
void updateCursor();
void moveCursor(Cursor* cursor, const QPoint &globalPos);
void moveCursor(Cursor *cursor, const QPoint &globalPos);
bool init(drmModeConnector *connector);
bool present(DrmBuffer *buffer);
void pageFlipped();
@ -91,12 +92,17 @@ public:
* @return true if the hardware realizes the transform without further assistance
*/
bool hardwareTransforms() const;
DrmGpu *gpu() {
return m_gpu;
}
private:
friend class DrmGpu;
friend class DrmBackend;
friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc
// and save the connector ids in the DrmCrtc instance.
DrmOutput(DrmBackend *backend);
DrmOutput(DrmBackend *backend, DrmGpu* gpu);
bool presentAtomically(DrmBuffer *buffer);
@ -139,6 +145,7 @@ private:
QMatrix4x4 matrixDisplay(const QSize &s) const;
DrmBackend *m_backend;
DrmGpu *m_gpu;
DrmConnector *m_conn = nullptr;
DrmCrtc *m_crtc = nullptr;
bool m_lastGbm = false;
@ -150,8 +157,8 @@ private:
QByteArray m_uuid;
uint32_t m_blobId = 0;
DrmPlane* m_primaryPlane = nullptr;
DrmPlane* m_cursorPlane = nullptr;
DrmPlane *m_primaryPlane = nullptr;
DrmPlane *m_cursorPlane = nullptr;
QVector<DrmPlane*> m_nextPlanesFlipList;
bool m_pageFlipPending = false;
bool m_atomicOffPending = false;

View File

@ -15,6 +15,7 @@
#include "logging.h"
#include "options.h"
#include "screens.h"
#include "drm_gpu.h"
// kwin libs
#include <kwinglplatform.h>
#include <kwineglimagetexture.h>
@ -24,15 +25,16 @@
namespace KWin
{
EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend)
EglGbmBackend::EglGbmBackend(DrmBackend *drmBackend, DrmGpu *gpu)
: AbstractEglBackend()
, m_backend(drmBackend)
, m_gpu(gpu)
{
// Egl is always direct rendering.
setIsDirectRendering(true);
setSyncsToVBlank(true);
connect(m_backend, &DrmBackend::outputAdded, this, &EglGbmBackend::createOutput);
connect(m_backend, &DrmBackend::outputRemoved, this, &EglGbmBackend::removeOutput);
connect(m_gpu, &DrmGpu::outputEnabled, this, &EglGbmBackend::createOutput);
connect(m_gpu, &DrmGpu::outputDisabled, this, &EglGbmBackend::removeOutput);
}
EglGbmBackend::~EglGbmBackend()
@ -72,7 +74,7 @@ void EglGbmBackend::cleanupOutput(Output &output)
bool EglGbmBackend::initializeEgl()
{
initClientExtensions();
EGLDisplay display = m_backend->sceneEglDisplay();
EGLDisplay display = eglDisplay();
// Use eglGetPlatformDisplayEXT() to get the display pointer
// if the implementation supports it.
@ -88,12 +90,12 @@ bool EglGbmBackend::initializeEgl()
return false;
}
auto device = gbm_create_device(m_backend->fd());
auto device = gbm_create_device(m_gpu->fd());
if (!device) {
setFailed("Could not create gbm device");
return false;
}
m_backend->setGbmDevice(device);
m_gpu->setGbmDevice(device);
display = eglGetPlatformDisplayEXT(platform, device, nullptr);
}
@ -147,7 +149,7 @@ bool EglGbmBackend::initRenderingContext()
std::shared_ptr<GbmSurface> EglGbmBackend::createGbmSurface(const QSize &size) const
{
auto gbmSurface = std::make_shared<GbmSurface>(m_backend->gbmDevice(),
auto gbmSurface = std::make_shared<GbmSurface>(m_gpu->gbmDevice(),
size.width(), size.height(),
GBM_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
@ -460,7 +462,7 @@ void EglGbmBackend::presentOnOutput(Output &output, const QRegion &damagedRegion
} else {
eglSwapBuffers(eglDisplay(), output.eglSurface);
}
output.buffer = m_backend->createBuffer(output.gbmSurface);
output.buffer = new DrmSurfaceBuffer(m_gpu->fd(), output.gbmSurface);
Q_EMIT output.output->outputChange(damagedRegion);
m_backend->present(output.buffer, output.output);

View File

@ -22,6 +22,7 @@ class DrmBuffer;
class DrmSurfaceBuffer;
class DrmOutput;
class GbmSurface;
class DrmGpu;
/**
* @brief OpenGL Backend using Egl on a GBM surface.
@ -30,7 +31,7 @@ class EglGbmBackend : public AbstractEglBackend
{
Q_OBJECT
public:
EglGbmBackend(DrmBackend *drmBackend);
EglGbmBackend(DrmBackend *drmBackend, DrmGpu *gpu);
~EglGbmBackend() override;
void screenGeometryChanged(const QSize &size) override;
SceneOpenGLTexturePrivate *createBackendTexture(SceneOpenGLTexture *texture) override;
@ -93,6 +94,7 @@ private:
void cleanupFramebuffer(Output &output);
DrmBackend *m_backend;
DrmGpu *m_gpu;
QVector<Output> m_outputs;
friend class EglGbmTexture;
};

View File

@ -26,6 +26,7 @@
#include <KWaylandServer/display.h>
#include <KWaylandServer/resource.h>
#include <wayland-server-core.h>
#include "drm_gpu.h"
namespace KWin
{
@ -71,13 +72,13 @@ PFNEGLQUERYWAYLANDBUFFERWL pEglQueryWaylandBufferWL = nullptr;
#define EGL_WAYLAND_Y_INVERTED_WL 0x31DB
#endif
EglStreamBackend::EglStreamBackend(DrmBackend *b)
: AbstractEglBackend(), m_backend(b)
EglStreamBackend::EglStreamBackend(DrmBackend *b, DrmGpu *gpu)
: AbstractEglBackend(), m_backend(b), m_gpu(gpu)
{
setIsDirectRendering(true);
setSyncsToVBlank(true);
connect(m_backend, &DrmBackend::outputAdded, this, &EglStreamBackend::createOutput);
connect(m_backend, &DrmBackend::outputRemoved, this,
connect(m_gpu, &DrmGpu::outputEnabled, this, &EglStreamBackend::createOutput);
connect(m_gpu, &DrmGpu::outputDisabled, this,
[this] (DrmOutput *output) {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
[output] (const Output &o) {
@ -120,7 +121,7 @@ void EglStreamBackend::cleanupOutput(const Output &o)
bool EglStreamBackend::initializeEgl()
{
initClientExtensions();
EGLDisplay display = m_backend->sceneEglDisplay();
EGLDisplay display = eglDisplay();
if (display == EGL_NO_DISPLAY) {
if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_device_base")) &&
!(hasClientExtension(QByteArrayLiteral("EGL_EXT_device_query")) &&
@ -138,7 +139,7 @@ bool EglStreamBackend::initializeEgl()
eglQueryDevicesEXT(numDevices, devices.data(), &numDevices);
for (EGLDeviceEXT device : devices) {
const char *drmDeviceFile = eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
if (m_backend->devNode() != drmDeviceFile) {
if (m_gpu->devNode().compare(drmDeviceFile)) {
continue;
}
@ -150,7 +151,7 @@ bool EglStreamBackend::initializeEgl()
}
EGLint platformAttribs[] = {
EGL_DRM_MASTER_FD_EXT, m_backend->fd(),
EGL_DRM_MASTER_FD_EXT, m_gpu->fd(),
EGL_NONE
};
display = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, device, platformAttribs);
@ -254,7 +255,7 @@ void EglStreamBackend::attachStreamConsumer(KWaylandServer::SurfaceInterface *su
void EglStreamBackend::init()
{
if (!m_backend->atomicModeSetting()) {
if (!m_gpu->atomicModeSetting()) {
setFailed("EGLStream backend requires atomic modesetting");
return;
}
@ -307,7 +308,7 @@ bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput)
delete o.buffer;
}
// dumb buffer used for modesetting
o.buffer = m_backend->createBuffer(drmOutput->pixelSize());
o.buffer = m_gpu->createBuffer(drmOutput->pixelSize());
EGLAttrib streamAttribs[] = {
EGL_STREAM_FIFO_LENGTH_KHR, 0, // mailbox mode

View File

@ -19,6 +19,7 @@ namespace KWin
class DrmBackend;
class DrmOutput;
class DrmBuffer;
class DrmGpu;
/**
* @brief OpenGL Backend using Egl with an EGLDevice.
@ -27,7 +28,7 @@ class EglStreamBackend : public AbstractEglBackend
{
Q_OBJECT
public:
EglStreamBackend(DrmBackend *b);
EglStreamBackend(DrmBackend *b, DrmGpu *gpu);
~EglStreamBackend() override;
void screenGeometryChanged(const QSize &size) override;
SceneOpenGLTexturePrivate *createBackendTexture(SceneOpenGLTexture *texture) override;
@ -70,6 +71,7 @@ private:
void createOutput(DrmOutput *output);
DrmBackend *m_backend;
DrmGpu *m_gpu;
QVector<Output> m_outputs;
KWaylandServer::EglStreamControllerInterface *m_eglStreamControllerInterface;
QHash<KWaylandServer::SurfaceInterface *, StreamTexture> m_streamTextures;

View File

@ -10,21 +10,23 @@
#include "drm_backend.h"
#include "drm_output.h"
#include "logind.h"
#include "drm_gpu.h"
namespace KWin
{
DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend)
DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu)
: QObject()
, QPainterBackend()
, m_backend(backend)
, m_gpu(gpu)
{
const auto outputs = m_backend->drmOutputs();
for (auto output: outputs) {
initOutput(output);
}
connect(m_backend, &DrmBackend::outputAdded, this, &DrmQPainterBackend::initOutput);
connect(m_backend, &DrmBackend::outputRemoved, this,
connect(m_gpu, &DrmGpu::outputAdded, this, &DrmQPainterBackend::initOutput);
connect(m_gpu, &DrmGpu::outputDisabled, this,
[this] (DrmOutput *o) {
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
[o] (const Output &output) {
@ -53,7 +55,7 @@ void DrmQPainterBackend::initOutput(DrmOutput *output)
{
Output o;
auto initBuffer = [&o, output, this] (int index) {
o.buffer[index] = m_backend->createBuffer(output->pixelSize());
o.buffer[index] = m_gpu->createBuffer(output->pixelSize());
if (o.buffer[index]->map()) {
o.buffer[index]->image()->fill(Qt::black);
}
@ -71,7 +73,7 @@ void DrmQPainterBackend::initOutput(DrmOutput *output)
delete (*it).buffer[0];
delete (*it).buffer[1];
auto initBuffer = [it, output, this] (int index) {
it->buffer[index] = m_backend->createBuffer(output->pixelSize());
it->buffer[index] = m_gpu->createBuffer(output->pixelSize());
if (it->buffer[index]->map()) {
it->buffer[index]->image()->fill(Qt::black);
}

View File

@ -18,12 +18,13 @@ namespace KWin
class DrmBackend;
class DrmDumbBuffer;
class DrmOutput;
class DrmGpu;
class DrmQPainterBackend : public QObject, public QPainterBackend
{
Q_OBJECT
public:
DrmQPainterBackend(DrmBackend *backend);
DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu);
~DrmQPainterBackend() override;
QImage *buffer() override;
@ -43,6 +44,7 @@ private:
};
QVector<Output> m_outputs;
DrmBackend *m_backend;
DrmGpu *m_gpu;
};
}

View File

@ -90,7 +90,7 @@ void FramebufferBackend::openFrameBuffer()
VirtualTerminal::self()->init();
QString framebufferDevice = deviceIdentifier().constData();
if (framebufferDevice.isEmpty()) {
framebufferDevice = QString(Udev().primaryFramebuffer()->devNode());
framebufferDevice = QString(Udev().listFramebuffers().at(0)->devNode());
}
int fd = LogindIntegration::self()->takeDevice(framebufferDevice.toUtf8().constData());
qCDebug(KWIN_FB) << "Using frame buffer device:" << framebufferDevice;

View File

@ -42,7 +42,7 @@ public:
};
void addMatch(Match match, const char *name);
void scan();
UdevDevice::Ptr find(std::function<bool(const UdevDevice::Ptr &)> test);
std::vector<UdevDevice::Ptr> find();
private:
Udev *m_udev;
@ -91,14 +91,15 @@ void UdevEnumerate::scan()
udev_enumerate_scan_devices(m_enumerate.data());
}
UdevDevice::Ptr UdevEnumerate::find(std::function<bool(const UdevDevice::Ptr &device)> test)
std::vector<UdevDevice::Ptr> UdevEnumerate::find()
{
std::vector<UdevDevice::Ptr> vect;
if (m_enumerate.isNull()) {
return UdevDevice::Ptr();
vect.push_back( UdevDevice::Ptr() );
return vect;
}
QString defaultSeat = QStringLiteral("seat0");
udev_list_entry *it = udev_enumerate_get_list_entry(m_enumerate.data());
UdevDevice::Ptr firstFound;
while (it) {
auto current = it;
it = udev_list_entry_get_next(it);
@ -113,62 +114,76 @@ UdevDevice::Ptr UdevEnumerate::find(std::function<bool(const UdevDevice::Ptr &de
if (deviceSeat != LogindIntegration::self()->seat()) {
continue;
}
if (test(device)) {
return device;
}
if (!firstFound) {
firstFound.swap(device);
}
if (device->getParentWithSubsystemDevType("pci"))
vect.push_back(std::move(device));
}
return firstFound;
return vect;
}
UdevDevice::Ptr Udev::primaryGpu()
std::vector<UdevDevice::Ptr> Udev::listGPUs()
{
if (!m_udev) {
return UdevDevice::Ptr();
std::vector<UdevDevice::Ptr> vect;
vect.push_back(UdevDevice::Ptr());
return vect;
}
#if defined(Q_OS_FREEBSD)
return deviceFromSyspath("/dev/dri/card0");
std::vector<UdevDevice::Ptr> r;
r.push_back(deviceFromSyspath("/dev/dri/card0"));
return r;
#else
UdevEnumerate enumerate(this);
enumerate.addMatch(UdevEnumerate::Match::SubSystem, "drm");
enumerate.addMatch(UdevEnumerate::Match::SysName, "card[0-9]*");
enumerate.addMatch(UdevEnumerate::Match::SysName, "card[0-9]");
enumerate.scan();
return enumerate.find([](const UdevDevice::Ptr &device) {
auto pci = device->getParentWithSubsystemDevType("pci");
if (!pci) {
return false;
}
const char *systAttrValue = udev_device_get_sysattr_value(pci, "boot_vga");
auto vect = enumerate.find();
std::sort(vect.begin(), vect.end(), [](const UdevDevice::Ptr &device1, const UdevDevice::Ptr &device2) {
auto pci1 = device1->getParentWithSubsystemDevType("pci");
auto pci2 = device2->getParentWithSubsystemDevType("pci");
// if set as boot GPU, prefer 1
const char *systAttrValue = udev_device_get_sysattr_value(pci1, "boot_vga");
if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) {
return true;
}
return false;
// if set as boot GPU, prefer 2
systAttrValue = udev_device_get_sysattr_value(pci2, "boot_vga");
if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) {
return false;
}
return udev_device_get_sysnum(pci1) > udev_device_get_sysnum(pci2);
});
return vect;
#endif
}
UdevDevice::Ptr Udev::primaryFramebuffer()
std::vector<UdevDevice::Ptr> Udev::listFramebuffers()
{
if (!m_udev) {
return UdevDevice::Ptr();
std::vector<UdevDevice::Ptr> vect;
vect.push_back(UdevDevice::Ptr());
return vect;
}
UdevEnumerate enumerate(this);
enumerate.addMatch(UdevEnumerate::Match::SubSystem, "graphics");
enumerate.addMatch(UdevEnumerate::Match::SysName, "fb[0-9]*");
enumerate.addMatch(UdevEnumerate::Match::SysName, "fb[0-9]");
enumerate.scan();
return enumerate.find([](const UdevDevice::Ptr &device) {
auto pci = device->getParentWithSubsystemDevType("pci");
if (!pci) {
return false;
}
const char *systAttrValue = udev_device_get_sysattr_value(pci, "boot_vga");
auto vect = enumerate.find();
std::sort(vect.begin(), vect.end(), [](const UdevDevice::Ptr &device1, const UdevDevice::Ptr &device2) {
auto pci1 = device1->getParentWithSubsystemDevType("pci");
auto pci2 = device2->getParentWithSubsystemDevType("pci");
// if set as boot GPU, prefer 1
const char *systAttrValue = udev_device_get_sysattr_value(pci1, "boot_vga");
if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) {
return true;
}
return false;
// if set as boot GPU, prefer 2
systAttrValue = udev_device_get_sysattr_value(pci2, "boot_vga");
if (systAttrValue && qstrcmp(systAttrValue, "1") == 0) {
return false;
}
return udev_device_get_sysnum(pci1) > udev_device_get_sysnum(pci2);
});
return vect;
}
UdevDevice::Ptr Udev::deviceFromSyspath(const char *syspath)

7
udev.h
View File

@ -11,6 +11,9 @@
#include <memory>
#include <kwin_export.h>
#include <vector>
#include <QVector>
struct udev;
struct udev_device;
struct udev_monitor;
@ -70,8 +73,8 @@ public:
bool isValid() const {
return m_udev != nullptr;
}
UdevDevice::Ptr primaryGpu();
UdevDevice::Ptr primaryFramebuffer();
std::vector<UdevDevice::Ptr> listGPUs();
std::vector<UdevDevice::Ptr> listFramebuffers();
UdevDevice::Ptr deviceFromSyspath(const char *syspath);
UdevMonitor *monitor();
operator udev*() const {