Multi-GPU output support on Wayland
This commit sets up udev and the DrmBackend to list and use all GPUs. BUG: 425586 BUG: 417323
This commit is contained in:
parent
1f6e615ec0
commit
b50f747876
18 changed files with 635 additions and 424 deletions
|
@ -11,6 +11,7 @@ set(DRM_SOURCES
|
|||
logging.cpp
|
||||
scene_qpainter_drm_backend.cpp
|
||||
screens_drm.cpp
|
||||
drm_gpu.cpp
|
||||
)
|
||||
|
||||
if (HAVE_GBM)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
273
plugins/platforms/drm/drm_gpu.cpp
Normal file
273
plugins/platforms/drm/drm_gpu.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
111
plugins/platforms/drm/drm_gpu.h
Normal file
111
plugins/platforms/drm/drm_gpu.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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()";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
79
udev.cpp
79
udev.cpp
|
@ -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
7
udev.h
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue