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
This commit is contained in:
Xaver Hugl 2021-07-27 01:11:50 +02:00
parent f7e2f5504a
commit 1041ef8275
24 changed files with 538 additions and 184 deletions

View file

@ -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;
}
}

View file

@ -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;
};

View file

@ -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)

View file

@ -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<DrmBuffer> renderTestFrame(DrmOutput *output) = 0;
virtual QSharedPointer<DrmBuffer> renderTestFrame(DrmAbstractOutput *output) = 0;
static AbstractEglDrmBackend *renderingBackend() {
return static_cast<AbstractEglDrmBackend*>(primaryBackend());

View file

@ -0,0 +1,59 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drm_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;
}
}

View file

@ -0,0 +1,55 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "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<DrmBuffer> &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;
};
}

View file

@ -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 <gbm.h>
@ -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<DrmOutput *>(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<DrmOutput *>(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<DrmOutput *>(a);
auto db = qobject_cast<DrmOutput *>(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<DrmOutput*> &outputs)
QString connectedOutputsHash(const QVector<DrmAbstractOutput*> &outputs)
{
QStringList hashedOutputs;
hashedOutputs.reserve(outputs.count());
@ -387,7 +413,7 @@ namespace KWinKScreenIntegration
return QString::fromLatin1(hash.toHex());
}
QMap<DrmOutput*, QJsonObject> outputsConfig(const QVector<DrmOutput*> &outputs)
QMap<DrmAbstractOutput*, QJsonObject> outputsConfig(const QVector<DrmAbstractOutput*> &outputs)
{
const QString kscreenJsonPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kscreen/") % connectedOutputsHash(outputs));
if (kscreenJsonPath.isEmpty()) {
@ -407,7 +433,7 @@ namespace KWinKScreenIntegration
return {};
}
QMap<DrmOutput*, QJsonObject> ret;
QMap<DrmAbstractOutput*, QJsonObject> 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();

View file

@ -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<DrmOutput*> drmOutputs() const {
QVector<DrmAbstractOutput*> drmOutputs() const {
return m_outputs;
}
QVector<DrmOutput*> drmEnabledOutputs() const {
QVector<DrmAbstractOutput*> 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<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor;
Session *m_session = nullptr;
// active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
// active and enabled pipelines (above + wl_output)
QVector<DrmOutput*> m_enabledOutputs;
// all outputs, enabled and disabled
QVector<DrmAbstractOutput*> m_outputs;
// only enabled outputs
QVector<DrmAbstractOutput*> m_enabledOutputs;
DrmVirtualOutput *m_placeHolderOutput = nullptr;
bool m_active = false;
QVector<DrmGpu*> m_gpus;

View file

@ -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<DrmOutput *>(output)) {
removeOutput(drmOutput);
} else {
removeVirtualOutput(dynamic_cast<DrmVirtualOutput*>(output));
}
}
if (m_eglDisplay != EGL_NO_DISPLAY) {
eglTerminate(m_eglDisplay);
@ -219,14 +224,14 @@ bool DrmGpu::updateOutputs()
// check for outputs which got removed
QVector<DrmOutput*> 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<DrmBackend*>(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<DrmPipeline*> 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;
}
}
}

View file

@ -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<DrmOutput*> outputs() const {
QVector<DrmAbstractOutput*> outputs() const {
return m_outputs;
}
@ -97,11 +99,14 @@ public:
DrmBackend *platform() const;
const QVector<DrmPipeline*> 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<DrmConnector*> m_connectors;
// pipelines
QVector<DrmPipeline*> m_pipelines;
QVector<DrmOutput*> m_outputs;
QVector<DrmOutput*> m_drmOutputs;
// includes virtual outputs
QVector<DrmAbstractOutput*> m_outputs;
};
}

View file

@ -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<DrmDumbBuffer>::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<GbmBuffer*>(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();
}
}

View file

