From 1041ef8275f4f07c6189205cb34043d0b6155710 Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Tue, 27 Jul 2021 01:11:50 +0200 Subject: [PATCH] platforms/drm: introduce virtual outputs When the last output gets disconnected, create a virtual output as a placeholder until we have access to a physical output again. While this placecholder never gets rendered to, with virtual outputs in general that is possible (with gbm and qpainter atm) and can be done for future use cases like wireless displays. CCBUG: 420160 CCBUG: 438839 --- src/abstract_wayland_output.cpp | 10 ++ src/abstract_wayland_output.h | 4 + src/plugins/platforms/drm/CMakeLists.txt | 4 +- .../platforms/drm/abstract_egl_drm_backend.h | 15 +-- .../platforms/drm/drm_abstract_output.cpp | 59 ++++++++++ .../platforms/drm/drm_abstract_output.h | 55 +++++++++ src/plugins/platforms/drm/drm_backend.cpp | 72 ++++++++---- src/plugins/platforms/drm/drm_backend.h | 22 ++-- src/plugins/platforms/drm/drm_gpu.cpp | 46 ++++++-- src/plugins/platforms/drm/drm_gpu.h | 19 ++- src/plugins/platforms/drm/drm_output.cpp | 43 ++++--- src/plugins/platforms/drm/drm_output.h | 42 +++---- src/plugins/platforms/drm/drm_pipeline.cpp | 2 +- .../platforms/drm/drm_virtual_output.cpp | 108 ++++++++++++++++++ .../platforms/drm/drm_virtual_output.h | 60 ++++++++++ src/plugins/platforms/drm/egl_gbm_backend.cpp | 30 +++-- src/plugins/platforms/drm/egl_gbm_backend.h | 20 ++-- .../platforms/drm/egl_stream_backend.cpp | 67 ++++++----- .../platforms/drm/egl_stream_backend.h | 8 +- .../drm/scene_qpainter_drm_backend.cpp | 6 +- .../drm/scene_qpainter_drm_backend.h | 6 +- src/plugins/platforms/drm/shadowbuffer.cpp | 2 +- src/plugins/platforms/drm/shadowbuffer.h | 4 +- src/wayland_server.cpp | 18 ++- 24 files changed, 538 insertions(+), 184 deletions(-) create mode 100644 src/plugins/platforms/drm/drm_abstract_output.cpp create mode 100644 src/plugins/platforms/drm/drm_abstract_output.h create mode 100644 src/plugins/platforms/drm/drm_virtual_output.cpp create mode 100644 src/plugins/platforms/drm/drm_virtual_output.h diff --git a/src/abstract_wayland_output.cpp b/src/abstract_wayland_output.cpp index 3b7fb74b6e..8961614990 100644 --- a/src/abstract_wayland_output.cpp +++ b/src/abstract_wayland_output.cpp @@ -386,4 +386,14 @@ RenderLoop::VrrPolicy AbstractWaylandOutput::vrrPolicy() const return renderLoop()->vrrPolicy(); } +bool AbstractWaylandOutput::isPlaceholder() const +{ + return m_isPlaceholder; +} + +void AbstractWaylandOutput::setPlaceholder(bool isPlaceholder) +{ + m_isPlaceholder = isPlaceholder; +} + } diff --git a/src/abstract_wayland_output.h b/src/abstract_wayland_output.h index ce95278b14..4a98a08f4a 100644 --- a/src/abstract_wayland_output.h +++ b/src/abstract_wayland_output.h @@ -151,6 +151,8 @@ public: void setVrrPolicy(RenderLoop::VrrPolicy policy); RenderLoop::VrrPolicy vrrPolicy() const; + bool isPlaceholder() const; + Q_SIGNALS: void modeChanged(); void outputChange(const QRegion &damagedRegion); @@ -195,6 +197,7 @@ protected: void setCapabilityInternal(Capability capability, bool on = true); void setSubPixelInternal(SubPixel subPixel); void setOverscanInternal(uint32_t overscan); + void setPlaceholder(bool isPlaceholder); QSize orientateSize(const QSize &size) const; @@ -219,6 +222,7 @@ private: int m_recorders = 0; bool m_isEnabled = true; bool m_internal = false; + bool m_isPlaceholder = false; uint32_t m_overscan = 0; }; diff --git a/src/plugins/platforms/drm/CMakeLists.txt b/src/plugins/platforms/drm/CMakeLists.txt index f12fdb9978..2a59269acc 100644 --- a/src/plugins/platforms/drm/CMakeLists.txt +++ b/src/plugins/platforms/drm/CMakeLists.txt @@ -15,6 +15,8 @@ set(DRM_SOURCES dumb_swapchain.cpp shadowbuffer.cpp drm_pipeline.cpp + drm_abstract_output.cpp + drm_virtual_output.cpp ) if (HAVE_GBM) @@ -34,7 +36,7 @@ endif() add_library(KWinWaylandDrmBackend MODULE ${DRM_SOURCES}) set_target_properties(KWinWaylandDrmBackend PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/org.kde.kwin.waylandbackends/") -target_link_libraries(KWinWaylandDrmBackend kwin Libdrm::Libdrm SceneQPainterBackend SceneOpenGLBackend) +target_link_libraries(KWinWaylandDrmBackend kwin Libdrm::Libdrm SceneQPainterBackend SceneOpenGLBackend VsyncSupport) if (HAVE_GBM) target_link_libraries(KWinWaylandDrmBackend gbm::gbm) diff --git a/src/plugins/platforms/drm/abstract_egl_drm_backend.h b/src/plugins/platforms/drm/abstract_egl_drm_backend.h index 25be32f6ca..ef6a493c83 100644 --- a/src/plugins/platforms/drm/abstract_egl_drm_backend.h +++ b/src/plugins/platforms/drm/abstract_egl_drm_backend.h @@ -19,6 +19,7 @@ class DrmBackend; class DrmGpu; class DrmOutput; class DrmBuffer; +class DrmAbstractOutput; class AbstractEglDrmBackend : public AbstractEglBackend { @@ -26,27 +27,27 @@ class AbstractEglDrmBackend : public AbstractEglBackend public: virtual int screenCount() const = 0; - virtual bool addOutput(DrmOutput *output) = 0; - virtual void removeOutput(DrmOutput *output) = 0; - virtual bool swapBuffers(DrmOutput *output, const QRegion &dirty) { + virtual bool addOutput(DrmAbstractOutput *output) = 0; + virtual void removeOutput(DrmAbstractOutput *output) = 0; + virtual bool swapBuffers(DrmAbstractOutput *output, const QRegion &dirty) { Q_UNUSED(output) Q_UNUSED(dirty) return false; } - virtual bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) { + virtual bool exportFramebuffer(DrmAbstractOutput *output, void *data, const QSize &size, uint32_t stride) { Q_UNUSED(output) Q_UNUSED(data) Q_UNUSED(size) Q_UNUSED(stride) return false; } - virtual int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) { + virtual int exportFramebufferAsDmabuf(DrmAbstractOutput *output, uint32_t *format, uint32_t *stride) { Q_UNUSED(output) Q_UNUSED(format) Q_UNUSED(stride) return 0; } - virtual QRegion beginFrameForSecondaryGpu(DrmOutput *output) { + virtual QRegion beginFrameForSecondaryGpu(DrmAbstractOutput *output) { Q_UNUSED(output) return QRegion(); } @@ -55,7 +56,7 @@ public: return m_gpu; } - virtual QSharedPointer renderTestFrame(DrmOutput *output) = 0; + virtual QSharedPointer renderTestFrame(DrmAbstractOutput *output) = 0; static AbstractEglDrmBackend *renderingBackend() { return static_cast(primaryBackend()); diff --git a/src/plugins/platforms/drm/drm_abstract_output.cpp b/src/plugins/platforms/drm/drm_abstract_output.cpp new file mode 100644 index 0000000000..84023e1735 --- /dev/null +++ b/src/plugins/platforms/drm/drm_abstract_output.cpp @@ -0,0 +1,59 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#include "drm_abstract_output.h" +#include "drm_gpu.h" +#include "drm_backend.h" + +namespace KWin +{ + +DrmAbstractOutput::DrmAbstractOutput(DrmGpu *gpu) + : AbstractWaylandOutput(gpu->platform()) + , m_renderLoop(new RenderLoop(this)) + , m_gpu(gpu) +{ +} + +bool DrmAbstractOutput::initCursor(const QSize &cursorSize) +{ + Q_UNUSED(cursorSize) + return true; +} + +bool DrmAbstractOutput::showCursor() +{ + return true; +} + +bool DrmAbstractOutput::hideCursor() +{ + return true; +} + +bool DrmAbstractOutput::updateCursor() +{ + return true; +} + +bool DrmAbstractOutput::moveCursor() +{ + return true; +} + +DrmGpu *DrmAbstractOutput::gpu() const +{ + return m_gpu; +} + +RenderLoop *DrmAbstractOutput::renderLoop() const +{ + return m_renderLoop; +} + +} diff --git a/src/plugins/platforms/drm/drm_abstract_output.h b/src/plugins/platforms/drm/drm_abstract_output.h new file mode 100644 index 0000000000..619158c760 --- /dev/null +++ b/src/plugins/platforms/drm/drm_abstract_output.h @@ -0,0 +1,55 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once + +#include "abstract_wayland_output.h" + +namespace KWin +{ + +class DrmBackend; +class DrmGpu; +class DrmBuffer; +class GbmBuffer; + +class DrmAbstractOutput : public AbstractWaylandOutput +{ + Q_OBJECT +public: + + virtual bool initCursor(const QSize &cursorSize); + virtual bool showCursor(); + virtual bool hideCursor(); + virtual bool updateCursor(); + virtual bool moveCursor(); + + virtual bool present(const QSharedPointer &buffer, QRegion damagedRegion) = 0; + + virtual bool isDpmsEnabled() const = 0; + virtual GbmBuffer *currentBuffer() const = 0; + virtual QSize sourceSize() const = 0; + + DrmGpu *gpu() const; + RenderLoop *renderLoop() const override; + +protected: + friend class DrmBackend; + friend class DrmGpu; + DrmAbstractOutput(DrmGpu *gpu); + + virtual void updateMode(int modeIndex) override { + Q_UNUSED(modeIndex) + } + virtual void updateMode(uint32_t width, uint32_t height, uint32_t refreshRate) = 0; + + RenderLoop *m_renderLoop; + DrmGpu *m_gpu; +}; + +} diff --git a/src/plugins/platforms/drm/drm_backend.cpp b/src/plugins/platforms/drm/drm_backend.cpp index b37e973477..669880447d 100644 --- a/src/plugins/platforms/drm/drm_backend.cpp +++ b/src/plugins/platforms/drm/drm_backend.cpp @@ -21,6 +21,10 @@ #include "session.h" #include "udev.h" #include "wayland_server.h" +#include "drm_gpu.h" +#include "egl_multi_backend.h" +#include "drm_pipeline.h" +#include "drm_virtual_output.h" #if HAVE_GBM #include "egl_gbm_backend.h" #include @@ -142,19 +146,23 @@ void DrmBackend::reactivate() m_active = true; for (const auto &output : qAsConst(m_outputs)) { - output->pipeline()->updateProperties(); - if (output->isEnabled()) { - if (output->gpu()->atomicModeSetting() && !output->pipeline()->isConnected()) { - output->pipeline()->setActive(false); + if (auto drmOutput = qobject_cast(output)) { + drmOutput->pipeline()->updateProperties(); + if (drmOutput->isEnabled()) { + if (drmOutput->gpu()->atomicModeSetting() && !drmOutput->pipeline()->isConnected()) { + drmOutput->pipeline()->setActive(false); + } + drmOutput->showCursor(); + } else { + drmOutput->pipeline()->setActive(false); } - output->renderLoop()->uninhibit(); - output->showCursor(); - } else { - output->pipeline()->setActive(false); } + output->renderLoop()->uninhibit(); } for (const auto &output : qAsConst(m_outputs)) { - output->pipeline()->setActive(output->isEnabled()); + if (auto drmOutput = qobject_cast(output)) { + drmOutput->pipeline()->setActive(output->isEnabled()); + } } if (Compositor *compositor = Compositor::self()) { @@ -173,9 +181,8 @@ void DrmBackend::deactivate() return; } - for (DrmOutput *output : qAsConst(m_outputs)) { + for (const auto &output : qAsConst(m_outputs)) { if (output->isEnabled()) { - output->hideCursor(); output->renderLoop()->inhibit(); } } @@ -318,7 +325,7 @@ DrmGpu *DrmBackend::addGpu(const QString &fileName) return gpu; } -void DrmBackend::addOutput(DrmOutput *o) +void DrmBackend::addOutput(DrmAbstractOutput *o) { m_outputs.append(o); m_enabledOutputs.append(o); @@ -326,7 +333,7 @@ void DrmBackend::addOutput(DrmOutput *o) Q_EMIT outputEnabled(o); } -void DrmBackend::removeOutput(DrmOutput *o) +void DrmBackend::removeOutput(DrmAbstractOutput *o) { if (m_enabledOutputs.removeOne(o)) { Q_EMIT outputDisabled(o); @@ -351,19 +358,38 @@ void DrmBackend::updateOutputs() } } - std::sort(m_outputs.begin(), m_outputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->pipeline()->connector()->id() < b->pipeline()->connector()->id(); }); + if (m_outputs.isEmpty()) { + qCDebug(KWIN_DRM) << "adding placeholder output"; + m_placeHolderOutput = primaryGpu()->createVirtualOutput(); + // placeholder doesn't actually need to render anything + m_placeHolderOutput->renderLoop()->inhibit(); + } else if (m_placeHolderOutput && m_outputs.count() > 1) { + qCDebug(KWIN_DRM) << "removing placeholder output"; + primaryGpu()->removeVirtualOutput(m_placeHolderOutput); + m_placeHolderOutput = nullptr; + } + + std::sort(m_outputs.begin(), m_outputs.end(), [] (DrmAbstractOutput *a, DrmAbstractOutput *b) { + auto da = qobject_cast(a); + auto db = qobject_cast(b); + if (da && !db) { + return true; + } else if (da && db) { + return da->pipeline()->connector()->id() < db->pipeline()->connector()->id(); + } else { + return false; + } + }); if (oldOutputs != m_outputs) { readOutputsConfiguration(); } - if (!m_outputs.isEmpty()) { - Q_EMIT screensQueried(); - } + Q_EMIT screensQueried(); } namespace KWinKScreenIntegration { /// See KScreen::Output::hashMd5 - QString outputHash(DrmOutput *output) + QString outputHash(DrmAbstractOutput *output) { QCryptographicHash hash(QCryptographicHash::Md5); if (!output->edid().isEmpty()) { @@ -375,7 +401,7 @@ namespace KWinKScreenIntegration } /// See KScreen::Config::connectedOutputsHash in libkscreen - QString connectedOutputsHash(const QVector &outputs) + QString connectedOutputsHash(const QVector &outputs) { QStringList hashedOutputs; hashedOutputs.reserve(outputs.count()); @@ -387,7 +413,7 @@ namespace KWinKScreenIntegration return QString::fromLatin1(hash.toHex()); } - QMap outputsConfig(const QVector &outputs) + QMap outputsConfig(const QVector &outputs) { const QString kscreenJsonPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kscreen/") % connectedOutputsHash(outputs)); if (kscreenJsonPath.isEmpty()) { @@ -407,7 +433,7 @@ namespace KWinKScreenIntegration return {}; } - QMap ret; + QMap ret; const auto outputsJson = doc.array(); for (const auto &outputJson : outputsJson) { const auto outputObject = outputJson.toObject(); @@ -479,7 +505,7 @@ void DrmBackend::readOutputsConfiguration() } } -void DrmBackend::enableOutput(DrmOutput *output, bool enable) +void DrmBackend::enableOutput(DrmAbstractOutput *output, bool enable) { if (enable) { Q_ASSERT(!m_enabledOutputs.contains(output)); @@ -547,7 +573,7 @@ void DrmBackend::updateCursor() bool success = true; - for (DrmOutput *output : qAsConst(m_outputs)) { + for (DrmAbstractOutput *output : qAsConst(m_outputs)) { success = output->updateCursor(); if (!success) { qCDebug(KWIN_DRM) << "Failed to update cursor on output" << output->name(); diff --git a/src/plugins/platforms/drm/drm_backend.h b/src/plugins/platforms/drm/drm_backend.h index 0e6d6df90d..f26c7bf791 100644 --- a/src/plugins/platforms/drm/drm_backend.h +++ b/src/plugins/platforms/drm/drm_backend.h @@ -25,9 +25,10 @@ class Udev; class UdevMonitor; class UdevDevice; -class DrmOutput; +class DrmAbstractOutput; class Cursor; class DrmGpu; +class DrmVirtualOutput; class KWIN_EXPORT DrmBackend : public Platform { @@ -46,14 +47,14 @@ public: Outputs outputs() const override; Outputs enabledOutputs() const override; - QVector drmOutputs() const { + QVector drmOutputs() const { return m_outputs; } - QVector drmEnabledOutputs() const { + QVector drmEnabledOutputs() const { return m_enabledOutputs; } - void enableOutput(DrmOutput *output, bool enable); + void enableOutput(DrmAbstractOutput *output, bool enable); void createDpmsFilter(); void checkOutputsAreOn(); @@ -81,8 +82,8 @@ protected: private: friend class DrmGpu; - void addOutput(DrmOutput* output); - void removeOutput(DrmOutput* output); + void addOutput(DrmAbstractOutput* output); + void removeOutput(DrmAbstractOutput* output); void activate(bool active); void reactivate(); void deactivate(); @@ -97,10 +98,11 @@ private: QScopedPointer m_udev; QScopedPointer m_udevMonitor; Session *m_session = nullptr; - // active output pipelines (planes + crtc + encoder + connector) - QVector m_outputs; - // active and enabled pipelines (above + wl_output) - QVector m_enabledOutputs; + // all outputs, enabled and disabled + QVector m_outputs; + // only enabled outputs + QVector m_enabledOutputs; + DrmVirtualOutput *m_placeHolderOutput = nullptr; bool m_active = false; QVector m_gpus; diff --git a/src/plugins/platforms/drm/drm_gpu.cpp b/src/plugins/platforms/drm/drm_gpu.cpp index 405d7ec8f3..38449a9f53 100644 --- a/src/plugins/platforms/drm/drm_gpu.cpp +++ b/src/plugins/platforms/drm/drm_gpu.cpp @@ -19,6 +19,7 @@ #include "renderloop_p.h" #include "main.h" #include "drm_pipeline.h" +#include "drm_virtual_output.h" #if HAVE_GBM #include "egl_gbm_backend.h" @@ -91,7 +92,11 @@ DrmGpu::~DrmGpu() waitIdle(); const auto outputs = m_outputs; for (const auto &output : outputs) { - removeOutput(output); + if (auto drmOutput = qobject_cast(output)) { + removeOutput(drmOutput); + } else { + removeVirtualOutput(dynamic_cast(output)); + } } if (m_eglDisplay != EGL_NO_DISPLAY) { eglTerminate(m_eglDisplay); @@ -219,14 +224,14 @@ bool DrmGpu::updateOutputs() // check for outputs which got removed QVector removedOutputs; - auto it = m_outputs.begin(); - while (it != m_outputs.end()) { + auto it = m_drmOutputs.begin(); + while (it != m_drmOutputs.end()) { if (connectedOutputs.contains(*it)) { it++; continue; } DrmOutput *removed = *it; - it = m_outputs.erase(it); + it = m_drmOutputs.erase(it); removedOutputs.append(removed); } @@ -272,7 +277,7 @@ bool DrmGpu::updateOutputs() } auto pipeline = new DrmPipeline(this, con, crtc, primary); - DrmOutput *output = new DrmOutput(m_backend, this, pipeline); + DrmOutput *output = new DrmOutput(this, pipeline); // outputEnabled only adds it to the render backend but not to the platform // this is necessary for accurate pipeline tests @@ -296,6 +301,7 @@ bool DrmGpu::updateOutputs() qCDebug(KWIN_DRM, "For new output %s on GPU %s use mode %dx%d@%d", qPrintable(output->name()), qPrintable(m_devNode), output->modeSize().width(), output->modeSize().height(), output->refreshRate()); connectedOutputs << output; + m_outputs << output; outputDone = true; break; } @@ -304,8 +310,7 @@ bool DrmGpu::updateOutputs() } } } - std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_pipeline->connector()->id() < b->m_pipeline->connector()->id(); }); - m_outputs = connectedOutputs; + m_drmOutputs = connectedOutputs; for(DrmOutput *removedOutput : removedOutputs) { removeOutput(removedOutput); @@ -318,10 +323,10 @@ bool DrmGpu::updateOutputs() DrmOutput *DrmGpu::findOutput(quint32 connector) { - auto it = std::find_if(m_outputs.constBegin(), m_outputs.constEnd(), [connector] (DrmOutput *o) { + auto it = std::find_if(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [connector] (DrmOutput *o) { return o->m_pipeline->connector()->id() == connector; }); - if (it != m_outputs.constEnd()) { + if (it != m_drmOutputs.constEnd()) { return *it; } return nullptr; @@ -345,7 +350,7 @@ void DrmGpu::waitIdle() { m_socketNotifier->setEnabled(false); while (true) { - const bool idle = std::all_of(m_outputs.constBegin(), m_outputs.constEnd(), [](DrmOutput *output){ + const bool idle = std::all_of(m_drmOutputs.constBegin(), m_drmOutputs.constEnd(), [](DrmOutput *output){ return !output->m_pageFlipPending; }); if (idle) { @@ -397,7 +402,6 @@ static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsign { Q_UNUSED(fd) Q_UNUSED(frame) - auto backend = dynamic_cast(kwinApp()->platform()); if (!backend) { return; @@ -444,6 +448,7 @@ void DrmGpu::dispatchEvents() void DrmGpu::removeOutput(DrmOutput *output) { + m_drmOutputs.removeOne(output); m_outputs.removeOne(output); Q_EMIT outputDisabled(output); Q_EMIT outputRemoved(output); @@ -476,4 +481,23 @@ const QVector DrmGpu::pipelines() const return m_pipelines; } +DrmVirtualOutput *DrmGpu::createVirtualOutput() +{ + auto output = new DrmVirtualOutput(this); + output->setPlaceholder(true); + m_outputs << output; + Q_EMIT outputEnabled(output); + Q_EMIT outputAdded(output); + return output; +} + +void DrmGpu::removeVirtualOutput(DrmVirtualOutput *output) +{ + if (m_outputs.removeOne(output)) { + Q_EMIT outputDisabled(output); + Q_EMIT outputRemoved(output); + delete output; + } +} + } diff --git a/src/plugins/platforms/drm/drm_gpu.h b/src/plugins/platforms/drm/drm_gpu.h index c8d0450da7..b6ef14b025 100644 --- a/src/plugins/platforms/drm/drm_gpu.h +++ b/src/plugins/platforms/drm/drm_gpu.h @@ -31,6 +31,8 @@ class DrmConnector; class DrmBackend; class AbstractEglDrmBackend; class DrmPipeline; +class DrmAbstractOutput; +class DrmVirtualOutput; class DrmGpu : public QObject { @@ -40,7 +42,7 @@ public: ~DrmGpu(); // getters - QVector outputs() const { + QVector outputs() const { return m_outputs; } @@ -97,11 +99,14 @@ public: DrmBackend *platform() const; const QVector pipelines() const; + DrmVirtualOutput *createVirtualOutput(); + void removeVirtualOutput(DrmVirtualOutput *output); + Q_SIGNALS: - void outputAdded(DrmOutput *output); - void outputRemoved(DrmOutput *output); - void outputEnabled(DrmOutput *output); - void outputDisabled(DrmOutput *output); + void outputAdded(DrmAbstractOutput *output); + void outputRemoved(DrmAbstractOutput *output); + void outputEnabled(DrmAbstractOutput *output); + void outputDisabled(DrmAbstractOutput *output); protected: friend class DrmBackend; @@ -138,7 +143,9 @@ private: QVector m_connectors; // pipelines QVector m_pipelines; - QVector m_outputs; + QVector m_drmOutputs; + // includes virtual outputs + QVector m_outputs; }; } diff --git a/src/plugins/platforms/drm/drm_output.cpp b/src/plugins/platforms/drm/drm_output.cpp index a8b9ba6ebc..a26a91b372 100644 --- a/src/plugins/platforms/drm/drm_output.cpp +++ b/src/plugins/platforms/drm/drm_output.cpp @@ -12,6 +12,9 @@ #include "drm_object_connector.h" #include "drm_gpu.h" #include "drm_pipeline.h" +#if HAVE_GBM +#include "drm_buffer_gbm.h" +#endif #include "composite.h" #include "cursor.h" @@ -34,12 +37,9 @@ namespace KWin { -DrmOutput::DrmOutput(DrmBackend *backend, DrmGpu *gpu, DrmPipeline *pipeline) - : AbstractWaylandOutput(backend) - , m_backend(backend) - , m_gpu(gpu) +DrmOutput::DrmOutput(DrmGpu *gpu, DrmPipeline *pipeline) + : DrmAbstractOutput(gpu) , m_pipeline(pipeline) - , m_renderLoop(new RenderLoop(this)) { m_pipeline->setUserData(this); auto conn = m_pipeline->connector(); @@ -72,11 +72,6 @@ DrmOutput::~DrmOutput() } } -RenderLoop *DrmOutput::renderLoop() const -{ - return m_renderLoop; -} - bool DrmOutput::initCursor(const QSize &cursorSize) { m_cursor = QSharedPointer::create(m_gpu, cursorSize); @@ -214,7 +209,7 @@ void DrmOutput::initOutputDevice() void DrmOutput::updateEnablement(bool enable) { if (m_pipeline->setActive(enable)) { - m_backend->enableOutput(this, enable); + m_gpu->platform()->enableOutput(this, enable); } else { qCCritical(KWIN_DRM) << "failed to update enablement to" << enable; } @@ -228,7 +223,7 @@ void DrmOutput::setDpmsMode(DpmsMode mode) m_turnOffTimer.start(); } if (isEnabled()) { - m_backend->createDpmsFilter(); + m_gpu->platform()->createDpmsFilter(); } } else { m_turnOffTimer.stop(); @@ -255,13 +250,13 @@ void DrmOutput::setDrmDpmsMode(DpmsMode mode) setDpmsModeInternal(mode); if (active) { m_renderLoop->uninhibit(); - m_backend->checkOutputsAreOn(); + m_gpu->platform()->checkOutputsAreOn(); if (Compositor *compositor = Compositor::self()) { compositor->addRepaintFull(); } } else { m_renderLoop->inhibit(); - m_backend->createDpmsFilter(); + m_gpu->platform()->createDpmsFilter(); } } else { qCCritical(KWIN_DRM) << "failed to set active to" << active; @@ -293,11 +288,6 @@ DrmPlane::Transformations outputToPlaneTransform(DrmOutput::Transform transform) } } -bool DrmOutput::hardwareTransforms() const -{ - return m_pipeline->transformation() == outputToPlaneTransform(transform()); -} - void DrmOutput::updateTransform(Transform transform) { const auto planeTransform = outputToPlaneTransform(transform); @@ -309,7 +299,7 @@ void DrmOutput::updateTransform(Transform transform) } // show cursor only if is enabled, i.e if pointer device is present - if (!m_backend->isCursorHidden() && !m_backend->usesSoftwareCursor()) { + if (!m_gpu->platform()->isCursorHidden() && !m_gpu->platform()->usesSoftwareCursor()) { // the cursor might need to get rotated showCursor(); updateCursor(); @@ -394,9 +384,13 @@ DrmPipeline *DrmOutput::pipeline() const return m_pipeline; } -DrmBuffer *DrmOutput::currentBuffer() const +GbmBuffer *DrmOutput::currentBuffer() const { - return m_pipeline->currentBuffer(); +#if HAVE_GBM + return dynamic_cast(m_pipeline->currentBuffer()); +#else + return nullptr; +#endif } bool DrmOutput::isDpmsEnabled() const @@ -404,4 +398,9 @@ bool DrmOutput::isDpmsEnabled() const return m_pipeline->isActive(); } +QSize DrmOutput::sourceSize() const +{ + return m_pipeline->sourceSize(); +} + } diff --git a/src/plugins/platforms/drm/drm_output.h b/src/plugins/platforms/drm/drm_output.h index d8fca89e95..b7886319be 100644 --- a/src/plugins/platforms/drm/drm_output.h +++ b/src/plugins/platforms/drm/drm_output.h @@ -9,7 +9,7 @@ #ifndef KWIN_DRM_OUTPUT_H #define KWIN_DRM_OUTPUT_H -#include "abstract_wayland_output.h" +#include "drm_abstract_output.h" #include "drm_pointer.h" #include "drm_object.h" #include "drm_object_plane.h" @@ -34,44 +34,31 @@ class Cursor; class DrmGpu; class DrmPipeline; -class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput +class KWIN_EXPORT DrmOutput : public DrmAbstractOutput { Q_OBJECT public: ///deletes the output, calling this whilst a page flip is pending will result in an error ~DrmOutput() override; - RenderLoop *renderLoop() const override; + bool initCursor(const QSize &cursorSize) override; + bool showCursor() override; + bool hideCursor() override; + bool updateCursor() override; + bool moveCursor() override; - bool showCursor(); - bool hideCursor(); - bool updateCursor(); - bool moveCursor(); - bool present(const QSharedPointer &buffer, QRegion damagedRegion); + bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; void pageFlipped(); - bool isDpmsEnabled() const; + bool isDpmsEnabled() const override; DrmPipeline *pipeline() const; - DrmBuffer *currentBuffer() const; - - bool initCursor(const QSize &cursorSize); - - /** - * Drm planes might be capable of realizing the current output transform without usage - * of compositing. This is a getter to query the current state of that - * - * @return true if the hardware realizes the transform without further assistance - */ - bool hardwareTransforms() const; - - DrmGpu *gpu() { - return m_gpu; - } + GbmBuffer *currentBuffer() const override; + QSize sourceSize() const override; private: friend class DrmGpu; friend class DrmBackend; - DrmOutput(DrmBackend *backend, DrmGpu* gpu, DrmPipeline *pipeline); + DrmOutput(DrmGpu* gpu, DrmPipeline *pipeline); void initOutputDevice(); bool isCurrentMode(const drmModeModeInfo *mode) const; @@ -80,17 +67,14 @@ private: void setDrmDpmsMode(DpmsMode mode); void setDpmsMode(DpmsMode mode) override; void updateMode(int modeIndex) override; - void updateMode(uint32_t width, uint32_t height, uint32_t refreshRate); + void updateMode(uint32_t width, uint32_t height, uint32_t refreshRate) override; void updateTransform(Transform transform) override; int gammaRampSize() const override; bool setGammaRamp(const GammaRamp &gamma) override; void setOverscan(uint32_t overscan) override; - DrmBackend *m_backend; - DrmGpu *m_gpu; DrmPipeline *m_pipeline; - RenderLoop *m_renderLoop; QSharedPointer m_cursor; bool m_pageFlipPending = false; diff --git a/src/plugins/platforms/drm/drm_pipeline.cpp b/src/plugins/platforms/drm/drm_pipeline.cpp index 6b8571c4c8..ac38520cd0 100644 --- a/src/plugins/platforms/drm/drm_pipeline.cpp +++ b/src/plugins/platforms/drm/drm_pipeline.cpp @@ -19,7 +19,7 @@ #include "drm_buffer.h" #include "cursor.h" #include "session.h" -#include "abstract_output.h" +#include "drm_output.h" #include "drm_backend.h" #if HAVE_GBM diff --git a/src/plugins/platforms/drm/drm_virtual_output.cpp b/src/plugins/platforms/drm/drm_virtual_output.cpp new file mode 100644 index 0000000000..35ed1c2fa1 --- /dev/null +++ b/src/plugins/platforms/drm/drm_virtual_output.cpp @@ -0,0 +1,108 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2018 Roman Gilg + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#include "drm_virtual_output.h" + +#include "renderloop_p.h" +#include "softwarevsyncmonitor.h" +#include "drm_gpu.h" +#include "drm_backend.h" +#include "logging.h" +#if HAVE_GBM +#include "drm_buffer_gbm.h" +#endif + +namespace KWin +{ + +DrmVirtualOutput::DrmVirtualOutput(DrmGpu *gpu) + : DrmAbstractOutput(gpu) + , m_vsyncMonitor(SoftwareVsyncMonitor::create(this)) +{ + connect(m_vsyncMonitor, &VsyncMonitor::vblankOccurred, this, &DrmVirtualOutput::vblank); + + static int s_serial = 0; + m_identifier = s_serial++; + setName("Virtual-" + QString::number(m_identifier)); + m_modeIndex = 0; + QVector modes = {{{1920, 1080}, 60000, AbstractWaylandOutput::ModeFlags(AbstractWaylandOutput::ModeFlag::Current) | AbstractWaylandOutput::ModeFlag::Preferred, 0}}; + initialize(QByteArray("model_").append(QByteArray::number(m_identifier)), + QByteArray("manufacturer_").append(QByteArray::number(m_identifier)), + QByteArray("eisa_").append(QByteArray::number(m_identifier)), + QByteArray("serial_").append(QByteArray::number(m_identifier)), + modes[m_modeIndex].size, modes, QByteArray("EDID_").append(QByteArray::number(m_identifier))); + m_renderLoop->setRefreshRate(modes[m_modeIndex].refreshRate); +} + +DrmVirtualOutput::~DrmVirtualOutput() +{ +} + +bool DrmVirtualOutput::present(const QSharedPointer &buffer, QRegion damagedRegion) +{ + Q_UNUSED(damagedRegion) + + m_currentBuffer = buffer; + m_vsyncMonitor->arm(); + m_pageFlipPending = true; + Q_EMIT outputChange(damagedRegion); + return true; +} + +void DrmVirtualOutput::vblank(std::chrono::nanoseconds timestamp) +{ + if (m_pageFlipPending) { + RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop); + renderLoopPrivate->notifyFrameCompleted(timestamp); + } +} + +void DrmVirtualOutput::updateMode(int modeIndex) +{ + Q_UNUSED(modeIndex) +} + +void DrmVirtualOutput::updateMode(uint32_t width, uint32_t height, uint32_t refreshRate) +{ + Q_UNUSED(width) + Q_UNUSED(height) + Q_UNUSED(refreshRate) +} + +void DrmVirtualOutput::setDpmsMode(DpmsMode mode) +{ + setDpmsModeInternal(mode); + m_dpmsEnabled = mode == DpmsMode::On; +} + +bool DrmVirtualOutput::isDpmsEnabled() const +{ + return m_dpmsEnabled; +} + +void DrmVirtualOutput::updateEnablement(bool enable) +{ + gpu()->platform()->enableOutput(this, enable); +} + +QSize DrmVirtualOutput::sourceSize() const +{ + return pixelSize(); +} + +GbmBuffer *DrmVirtualOutput::currentBuffer() const +{ +#if HAVE_GBM + return dynamic_cast(m_currentBuffer.get()); +#else + return nullptr; +#endif +} + +} diff --git a/src/plugins/platforms/drm/drm_virtual_output.h b/src/plugins/platforms/drm/drm_virtual_output.h new file mode 100644 index 0000000000..6ad02a58b8 --- /dev/null +++ b/src/plugins/platforms/drm/drm_virtual_output.h @@ -0,0 +1,60 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2018 Roman Gilg + SPDX-FileCopyrightText: 2021 Xaver Hugl + + SPDX-License-Identifier: GPL-2.0-or-later +*/ +#pragma once + +#include "drm_abstract_output.h" + +#include +#include + +namespace KWin +{ + +class SoftwareVsyncMonitor; +class VirtualBackend; + +class DrmVirtualOutput : public DrmAbstractOutput +{ + Q_OBJECT +public: + DrmVirtualOutput(DrmGpu *gpu); + ~DrmVirtualOutput() override; + + bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; + GbmBuffer *currentBuffer() const override; + QSize sourceSize() const override; + bool isDpmsEnabled() const override; + + int gammaRampSize() const override { + return 200; + } + bool setGammaRamp(const GammaRamp &gamma) override { + Q_UNUSED(gamma); + return true; + } + + +private: + void vblank(std::chrono::nanoseconds timestamp); + void updateMode(int modeIndex) override; + void updateMode(uint32_t width, uint32_t height, uint32_t refreshRate) override; + void setDpmsMode(DpmsMode mode) override; + void updateEnablement(bool enable) override; + + QSharedPointer m_currentBuffer; + bool m_pageFlipPending = true; + int m_modeIndex = 0; + bool m_dpmsEnabled = true; + + int m_identifier; + SoftwareVsyncMonitor *m_vsyncMonitor; +}; + +} diff --git a/src/plugins/platforms/drm/egl_gbm_backend.cpp b/src/plugins/platforms/drm/egl_gbm_backend.cpp index 584f091911..cd33326f66 100644 --- a/src/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/src/plugins/platforms/drm/egl_gbm_backend.cpp @@ -144,10 +144,10 @@ bool EglGbmBackend::initRenderingContext() } return true; } -bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput) +bool EglGbmBackend::resetOutput(Output &output, DrmAbstractOutput *drmOutput) { output.output = drmOutput; - const QSize size = drmOutput->pipeline()->sourceSize(); + const QSize size = drmOutput->sourceSize(); int flags = GBM_BO_USE_RENDERING; if (drmOutput->gpu() == m_gpu) { flags |= GBM_BO_USE_SCANOUT; @@ -164,7 +164,7 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput) output.current = {}; output.current.gbmSurface = gbmSurface; - if (output.output->hardwareTransforms()) { + if (size == drmOutput->pixelSize()) { output.current.shadowBuffer = nullptr; } else { makeContextCurrent(output.current); @@ -176,7 +176,7 @@ bool EglGbmBackend::resetOutput(Output &output, DrmOutput *drmOutput) return true; } -bool EglGbmBackend::addOutput(DrmOutput *drmOutput) +bool EglGbmBackend::addOutput(DrmAbstractOutput *drmOutput) { if (isPrimary()) { Output newOutput; @@ -199,7 +199,7 @@ bool EglGbmBackend::addOutput(DrmOutput *drmOutput) return true; } -void EglGbmBackend::removeOutput(DrmOutput *drmOutput) +void EglGbmBackend::removeOutput(DrmAbstractOutput *drmOutput) { QVector &outputs = drmOutput->gpu() == m_gpu ? m_outputs : m_secondaryGpuOutputs; auto it = std::find_if(outputs.begin(), outputs.end(), @@ -219,7 +219,7 @@ void EglGbmBackend::removeOutput(DrmOutput *drmOutput) outputs.erase(it); } -bool EglGbmBackend::swapBuffers(DrmOutput *drmOutput, const QRegion &dirty) +bool EglGbmBackend::swapBuffers(DrmAbstractOutput *drmOutput, const QRegion &dirty) { auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), [drmOutput] (const Output &output) { @@ -240,7 +240,7 @@ bool EglGbmBackend::swapBuffers(DrmOutput *drmOutput, const QRegion &dirty) } } -bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QSize &size, uint32_t stride) +bool EglGbmBackend::exportFramebuffer(DrmAbstractOutput *drmOutput, void *data, const QSize &size, uint32_t stride) { auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), [drmOutput] (const Output &output) { @@ -261,9 +261,8 @@ bool EglGbmBackend::exportFramebuffer(DrmOutput *drmOutput, void *data, const QS return memcpy(data, bo->mappedData(), size.height() * stride); } -int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) +int EglGbmBackend::exportFramebufferAsDmabuf(DrmAbstractOutput *drmOutput, uint32_t *format, uint32_t *stride) { - DrmOutput *drmOutput = static_cast(output); auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), [drmOutput] (const Output &output) { return output.output == drmOutput; @@ -283,7 +282,7 @@ int EglGbmBackend::exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format return fd; } -QRegion EglGbmBackend::beginFrameForSecondaryGpu(DrmOutput *drmOutput) +QRegion EglGbmBackend::beginFrameForSecondaryGpu(DrmAbstractOutput *drmOutput) { auto it = std::find_if(m_secondaryGpuOutputs.begin(), m_secondaryGpuOutputs.end(), [drmOutput] (const Output &output) { @@ -492,12 +491,12 @@ QRegion EglGbmBackend::beginFrame(int screenId) } } -bool EglGbmBackend::doesRenderFit(DrmOutput *output, const Output::RenderData &render) +bool EglGbmBackend::doesRenderFit(DrmAbstractOutput *output, const Output::RenderData &render) { if (!render.gbmSurface) { return false; } - QSize surfaceSize = output->pipeline()->sourceSize(); + QSize surfaceSize = output->sourceSize(); if (surfaceSize != render.gbmSurface->size()) { return false; } @@ -558,13 +557,12 @@ void EglGbmBackend::endFrame(int screenId, const QRegion &renderedRegion, Q_UNUSED(renderedRegion) Output &output = m_outputs[screenId]; - DrmOutput *drmOutput = output.output; cleanupRenderData(output.old); const QRegion dirty = damagedRegion.intersected(output.output->geometry()); QSharedPointer buffer = endFrameWithBuffer(screenId, dirty); if (!buffer || !output.output->present(buffer, dirty)) { - RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(drmOutput->renderLoop()); + RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(output.output->renderLoop()); renderLoopPrivate->notifyFrameFailed(); return; } @@ -667,7 +665,7 @@ bool EglGbmBackend::scanout(int screenId, SurfaceItem *surfaceItem) } } -QSharedPointer EglGbmBackend::renderTestFrame(DrmOutput *output) +QSharedPointer EglGbmBackend::renderTestFrame(DrmAbstractOutput *output) { for (int i = 0; i < m_outputs.count(); i++) { if (m_outputs[i].output == output) { @@ -697,7 +695,7 @@ QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *abstra } } - DrmOutput *drmOutput = itOutput->output; + DrmAbstractOutput *drmOutput = itOutput->output; if (itOutput->current.shadowBuffer) { const auto glTexture = QSharedPointer::create(itOutput->current.shadowBuffer->texture(), GL_RGBA8, drmOutput->pixelSize()); glTexture->setYInverted(true); diff --git a/src/plugins/platforms/drm/egl_gbm_backend.h b/src/plugins/platforms/drm/egl_gbm_backend.h index 90a524888f..82b45f63e7 100644 --- a/src/plugins/platforms/drm/egl_gbm_backend.h +++ b/src/plugins/platforms/drm/egl_gbm_backend.h @@ -58,16 +58,16 @@ public: return m_outputs.count(); } - bool addOutput(DrmOutput *output) override; - void removeOutput(DrmOutput *output) override; - bool swapBuffers(DrmOutput *output, const QRegion &dirty) override; - bool exportFramebuffer(DrmOutput *output, void *data, const QSize &size, uint32_t stride) override; - int exportFramebufferAsDmabuf(DrmOutput *output, uint32_t *format, uint32_t *stride) override; - QRegion beginFrameForSecondaryGpu(DrmOutput *output) override; + bool addOutput(DrmAbstractOutput *output) override; + void removeOutput(DrmAbstractOutput *output) override; + bool swapBuffers(DrmAbstractOutput *output, const QRegion &dirty) override; + bool exportFramebuffer(DrmAbstractOutput *output, void *data, const QSize &size, uint32_t stride) override; + int exportFramebufferAsDmabuf(DrmAbstractOutput *output, uint32_t *format, uint32_t *stride) override; + QRegion beginFrameForSecondaryGpu(DrmAbstractOutput *output) override; bool directScanoutAllowed(int screen) const override; - QSharedPointer renderTestFrame(DrmOutput *output) override; + QSharedPointer renderTestFrame(DrmAbstractOutput *output) override; protected: void cleanupSurfaces() override; @@ -83,7 +83,7 @@ private: DumbBuffer }; struct Output { - DrmOutput *output = nullptr; + DrmAbstractOutput *output = nullptr; struct RenderData { QSharedPointer shadowBuffer; QSharedPointer gbmSurface; @@ -98,8 +98,8 @@ private: KWaylandServer::SurfaceInterface *surfaceInterface = nullptr; }; - bool doesRenderFit(DrmOutput *output, const Output::RenderData &render); - bool resetOutput(Output &output, DrmOutput *drmOutput); + bool doesRenderFit(DrmAbstractOutput *output, const Output::RenderData &render); + bool resetOutput(Output &output, DrmAbstractOutput *drmOutput); bool makeContextCurrent(const Output::RenderData &output) const; void setViewport(const Output &output) const; diff --git a/src/plugins/platforms/drm/egl_stream_backend.cpp b/src/plugins/platforms/drm/egl_stream_backend.cpp index ae97494ee1..0e59f662e2 100644 --- a/src/plugins/platforms/drm/egl_stream_backend.cpp +++ b/src/plugins/platforms/drm/egl_stream_backend.cpp @@ -281,7 +281,7 @@ void EglStreamBackend::init() } else { // secondary NVidia GPUs only import dumb buffers const auto outputs = m_gpu->outputs(); - for (DrmOutput *drmOutput : outputs) { + for (DrmAbstractOutput *drmOutput : outputs) { addOutput(drmOutput); } } @@ -296,7 +296,7 @@ bool EglStreamBackend::initRenderingContext() } const auto outputs = m_gpu->outputs(); - for (DrmOutput *drmOutput : outputs) { + for (DrmAbstractOutput *drmOutput : outputs) { addOutput(drmOutput); } return !m_outputs.isEmpty() && makeContextCurrent(m_outputs.first()); @@ -305,7 +305,7 @@ bool EglStreamBackend::initRenderingContext() bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput) { o.output = drmOutput; - QSize sourceSize = drmOutput->pipeline()->sourceSize(); + QSize sourceSize = drmOutput->sourceSize(); if (isPrimary()) { // dumb buffer used for modesetting @@ -366,7 +366,7 @@ bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput) o.eglStream = stream; o.eglSurface = eglSurface; - if (!drmOutput->hardwareTransforms()) { + if (sourceSize != drmOutput->pixelSize()) { makeContextCurrent(o); o.shadowBuffer = QSharedPointer::create(o.output->pixelSize()); if (!o.shadowBuffer->isComplete()) { @@ -383,35 +383,40 @@ bool EglStreamBackend::resetOutput(Output &o, DrmOutput *drmOutput) return true; } -bool EglStreamBackend::addOutput(DrmOutput *drmOutput) +bool EglStreamBackend::addOutput(DrmAbstractOutput *output) { - Q_ASSERT(drmOutput->gpu() == m_gpu); - Output o; - if (!resetOutput(o, drmOutput)) { - return false; - } - if (!isPrimary() && !renderingBackend()->addOutput(drmOutput)) { - return false; - } - - connect(drmOutput, &DrmOutput::modeChanged, this, - [drmOutput, this] { - auto it = std::find_if(m_outputs.begin(), m_outputs.end(), - [drmOutput] (const auto &o) { - return o.output == drmOutput; - } - ); - if (it == m_outputs.end()) { - return; - } - resetOutput(*it, drmOutput); + Q_ASSERT(output->gpu() == m_gpu); + DrmOutput *drmOutput = qobject_cast(output); + if (drmOutput) { + Output o; + if (!resetOutput(o, drmOutput)) { + return false; } - ); - m_outputs << o; - return true; + if (!isPrimary() && !renderingBackend()->addOutput(drmOutput)) { + return false; + } + + connect(drmOutput, &DrmOutput::modeChanged, this, + [drmOutput, this] { + auto it = std::find_if(m_outputs.begin(), m_outputs.end(), + [drmOutput] (const auto &o) { + return o.output == drmOutput; + } + ); + if (it == m_outputs.end()) { + return; + } + resetOutput(*it, drmOutput); + } + ); + m_outputs << o; + return true; + } else { + return false; + } } -void EglStreamBackend::removeOutput(DrmOutput *drmOutput) +void EglStreamBackend::removeOutput(DrmAbstractOutput *drmOutput) { Q_ASSERT(drmOutput->gpu() == m_gpu); auto it = std::find_if(m_outputs.begin(), m_outputs.end(), @@ -551,7 +556,7 @@ void EglStreamBackend::endFrame(int screenId, const QRegion &renderedRegion, con } } -QSharedPointer EglStreamBackend::renderTestFrame(DrmOutput *drmOutput) +QSharedPointer EglStreamBackend::renderTestFrame(DrmAbstractOutput *drmOutput) { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [drmOutput] (const Output &o) { @@ -562,7 +567,7 @@ QSharedPointer EglStreamBackend::renderTestFrame(DrmOutput *drmOutput return nullptr; } auto buffer = (*it).dumbSwapchain ? (*it).dumbSwapchain->currentBuffer() : (*it).buffer; - auto size = drmOutput->pipeline()->sourceSize(); + auto size = drmOutput->sourceSize(); if (buffer->size() == size) { return buffer; } else { diff --git a/src/plugins/platforms/drm/egl_stream_backend.h b/src/plugins/platforms/drm/egl_stream_backend.h index a01b9c7ce3..20e971b94b 100644 --- a/src/plugins/platforms/drm/egl_stream_backend.h +++ b/src/plugins/platforms/drm/egl_stream_backend.h @@ -17,7 +17,7 @@ namespace KWin { -class DrmOutput; +class DrmAbstractOutput; class DrmDumbBuffer; class DumbSwapchain; class ShadowBuffer; @@ -41,10 +41,10 @@ public: return m_outputs.count(); } - bool addOutput(DrmOutput *output) override; - void removeOutput(DrmOutput *output) override; + bool addOutput(DrmAbstractOutput *output) override; + void removeOutput(DrmAbstractOutput *output) override; - QSharedPointer renderTestFrame(DrmOutput *output) override; + QSharedPointer renderTestFrame(DrmAbstractOutput *output) override; protected: void cleanupSurfaces() override; diff --git a/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp b/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp index 0d410dfa20..d57605f4f7 100644 --- a/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp +++ b/src/plugins/platforms/drm/scene_qpainter_drm_backend.cpp @@ -26,7 +26,7 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu) } connect(m_gpu, &DrmGpu::outputEnabled, this, &DrmQPainterBackend::initOutput); connect(m_gpu, &DrmGpu::outputDisabled, this, - [this] (DrmOutput *o) { + [this] (DrmAbstractOutput *o) { auto it = std::find_if(m_outputs.begin(), m_outputs.end(), [o] (const Output &output) { return output.output == o; @@ -40,7 +40,7 @@ DrmQPainterBackend::DrmQPainterBackend(DrmBackend *backend, DrmGpu *gpu) ); } -void DrmQPainterBackend::initOutput(DrmOutput *output) +void DrmQPainterBackend::initOutput(DrmAbstractOutput *output) { Output o; o.swapchain = QSharedPointer::create(m_gpu, output->pixelSize()); @@ -80,7 +80,7 @@ QRegion DrmQPainterBackend::beginFrame(int screenId) void DrmQPainterBackend::endFrame(int screenId, const QRegion &damage) { Output &rendererOutput = m_outputs[screenId]; - DrmOutput *drmOutput = rendererOutput.output; + DrmAbstractOutput *drmOutput = rendererOutput.output; QSharedPointer back = rendererOutput.swapchain->currentBuffer(); rendererOutput.swapchain->releaseBuffer(back); diff --git a/src/plugins/platforms/drm/scene_qpainter_drm_backend.h b/src/plugins/platforms/drm/scene_qpainter_drm_backend.h index 5d26e687ad..7e4c28fe96 100644 --- a/src/plugins/platforms/drm/scene_qpainter_drm_backend.h +++ b/src/plugins/platforms/drm/scene_qpainter_drm_backend.h @@ -22,7 +22,7 @@ namespace KWin class DrmBackend; class DrmDumbBuffer; -class DrmOutput; +class DrmAbstractOutput; class DrmGpu; class DrmQPainterBackend : public QPainterBackend @@ -36,9 +36,9 @@ public: void endFrame(int screenId, const QRegion &damage) override; private: - void initOutput(DrmOutput *output); + void initOutput(DrmAbstractOutput *output); struct Output { - DrmOutput *output; + DrmAbstractOutput *output; QSharedPointer swapchain; DamageJournal damageJournal; }; diff --git a/src/plugins/platforms/drm/shadowbuffer.cpp b/src/plugins/platforms/drm/shadowbuffer.cpp index 4b99bbad6d..14074a27c6 100644 --- a/src/plugins/platforms/drm/shadowbuffer.cpp +++ b/src/plugins/platforms/drm/shadowbuffer.cpp @@ -67,7 +67,7 @@ ShadowBuffer::~ShadowBuffer() glDeleteFramebuffers(1, &m_framebuffer); } -void ShadowBuffer::render(DrmOutput *output) +void ShadowBuffer::render(DrmAbstractOutput *output) { const auto size = output->modeSize(); glViewport(0, 0, size.width(), size.height()); diff --git a/src/plugins/platforms/drm/shadowbuffer.h b/src/plugins/platforms/drm/shadowbuffer.h index 6fc2ec3bd4..dfb3bf535b 100644 --- a/src/plugins/platforms/drm/shadowbuffer.h +++ b/src/plugins/platforms/drm/shadowbuffer.h @@ -14,7 +14,7 @@ namespace KWin { -class DrmOutput; +class DrmAbstractOutput; class ShadowBuffer { @@ -25,7 +25,7 @@ public: bool isComplete() const; void bind(); - void render(DrmOutput *output); + void render(DrmAbstractOutput *output); int texture() const; diff --git a/src/wayland_server.cpp b/src/wayland_server.cpp index dac6f60271..506059e45c 100644 --- a/src/wayland_server.cpp +++ b/src/wayland_server.cpp @@ -325,23 +325,33 @@ void WaylandServer::initPlatform() void WaylandServer::handleOutputAdded(AbstractOutput *output) { auto o = static_cast(output); - m_waylandOutputDevices.insert(o, new WaylandOutputDevice(o)); + if (!o->isPlaceholder()) { + m_waylandOutputDevices.insert(o, new WaylandOutputDevice(o)); + } } void WaylandServer::handleOutputRemoved(AbstractOutput *output) { - delete m_waylandOutputDevices.take(static_cast(output)); + auto o = static_cast(output); + if (!o->isPlaceholder()) { + delete m_waylandOutputDevices.take(o); + } } void WaylandServer::handleOutputEnabled(AbstractOutput *output) { auto o = static_cast(output); - m_waylandOutputs.insert(o, new WaylandOutput(o)); + if (!o->isPlaceholder()) { + m_waylandOutputs.insert(o, new WaylandOutput(o)); + } } void WaylandServer::handleOutputDisabled(AbstractOutput *output) { - delete m_waylandOutputs.take(static_cast(output)); + auto o = static_cast(output); + if (!o->isPlaceholder()) { + delete m_waylandOutputs.take(o); + } } AbstractWaylandOutput *WaylandServer::findOutput(KWaylandServer::OutputInterface *outputIface) const