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:
parent
f7e2f5504a
commit
1041ef8275
24 changed files with 538 additions and 184 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
59
src/plugins/platforms/drm/drm_abstract_output.cpp
Normal file
59
src/plugins/platforms/drm/drm_abstract_output.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
55
src/plugins/platforms/drm/drm_abstract_output.h
Normal file
55
src/plugins/platforms/drm/drm_abstract_output.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
108
src/plugins/platforms/drm/drm_virtual_output.cpp
Normal file
108
src/plugins/platforms/drm/drm_virtual_output.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
60
src/plugins/platforms/drm/drm_virtual_output.h
Normal file
60
src/plugins/platforms/drm/drm_virtual_output.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue