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 logging.cpp
scene_qpainter_drm_backend.cpp scene_qpainter_drm_backend.cpp
screens_drm.cpp screens_drm.cpp
drm_gpu.cpp
) )
if (HAVE_GBM) if (HAVE_GBM)

View File

@ -47,6 +47,8 @@
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include <libdrm/drm_mode.h> #include <libdrm/drm_mode.h>
#include "drm_gpu.h"
#ifndef DRM_CAP_CURSOR_WIDTH #ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8 #define DRM_CAP_CURSOR_WIDTH 0x8
#endif #endif
@ -77,21 +79,12 @@ DrmBackend::DrmBackend(QObject *parent)
DrmBackend::~DrmBackend() DrmBackend::~DrmBackend()
{ {
#if HAVE_GBM if (m_gpus.size() > 0) {
if (m_gbmDevice) {
gbm_device_destroy(m_gbmDevice);
}
#endif
if (m_fd >= 0) {
// wait for pageflips // wait for pageflips
while (m_pageFlipsPending != 0) { while (m_pageFlipsPending != 0) {
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents); QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
} }
qDeleteAll(m_gpus);
qDeleteAll(m_planes);
qDeleteAll(m_crtcs);
qDeleteAll(m_connectors);
close(m_fd);
} }
} }
@ -243,71 +236,45 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u
void DrmBackend::openDrm() void DrmBackend::openDrm()
{ {
connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate); connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate);
UdevDevice::Ptr device = m_udev->primaryGpu(); std::vector<UdevDevice::Ptr> devices = m_udev->listGPUs();
if (!device) { if (devices.size() == 0) {
qCWarning(KWIN_DRM) << "Did not find a GPU"; qCWarning(KWIN_DRM) << "Did not find a GPU";
return; return;
} }
m_devNode = qEnvironmentVariableIsSet("KWIN_DRM_DEVICE_NODE") ? qgetenv("KWIN_DRM_DEVICE_NODE") : QByteArray(device->devNode());
int fd = LogindIntegration::self()->takeDevice(m_devNode.constData()); for (unsigned int gpu_index = 0; gpu_index < devices.size(); gpu_index++) {
if (fd < 0) { auto device = std::move(devices.at(gpu_index));
qCWarning(KWIN_DRM) << "failed to open drm device at" << m_devNode; auto devNode = QByteArray(device->devNode());
return; int fd = LogindIntegration::self()->takeDevice(devNode.constData());
} if (fd < 0) {
m_fd = fd; qCWarning(KWIN_DRM) << "failed to open drm device at" << devNode;
m_active = true; return;
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);
} }
); m_active = true;
m_drmId = device->sysNum(); 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) // trying to activate Atomic Mode Setting (this means also Universal Planes)
if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS")) { if (!qEnvironmentVariableIsSet("KWIN_DRM_NO_AMS")) {
if (drmSetClientCap(m_fd, DRM_CLIENT_CAP_ATOMIC, 1) == 0) { for (auto gpu : m_gpus)
qCDebug(KWIN_DRM) << "Using Atomic Mode Setting."; gpu->tryAMS();
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.";
}
} }
initCursor(); initCursor();
@ -317,7 +284,7 @@ void DrmBackend::openDrm()
if (m_outputs.isEmpty()) { if (m_outputs.isEmpty()) {
qCDebug(KWIN_DRM) << "No connected outputs found on startup."; qCDebug(KWIN_DRM) << "No connected outputs found on startup.";
} }
// setup udevMonitor // setup udevMonitor
if (m_udevMonitor) { if (m_udevMonitor) {
m_udevMonitor->filterSubsystemDevType("drm"); m_udevMonitor->filterSubsystemDevType("drm");
@ -330,7 +297,14 @@ void DrmBackend::openDrm()
if (!device) { if (!device) {
return; 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; return;
} }
if (device->hasProperty("HOTPLUG", "1")) { if (device->hasProperty("HOTPLUG", "1")) {
@ -346,176 +320,34 @@ void DrmBackend::openDrm()
setReady(true); 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() bool DrmBackend::updateOutputs()
{ {
if (m_fd < 0) { if (m_gpus.size() == 0) {
return false; return false;
} }
for (auto gpu : m_gpus)
DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd)); gpu->updateOutputs();
if (!resources) {
qCWarning(KWIN_DRM) << "drmModeGetResources failed"; std::sort(m_outputs.begin(), m_outputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); });
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;
readOutputsConfiguration(); readOutputsConfiguration();
updateOutputsEnabled(); updateOutputsEnabled();
if (!m_outputs.isEmpty()) { if (!m_outputs.isEmpty()) {
emit screensQueried(); emit screensQueried();
} }
for(DrmOutput* removedOutput : removedOutputs) {
removedOutput->teardown();
removedOutput->m_crtc = nullptr;
removedOutput->m_conn = nullptr;
}
qDeleteAll(oldConnectors);
qDeleteAll(oldCrtcs);
return true; return true;
} }
@ -574,12 +406,12 @@ void DrmBackend::enableOutput(DrmOutput *output, bool enable)
if (enable) { if (enable) {
Q_ASSERT(!m_enabledOutputs.contains(output)); Q_ASSERT(!m_enabledOutputs.contains(output));
m_enabledOutputs << output; m_enabledOutputs << output;
emit outputAdded(output); emit output->gpu()->outputEnabled(output);
} else { } else {
Q_ASSERT(m_enabledOutputs.contains(output)); Q_ASSERT(m_enabledOutputs.contains(output));
m_enabledOutputs.removeOne(output); m_enabledOutputs.removeOne(output);
Q_ASSERT(!m_enabledOutputs.contains(output)); Q_ASSERT(!m_enabledOutputs.contains(output));
emit outputRemoved(output); emit output->gpu()->outputDisabled(output);
} }
updateOutputsEnabled(); updateOutputsEnabled();
checkOutputsAreOn(); checkOutputsAreOn();
@ -640,6 +472,7 @@ void DrmBackend::initCursor()
if (m_cursorEnabled) { if (m_cursorEnabled) {
if (!(*it)->showCursor()) { if (!(*it)->showCursor()) {
setSoftWareCursor(true); setSoftWareCursor(true);
break;
} }
} else { } else {
(*it)->hideCursor(); (*it)->hideCursor();
@ -649,15 +482,17 @@ void DrmBackend::initCursor()
); );
uint64_t capability = 0; uint64_t capability = 0;
QSize cursorSize; QSize cursorSize;
if (drmGetCap(m_fd, DRM_CAP_CURSOR_WIDTH, &capability) == 0) { cursorSize.setWidth(64);
cursorSize.setWidth(capability); for (auto gpu : m_gpus) {
} else { if (drmGetCap(gpu->fd(), DRM_CAP_CURSOR_WIDTH, &capability) == 0) {
cursorSize.setWidth(64); cursorSize.setWidth(capability);
}
} }
if (drmGetCap(m_fd, DRM_CAP_CURSOR_HEIGHT, &capability) == 0) { cursorSize.setHeight(64);
cursorSize.setHeight(capability); for (auto gpu : m_gpus) {
} else { if (drmGetCap(gpu->fd(), DRM_CAP_CURSOR_HEIGHT, &capability) == 0) {
cursorSize.setHeight(64); cursorSize.setHeight(capability);
}
} }
m_cursorSize = cursorSize; m_cursorSize = cursorSize;
// now we have screens and can set cursors, so start tracking // now we have screens and can set cursors, so start tracking
@ -735,7 +570,7 @@ Screens *DrmBackend::createScreens(QObject *parent)
QPainterBackend *DrmBackend::createQPainterBackend() QPainterBackend *DrmBackend::createQPainterBackend()
{ {
m_deleteBufferAfterPageFlip = false; m_deleteBufferAfterPageFlip = false;
return new DrmQPainterBackend(this); return new DrmQPainterBackend(this, m_gpus.at(0));
} }
OpenGLBackend *DrmBackend::createOpenGLBackend() OpenGLBackend *DrmBackend::createOpenGLBackend()
@ -743,32 +578,18 @@ OpenGLBackend *DrmBackend::createOpenGLBackend()
#if HAVE_EGL_STREAMS #if HAVE_EGL_STREAMS
if (m_useEglStreams) { if (m_useEglStreams) {
m_deleteBufferAfterPageFlip = false; m_deleteBufferAfterPageFlip = false;
return new EglStreamBackend(this); return new EglStreamBackend(this, m_gpus.at(0));
} }
#endif #endif
#if HAVE_GBM #if HAVE_GBM
m_deleteBufferAfterPageFlip = true; m_deleteBufferAfterPageFlip = true;
return new EglGbmBackend(this); return new EglGbmBackend(this, m_gpus.at(0));
#else #else
return Platform::createOpenGLBackend(); return Platform::createOpenGLBackend();
#endif #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() void DrmBackend::updateOutputsEnabled()
{ {
bool enabled = false; bool enabled = false;
@ -801,7 +622,9 @@ QString DrmBackend::supportInformation() const
s.nospace(); s.nospace();
s << "Name: " << "DRM" << Qt::endl; s << "Name: " << "DRM" << Qt::endl;
s << "Active: " << m_active << 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 #if HAVE_EGL_STREAMS
s << "Using EGL Streams: " << m_useEglStreams << Qt::endl; s << "Using EGL Streams: " << m_useEglStreams << Qt::endl;
#endif #endif
@ -811,7 +634,10 @@ QString DrmBackend::supportInformation() const
DmaBufTexture *DrmBackend::createDmaBufTexture(const QSize &size) DmaBufTexture *DrmBackend::createDmaBufTexture(const QSize &size)
{ {
#if HAVE_GBM #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 #else
return nullptr; return nullptr;
#endif #endif

View File

@ -43,6 +43,7 @@ class DrmCrtc;
class DrmConnector; class DrmConnector;
class GbmSurface; class GbmSurface;
class Cursor; class Cursor;
class DrmGpu;
class KWIN_EXPORT DrmBackend : public Platform class KWIN_EXPORT DrmBackend : public Platform
{ {
@ -60,16 +61,9 @@ public:
void init() override; void init() override;
void prepareShutdown() 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); bool present(DrmBuffer *buffer, DrmOutput *output);
int fd() const {
return m_fd;
}
Outputs outputs() const override; Outputs outputs() const override;
Outputs enabledOutputs() const override; Outputs enabledOutputs() const override;
QVector<DrmOutput*> drmOutputs() const { QVector<DrmOutput*> drmOutputs() const {
@ -81,13 +75,6 @@ public:
void enableOutput(DrmOutput *output, bool enable); void enableOutput(DrmOutput *output, bool enable);
QVector<DrmPlane*> planes() const {
return m_planes;
}
QVector<DrmPlane*> overlayPlanes() const {
return m_overlayPlanes;
}
void createDpmsFilter(); void createDpmsFilter();
void checkOutputsAreOn(); void checkOutputsAreOn();
@ -95,21 +82,6 @@ public:
bool deleteBufferAfterPageFlip() const { bool deleteBufferAfterPageFlip() const {
return m_deleteBufferAfterPageFlip; 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 #if HAVE_EGL_STREAMS
bool useEglStreams() const { bool useEglStreams() const {
@ -128,22 +100,13 @@ public:
public Q_SLOTS: public Q_SLOTS:
void turnOutputsOn(); 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: protected:
void doHideCursor() override; void doHideCursor() override;
void doShowCursor() override; void doShowCursor() override;
private: 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); static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data);
void openDrm(); void openDrm();
void activate(bool active); void activate(bool active);
@ -161,32 +124,22 @@ private:
void updateOutputsEnabled(); void updateOutputsEnabled();
QScopedPointer<Udev> m_udev; QScopedPointer<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor; 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) // active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs; QVector<DrmOutput*> m_outputs;
// active and enabled pipelines (above + wl_output) // active and enabled pipelines (above + wl_output)
QVector<DrmOutput*> m_enabledOutputs; QVector<DrmOutput*> m_enabledOutputs;
bool m_deleteBufferAfterPageFlip; bool m_deleteBufferAfterPageFlip;
bool m_atomicModeSetting = false;
bool m_cursorEnabled = false; bool m_cursorEnabled = false;
QSize m_cursorSize; QSize m_cursorSize;
int m_pageFlipsPending = 0; int m_pageFlipsPending = 0;
bool m_active = false; bool m_active = false;
QByteArray m_devNode;
#if HAVE_EGL_STREAMS #if HAVE_EGL_STREAMS
bool m_useEglStreams = false; bool m_useEglStreams = false;
#endif #endif
// all available planes: primarys, cursors and overlays QVector<DrmGpu*> m_gpus;
QVector<DrmPlane*> m_planes;
QVector<DrmPlane*> m_overlayPlanes;
QScopedPointer<DpmsInputEventFilter> m_dpmsFilter; 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_buffer.h"
#include "drm_pointer.h" #include "drm_pointer.h"
#include "logging.h" #include "logging.h"
#include "drm_gpu.h"
namespace KWin namespace KWin
{ {
DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, int resIndex) DrmCrtc::DrmCrtc(uint32_t crtc_id, DrmBackend *backend, DrmGpu *gpu, int resIndex)
: DrmObject(crtc_id, backend->fd()), : DrmObject(crtc_id, gpu->fd()),
m_resIndex(resIndex), 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) { if (modeCrtc) {
m_gammaRampSize = modeCrtc->gamma_size; m_gammaRampSize = modeCrtc->gamma_size;
} }
@ -81,12 +83,12 @@ bool DrmCrtc::blank()
return false; return false;
} }
if (m_backend->atomicModeSetting()) { if (m_gpu->atomicModeSetting()) {
return false; return false;
} }
if (!m_blackBuffer) { if (!m_blackBuffer) {
DrmDumbBuffer *blackBuffer = m_backend->createBuffer(m_output->pixelSize()); DrmDumbBuffer *blackBuffer = m_gpu->createBuffer(m_output->pixelSize());
if (!blackBuffer->map()) { if (!blackBuffer->map()) {
delete blackBuffer; delete blackBuffer;
return false; return false;
@ -113,7 +115,7 @@ bool DrmCrtc::setGammaRamp(const GammaRamp &gamma)
uint16_t *green = const_cast<uint16_t *>(gamma.green()); uint16_t *green = const_cast<uint16_t *>(gamma.green());
uint16_t *blue = const_cast<uint16_t *>(gamma.blue()); 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); gamma.size(), red, green, blue);
return !isError; return !isError;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

7
udev.h
View File

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