@ -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<DrmBuffer> &buffer, QRegion damagedRegion);
bool present(const QSharedPointer<DrmBuffer> &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<DrmDumbBuffer> m_cursor;
bool m_pageFlipPending = false;

View file

@ -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

View file

@ -0,0 +1,108 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
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<Mode> 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<DrmBuffer> &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<GbmBuffer*>(m_currentBuffer.get());
#else
return nullptr;
#endif
}
}

View file

@ -0,0 +1,60 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "drm_abstract_output.h"
#include <QObject>
#include <QRect>
namespace KWin
{
class SoftwareVsyncMonitor;
class VirtualBackend;
class DrmVirtualOutput : public DrmAbstractOutput
{
Q_OBJECT
public:
DrmVirtualOutput(DrmGpu *gpu);
~DrmVirtualOutput() override;
bool present(const QSharedPointer<DrmBuffer> &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<DrmBuffer> m_currentBuffer;
bool m_pageFlipPending = true;
int m_modeIndex = 0;
bool m_dpmsEnabled = true;
int m_identifier;
SoftwareVsyncMonitor *m_vsyncMonitor;
};
}

View file

@ -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<Output> &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<DrmOutput*>(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<DrmBuffer> 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<DrmBuffer> EglGbmBackend::renderTestFrame(DrmOutput *output)
QSharedPointer<DrmBuffer> EglGbmBackend::renderTestFrame(DrmAbstractOutput *output)
{
for (int i = 0; i < m_outputs.count(); i++) {
if (m_outputs[i].output == output) {
@ -697,7 +695,7 @@ QSharedPointer<GLTexture> EglGbmBackend::textureForOutput(AbstractOutput *abstra
}
}
DrmOutput *drmOutput = itOutput->output;
DrmAbstractOutput *drmOutput = itOutput->output;
if (itOutput->current.shadowBuffer) {
const auto glTexture = QSharedPointer<KWin::GLTexture>::create(itOutput->current.shadowBuffer->texture(), GL_RGBA8, drmOutput->pixelSize());
glTexture->setYInverted(true);

View file

@ -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<DrmBuffer> renderTestFrame(DrmOutput *output) override;
QSharedPointer<DrmBuffer> 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> shadowBuffer;
QSharedPointer<GbmSurface> 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;

View file

@ -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<ShadowBuffer>::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<DrmOutput *>(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<DrmBuffer> EglStreamBackend::renderTestFrame(DrmOutput *drmOutput)
QSharedPointer<DrmBuffer> EglStreamBackend::renderTestFrame(DrmAbstractOutput *drmOutput)
{
auto it = std::find_if(m_outputs.begin(), m_outputs.end(),
[drmOutput] (const Output &o) {
@ -562,7 +567,7 @@ QSharedPointer<DrmBuffer> 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 {

View file

@ -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<DrmBuffer> renderTestFrame(DrmOutput *output) override;
QSharedPointer<DrmBuffer> renderTestFrame(DrmAbstractOutput *output) override;
protected:
void cleanupSurfaces() override;

View file

@ -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<DumbSwapchain>::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<DrmDumbBuffer> back = rendererOutput.swapchain->currentBuffer();
rendererOutput.swapchain->releaseBuffer(back);

View file

@ -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<DumbSwapchain> swapchain;
DamageJournal damageJournal;
};

View file

@ -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());

View file

@ -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;

View file

@ -325,23 +325,33 @@ void WaylandServer::initPlatform()
void WaylandServer::handleOutputAdded(AbstractOutput *output)
{
auto o = static_cast<AbstractWaylandOutput *>(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<AbstractWaylandOutput *>(output));
auto o = static_cast<AbstractWaylandOutput *>(output);
if (!o->isPlaceholder()) {
delete m_waylandOutputDevices.take(o);
}
}
void WaylandServer::handleOutputEnabled(AbstractOutput *output)
{
auto o = static_cast<AbstractWaylandOutput *>(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<AbstractWaylandOutput *>(output));
auto o = static_cast<AbstractWaylandOutput *>(output);
if (!o->isPlaceholder()) {
delete m_waylandOutputs.take(o);
}
}
AbstractWaylandOutput *WaylandServer::findOutput(KWaylandServer::OutputInterface *outputIface) const