platforms/drm: more dynamic crtc assignment
Hardware constraints limit the number of crtcs and which connector + crtc combinations can work together. The current code is searching for working combinations when a hotplug happens but that's not enough, it also needs to happen when the user enables or disables outputs and when modesets are done, and the configuration change needs to be applied with a single atomic commit. This commit removes the hard dependency of DrmPipeline on crtcs by moving the pending state of outputs from the drm objects to DrmPipeline itself, which ensures that it's independent from the set of drm objects currently used. It also changes requests from KScreen to be applied truly atomically.
This commit is contained in:
parent
29e5864402
commit
e2a0863843
25 changed files with 686 additions and 735 deletions
|
@ -135,6 +135,7 @@ set(kwin_SRCS
|
||||||
wayland_server.cpp
|
wayland_server.cpp
|
||||||
waylandclient.cpp
|
waylandclient.cpp
|
||||||
waylandoutput.cpp
|
waylandoutput.cpp
|
||||||
|
waylandoutputconfig.cpp
|
||||||
waylandoutputdevicev2.cpp
|
waylandoutputdevicev2.cpp
|
||||||
waylandshellintegration.cpp
|
waylandshellintegration.cpp
|
||||||
window_property_notify_x11_filter.cpp
|
window_property_notify_x11_filter.cpp
|
||||||
|
|
|
@ -100,11 +100,6 @@ void AbstractOutput::setEnabled(bool enable)
|
||||||
Q_UNUSED(enable)
|
Q_UNUSED(enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractOutput::applyChanges(const KWaylandServer::OutputChangeSetV2 *changeSet)
|
|
||||||
{
|
|
||||||
Q_UNUSED(changeSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AbstractOutput::isInternal() const
|
bool AbstractOutput::isInternal() const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -117,13 +117,6 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void setEnabled(bool enable);
|
virtual void setEnabled(bool enable);
|
||||||
|
|
||||||
/**
|
|
||||||
* This sets the changes and tests them against the specific output.
|
|
||||||
*
|
|
||||||
* Default implementation does nothing
|
|
||||||
*/
|
|
||||||
virtual void applyChanges(const KWaylandServer::OutputChangeSetV2 *changeSet);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns geometry of this output in device independent pixels.
|
* Returns geometry of this output in device independent pixels.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
*/
|
*/
|
||||||
#include "abstract_wayland_output.h"
|
#include "abstract_wayland_output.h"
|
||||||
#include "screens.h"
|
#include "screens.h"
|
||||||
|
#include "waylandoutputconfig.h"
|
||||||
|
|
||||||
// KWayland
|
// KWayland
|
||||||
#include <KWaylandServer/outputchangeset_v2.h>
|
#include <KWaylandServer/outputchangeset_v2.h>
|
||||||
|
@ -20,11 +21,6 @@
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
static AbstractWaylandOutput::Transform outputDeviceTransformToKWinTransform(KWaylandServer::OutputDeviceV2Interface::Transform transform)
|
|
||||||
{
|
|
||||||
return static_cast<AbstractWaylandOutput::Transform>(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
|
AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
|
||||||
: AbstractOutput(parent)
|
: AbstractOutput(parent)
|
||||||
{
|
{
|
||||||
|
@ -152,59 +148,18 @@ void AbstractWaylandOutput::setSubPixelInternal(SubPixel subPixel)
|
||||||
m_subPixel = subPixel;
|
m_subPixel = subPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSetV2 *changeSet)
|
void AbstractWaylandOutput::applyChanges(const WaylandOutputConfig &config)
|
||||||
{
|
{
|
||||||
|
auto props = config.constChangeSet(this);
|
||||||
Q_EMIT aboutToChange();
|
Q_EMIT aboutToChange();
|
||||||
|
|
||||||
qCDebug(KWIN_CORE) << "Apply changes to the Wayland output.";
|
setEnabled(props->enabled);
|
||||||
bool emitModeChanged = false;
|
updateTransform(props->transform);
|
||||||
bool overallSizeCheckNeeded = false;
|
moveTo(props->pos);
|
||||||
|
setScale(props->scale);
|
||||||
|
setVrrPolicy(props->vrrPolicy);
|
||||||
|
|
||||||
// Enablement changes are handled by platform.
|
|
||||||
if (changeSet->sizeChanged() || changeSet->refreshRateChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Setting new mode:" << changeSet->size() << changeSet->refreshRate();
|
|
||||||
updateMode(changeSet->size(), changeSet->refreshRate());
|
|
||||||
emitModeChanged = true;
|
|
||||||
}
|
|
||||||
if (changeSet->transformChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Server setting transform: " << changeSet->transform();
|
|
||||||
auto transform = outputDeviceTransformToKWinTransform(changeSet->transform());
|
|
||||||
updateTransform(transform);
|
|
||||||
emitModeChanged = true;
|
|
||||||
}
|
|
||||||
if (changeSet->positionChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Server setting position: " << changeSet->position();
|
|
||||||
moveTo(changeSet->position());
|
|
||||||
// may just work already!
|
|
||||||
overallSizeCheckNeeded = true;
|
|
||||||
}
|
|
||||||
if (changeSet->scaleChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Setting scale:" << changeSet->scale();
|
|
||||||
setScale(changeSet->scale());
|
|
||||||
emitModeChanged = true;
|
|
||||||
}
|
|
||||||
if (changeSet->overscanChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Setting overscan:" << changeSet->overscan();
|
|
||||||
setOverscan(changeSet->overscan());
|
|
||||||
}
|
|
||||||
if (changeSet->vrrPolicyChanged()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Setting VRR Policy:" << changeSet->vrrPolicy();
|
|
||||||
setVrrPolicy(static_cast<RenderLoop::VrrPolicy>(changeSet->vrrPolicy()));
|
|
||||||
}
|
|
||||||
if (changeSet->rgbRangeChanged()) {
|
|
||||||
qDebug(KWIN_CORE) << "Setting rgb range:" << changeSet->rgbRange();
|
|
||||||
setRgbRange(static_cast<AbstractWaylandOutput::RgbRange>(changeSet->rgbRange()));
|
|
||||||
}
|
|
||||||
Q_EMIT changed();
|
Q_EMIT changed();
|
||||||
|
|
||||||
overallSizeCheckNeeded |= emitModeChanged;
|
|
||||||
if (overallSizeCheckNeeded) {
|
|
||||||
Q_EMIT screens()->changed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (emitModeChanged) {
|
|
||||||
Q_EMIT currentModeChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AbstractWaylandOutput::isEnabled() const
|
bool AbstractWaylandOutput::isEnabled() const
|
||||||
|
@ -386,11 +341,6 @@ uint32_t AbstractWaylandOutput::overscan() const
|
||||||
return m_overscan;
|
return m_overscan;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractWaylandOutput::setOverscan(uint32_t overscan)
|
|
||||||
{
|
|
||||||
Q_UNUSED(overscan);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy)
|
void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy)
|
||||||
{
|
{
|
||||||
if (renderLoop()->vrrPolicy() != policy && (m_capabilities & Capability::Vrr)) {
|
if (renderLoop()->vrrPolicy() != policy && (m_capabilities & Capability::Vrr)) {
|
||||||
|
@ -419,11 +369,6 @@ AbstractWaylandOutput::RgbRange AbstractWaylandOutput::rgbRange() const
|
||||||
return m_rgbRange;
|
return m_rgbRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractWaylandOutput::setRgbRange(RgbRange range)
|
|
||||||
{
|
|
||||||
Q_UNUSED(range)
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractWaylandOutput::setRgbRangeInternal(RgbRange range)
|
void AbstractWaylandOutput::setRgbRangeInternal(RgbRange range)
|
||||||
{
|
{
|
||||||
if (m_rgbRange != range) {
|
if (m_rgbRange != range) {
|
||||||
|
|
|
@ -18,14 +18,11 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
|
|
||||||
namespace KWaylandServer
|
|
||||||
{
|
|
||||||
class OutputChangeSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class WaylandOutputConfig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic output representation in a Wayland session
|
* Generic output representation in a Wayland session
|
||||||
*/
|
*/
|
||||||
|
@ -121,7 +118,7 @@ public:
|
||||||
void moveTo(const QPoint &pos);
|
void moveTo(const QPoint &pos);
|
||||||
void setScale(qreal scale);
|
void setScale(qreal scale);
|
||||||
|
|
||||||
void applyChanges(const KWaylandServer::OutputChangeSetV2 *changeSet) override;
|
void applyChanges(const WaylandOutputConfig &config);
|
||||||
|
|
||||||
bool isEnabled() const override;
|
bool isEnabled() const override;
|
||||||
void setEnabled(bool enable) override;
|
void setEnabled(bool enable) override;
|
||||||
|
@ -136,7 +133,6 @@ public:
|
||||||
virtual void setDpmsMode(DpmsMode mode);
|
virtual void setDpmsMode(DpmsMode mode);
|
||||||
|
|
||||||
uint32_t overscan() const;
|
uint32_t overscan() const;
|
||||||
virtual void setOverscan(uint32_t overscan);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a matrix that can translate into the display's coordinates system
|
* Returns a matrix that can translate into the display's coordinates system
|
||||||
|
@ -150,8 +146,6 @@ public:
|
||||||
|
|
||||||
void setVrrPolicy(RenderLoop::VrrPolicy policy);
|
void setVrrPolicy(RenderLoop::VrrPolicy policy);
|
||||||
RenderLoop::VrrPolicy vrrPolicy() const;
|
RenderLoop::VrrPolicy vrrPolicy() const;
|
||||||
|
|
||||||
virtual void setRgbRange(RgbRange range);
|
|
||||||
RgbRange rgbRange() const;
|
RgbRange rgbRange() const;
|
||||||
|
|
||||||
bool isPlaceholder() const;
|
bool isPlaceholder() const;
|
||||||
|
@ -183,14 +177,6 @@ protected:
|
||||||
virtual void updateEnablement(bool enable) {
|
virtual void updateEnablement(bool enable) {
|
||||||
Q_UNUSED(enable);
|
Q_UNUSED(enable);
|
||||||
}
|
}
|
||||||
virtual void updateMode(const QSize &size, uint32_t refreshRate)
|
|
||||||
{
|
|
||||||
Q_UNUSED(size);
|
|
||||||
Q_UNUSED(refreshRate);
|
|
||||||
}
|
|
||||||
virtual void applyMode(int modeIndex) {
|
|
||||||
Q_UNUSED(modeIndex);
|
|
||||||
}
|
|
||||||
virtual void updateTransform(Transform transform) {
|
virtual void updateTransform(Transform transform) {
|
||||||
Q_UNUSED(transform);
|
Q_UNUSED(transform);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "egl_multi_backend.h"
|
#include "egl_multi_backend.h"
|
||||||
#include "drm_pipeline.h"
|
#include "drm_pipeline.h"
|
||||||
#include "drm_virtual_output.h"
|
#include "drm_virtual_output.h"
|
||||||
|
#include "waylandoutputconfig.h"
|
||||||
#if HAVE_GBM
|
#if HAVE_GBM
|
||||||
#include "egl_gbm_backend.h"
|
#include "egl_gbm_backend.h"
|
||||||
#include <gbm.h>
|
#include <gbm.h>
|
||||||
|
@ -351,7 +352,7 @@ void DrmBackend::updateOutputs()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (oldOutputs != m_outputs) {
|
if (oldOutputs != m_outputs) {
|
||||||
readOutputsConfiguration();
|
readOutputsConfiguration(m_outputs);
|
||||||
}
|
}
|
||||||
Q_EMIT screensQueried();
|
Q_EMIT screensQueried();
|
||||||
}
|
}
|
||||||
|
@ -443,40 +444,43 @@ namespace KWinKScreenIntegration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmBackend::readOutputsConfiguration()
|
void DrmBackend::readOutputsConfiguration(const QVector<DrmAbstractOutput*> &outputs)
|
||||||
{
|
{
|
||||||
if (m_outputs.isEmpty()) {
|
const auto outputsInfo = KWinKScreenIntegration::outputsConfig(outputs);
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto outputsInfo = KWinKScreenIntegration::outputsConfig(m_outputs);
|
|
||||||
|
|
||||||
AbstractOutput *primaryOutput = m_outputs.constFirst();
|
AbstractOutput *primaryOutput = m_outputs.constFirst();
|
||||||
|
WaylandOutputConfig cfg;
|
||||||
// default position goes from left to right
|
// default position goes from left to right
|
||||||
QPoint pos(0, 0);
|
QPoint pos(0, 0);
|
||||||
for (auto it = m_outputs.begin(); it != m_outputs.end(); ++it) {
|
for (const auto &output : qAsConst(outputs)) {
|
||||||
const QJsonObject outputInfo = outputsInfo[*it];
|
auto props = cfg.changeSet(output);
|
||||||
qCDebug(KWIN_DRM) << "Reading output configuration for " << *it;
|
const QJsonObject outputInfo = outputsInfo[output];
|
||||||
|
qCDebug(KWIN_DRM) << "Reading output configuration for " << output;
|
||||||
if (!outputInfo.isEmpty()) {
|
if (!outputInfo.isEmpty()) {
|
||||||
if (outputInfo["primary"].toBool()) {
|
if (outputInfo["primary"].toBool()) {
|
||||||
primaryOutput = *it;
|
primaryOutput = output;
|
||||||
}
|
}
|
||||||
|
props->enabled = outputInfo["enabled"].toBool(true);
|
||||||
const QJsonObject pos = outputInfo["pos"].toObject();
|
const QJsonObject pos = outputInfo["pos"].toObject();
|
||||||
(*it)->moveTo({pos["x"].toInt(), pos["y"].toInt()});
|
props->pos = QPoint(pos["x"].toInt(), pos["y"].toInt());
|
||||||
if (const QJsonValue scale = outputInfo["scale"]; !scale.isUndefined()) {
|
if (const QJsonValue scale = outputInfo["scale"]; !scale.isUndefined()) {
|
||||||
(*it)->setScale(scale.toDouble(1.));
|
props->scale = scale.toDouble(1.);
|
||||||
}
|
}
|
||||||
(*it)->updateTransform(KWinKScreenIntegration::toDrmTransform(outputInfo["rotation"].toInt()));
|
props->transform = KWinKScreenIntegration::toDrmTransform(outputInfo["rotation"].toInt());
|
||||||
|
|
||||||
if (const QJsonObject mode = outputInfo["mode"].toObject(); !mode.isEmpty()) {
|
if (const QJsonObject mode = outputInfo["mode"].toObject(); !mode.isEmpty()) {
|
||||||
const QJsonObject size = mode["size"].toObject();
|
const QJsonObject size = mode["size"].toObject();
|
||||||
(*it)->updateMode(QSize(size["width"].toInt(), size["height"].toInt()), mode["refresh"].toDouble() * 1000);
|
props->modeSize = QSize(size["width"].toInt(), size["height"].toInt());
|
||||||
|
props->refreshRate = mode["refresh"].toDouble() * 1000;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
(*it)->moveTo(pos);
|
props->enabled = true;
|
||||||
(*it)->updateTransform(DrmOutput::Transform::Normal);
|
props->pos = pos;
|
||||||
|
props->transform = DrmOutput::Transform::Normal;
|
||||||
}
|
}
|
||||||
pos.setX(pos.x() + (*it)->geometry().width());
|
pos.setX(pos.x() + output->geometry().width());
|
||||||
}
|
}
|
||||||
|
applyOutputChanges(cfg);
|
||||||
setPrimaryOutput(primaryOutput);
|
setPrimaryOutput(primaryOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,12 +501,9 @@ void DrmBackend::enableOutput(DrmAbstractOutput *output, bool enable)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (m_enabledOutputs.count() == 1) {
|
if (m_enabledOutputs.count() == 1) {
|
||||||
for (const auto &o : qAsConst(m_outputs)) {
|
auto outputs = m_outputs;
|
||||||
if (o != output) {
|
outputs.removeOne(output);
|
||||||
o->setEnabled(true);
|
readOutputsConfiguration(outputs);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (m_enabledOutputs.count() == 1 && !kwinApp()->isTerminating()) {
|
if (m_enabledOutputs.count() == 1 && !kwinApp()->isTerminating()) {
|
||||||
qCDebug(KWIN_DRM) << "adding placeholder output";
|
qCDebug(KWIN_DRM) << "adding placeholder output";
|
||||||
|
@ -683,7 +684,7 @@ QString DrmBackend::supportInformation() const
|
||||||
AbstractOutput *DrmBackend::createVirtualOutput(const QString &name, const QSize &size, double scale)
|
AbstractOutput *DrmBackend::createVirtualOutput(const QString &name, const QSize &size, double scale)
|
||||||
{
|
{
|
||||||
auto output = primaryGpu()->createVirtualOutput(name, size * scale, scale, DrmGpu::Full);
|
auto output = primaryGpu()->createVirtualOutput(name, size * scale, scale, DrmGpu::Full);
|
||||||
readOutputsConfiguration();
|
readOutputsConfiguration(m_outputs);
|
||||||
Q_EMIT screensQueried();
|
Q_EMIT screensQueried();
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -733,4 +734,36 @@ DrmGpu *DrmBackend::findGpuByFd(int fd) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DrmBackend::applyOutputChanges(const WaylandOutputConfig &config)
|
||||||
|
{
|
||||||
|
QVector<DrmOutput*> changed;
|
||||||
|
for (const auto &gpu : qAsConst(m_gpus)) {
|
||||||
|
const auto &outputs = gpu->outputs();
|
||||||
|
for (const auto &o : outputs) {
|
||||||
|
DrmOutput *output = qobject_cast<DrmOutput*>(o);
|
||||||
|
if (!output) {
|
||||||
|
// virtual outputs don't need testing
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
output->queueChanges(config);
|
||||||
|
changed << output;
|
||||||
|
}
|
||||||
|
if (!gpu->testPendingConfiguration()) {
|
||||||
|
for (const auto &output : qAsConst(changed)) {
|
||||||
|
output->revertQueuedChanges();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &output : qAsConst(m_outputs)) {
|
||||||
|
if (auto drmOutput = qobject_cast<DrmOutput*>(output)) {
|
||||||
|
drmOutput->applyQueuedChanges(config);
|
||||||
|
} else {
|
||||||
|
output->applyChanges(config);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateCursor();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ protected:
|
||||||
void doHideCursor() override;
|
void doHideCursor() override;
|
||||||
void doShowCursor() override;
|
void doShowCursor() override;
|
||||||
void doSetSoftwareCursor() override;
|
void doSetSoftwareCursor() override;
|
||||||
|
bool applyOutputChanges(const WaylandOutputConfig &config) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmGpu;
|
friend class DrmGpu;
|
||||||
|
@ -94,7 +95,7 @@ private:
|
||||||
void updateCursor();
|
void updateCursor();
|
||||||
void moveCursor();
|
void moveCursor();
|
||||||
void initCursor();
|
void initCursor();
|
||||||
void readOutputsConfiguration();
|
void readOutputsConfiguration(const QVector<DrmAbstractOutput*> &outputs);
|
||||||
void handleUdevEvent();
|
void handleUdevEvent();
|
||||||
DrmGpu *addGpu(const QString &fileName);
|
DrmGpu *addGpu(const QString &fileName);
|
||||||
|
|
||||||
|
|
|
@ -183,18 +183,14 @@ void DrmGpu::initDrmResources()
|
||||||
}
|
}
|
||||||
if (m_planes.isEmpty()) {
|
if (m_planes.isEmpty()) {
|
||||||
qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode on GPU " << m_devNode;
|
qCWarning(KWIN_DRM) << "Failed to create any plane. Falling back to legacy mode on GPU " << m_devNode;
|
||||||
m_atomicModeSetting = false;
|
|
||||||
} else {
|
|
||||||
m_atomicModeSetting = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode on GPU " << m_devNode;
|
qCWarning(KWIN_DRM) << "Failed to get plane resources. Falling back to legacy mode on GPU " << m_devNode;
|
||||||
m_atomicModeSetting = false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode on GPU" << m_devNode;
|
qCWarning(KWIN_DRM) << "drmSetClientCap for Atomic Mode Setting failed. Using legacy mode on GPU" << m_devNode;
|
||||||
m_atomicModeSetting = false;
|
|
||||||
}
|
}
|
||||||
|
m_atomicModeSetting = !m_planes.isEmpty();
|
||||||
|
|
||||||
DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
|
DrmScopedPointer<drmModeRes> resources(drmModeGetResources(m_fd));
|
||||||
if (!resources) {
|
if (!resources) {
|
||||||
|
@ -266,6 +262,7 @@ bool DrmGpu::updateOutputs()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
m_connectors << c;
|
m_connectors << c;
|
||||||
|
m_pipelines << c->pipeline();
|
||||||
} else {
|
} else {
|
||||||
(*it)->updateProperties();
|
(*it)->updateProperties();
|
||||||
if ((*it)->isConnected()) {
|
if ((*it)->isConnected()) {
|
||||||
|
@ -280,17 +277,30 @@ bool DrmGpu::updateOutputs()
|
||||||
removeLeaseOutput(leaseOutput);
|
removeLeaseOutput(leaseOutput);
|
||||||
}
|
}
|
||||||
m_connectors.removeOne(connector);
|
m_connectors.removeOne(connector);
|
||||||
|
m_pipelines.removeOne(connector->pipeline());
|
||||||
delete connector;
|
delete connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find unused and connected connectors
|
// find unused and connected connectors
|
||||||
QVector<DrmConnector *> connectedConnectors;
|
|
||||||
for (const auto &conn : qAsConst(m_connectors)) {
|
for (const auto &conn : qAsConst(m_connectors)) {
|
||||||
auto output = findOutput(conn->id());
|
auto output = findOutput(conn->id());
|
||||||
if (conn->isConnected()) {
|
if (conn->isConnected()) {
|
||||||
connectedConnectors << conn;
|
|
||||||
if (output) {
|
if (output) {
|
||||||
output->updateModes();
|
output->updateModes();
|
||||||
|
} else if (!findLeaseOutput(conn->id())) {
|
||||||
|
qCDebug(KWIN_DRM, "New %soutput on GPU %s: %s", conn->isNonDesktop() ? "non-desktop " : "", qPrintable(m_devNode), qPrintable(conn->modelName()));
|
||||||
|
if (conn->isNonDesktop()) {
|
||||||
|
auto leaseOutput = new DrmLeaseOutput(conn->pipeline(), m_leaseDevice);
|
||||||
|
m_leaseOutputs << leaseOutput;
|
||||||
|
} else {
|
||||||
|
auto output = new DrmOutput(conn->pipeline());
|
||||||
|
if (!output->initCursor(m_cursorSize)) {
|
||||||
|
m_platform->setSoftwareCursor(true);
|
||||||
|
}
|
||||||
|
m_drmOutputs << output;
|
||||||
|
m_outputs << output;
|
||||||
|
Q_EMIT outputAdded(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (output) {
|
} else if (output) {
|
||||||
removeOutput(output);
|
removeOutput(output);
|
||||||
|
@ -302,170 +312,100 @@ bool DrmGpu::updateOutputs()
|
||||||
// update crtc properties
|
// update crtc properties
|
||||||
for (const auto &crtc : qAsConst(m_crtcs)) {
|
for (const auto &crtc : qAsConst(m_crtcs)) {
|
||||||
crtc->updateProperties();
|
crtc->updateProperties();
|
||||||
|
crtc->setLegacyCursor();
|
||||||
}
|
}
|
||||||
// update plane properties
|
// update plane properties
|
||||||
for (const auto &plane : qAsConst(m_planes)) {
|
for (const auto &plane : qAsConst(m_planes)) {
|
||||||
plane->updateProperties();
|
plane->updateProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
// stash away current pipelines of active outputs
|
if (testPendingConfiguration()) {
|
||||||
QMap<DrmOutput*, DrmPipeline*> oldPipelines;
|
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||||
for (const auto &output : qAsConst(m_drmOutputs)) {
|
pipeline->applyPendingChanges();
|
||||||
if (!output->isEnabled()) {
|
if (!pipeline->pending.crtc && pipeline->output()) {
|
||||||
// create render resources for findWorkingCombination
|
pipeline->output()->setEnabled(false);
|
||||||
Q_EMIT outputEnabled(output);
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const auto &pipeline : qAsConst(m_pipelines)) {
|
||||||
|
pipeline->revertPendingChanges();
|
||||||
}
|
}
|
||||||
m_pipelines.removeOne(output->pipeline());
|
|
||||||
oldPipelines.insert(output, output->pipeline());
|
|
||||||
output->setPipeline(nullptr);
|
|
||||||
}
|
}
|
||||||
|
m_leaseDevice->setDrmMaster(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrmGpu::checkCrtcAssignment(QVector<DrmConnector*> connectors, QVector<DrmCrtc*> crtcs)
|
||||||
|
{
|
||||||
|
if (connectors.isEmpty() || crtcs.isEmpty()) {
|
||||||
|
if (m_pipelines.isEmpty()) {
|
||||||
|
// nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// remaining connectors can't be powered
|
||||||
|
for (const auto &conn : qAsConst(connectors)) {
|
||||||
|
qCWarning(KWIN_DRM) << "disabling connector" << conn->modelName() << "without a crtc";
|
||||||
|
conn->pipeline()->pending.crtc = nullptr;
|
||||||
|
}
|
||||||
|
return DrmPipeline::commitPipelines(m_pipelines, DrmPipeline::CommitMode::Test);
|
||||||
|
}
|
||||||
|
auto connector = connectors.takeFirst();
|
||||||
|
auto pipeline = connector->pipeline();
|
||||||
|
if (const auto &output = pipeline->output(); output && !pipeline->pending.active) {
|
||||||
|
// disabled pipelines don't need CRTCs
|
||||||
|
pipeline->pending.crtc = nullptr;
|
||||||
|
return checkCrtcAssignment(connectors, crtcs);
|
||||||
|
}
|
||||||
if (m_atomicModeSetting) {
|
if (m_atomicModeSetting) {
|
||||||
// sort outputs by being already connected (to any CRTC) so that already working outputs get preferred
|
// try the crtc that this connector is already connected to first
|
||||||
std::sort(connectedConnectors.begin(), connectedConnectors.end(), [](auto c1, auto c2){
|
std::sort(crtcs.begin(), crtcs.end(), [connector](auto c1, auto c2){
|
||||||
return c1->getProp(DrmConnector::PropertyIndex::CrtcId)->current() > c2->getProp(DrmConnector::PropertyIndex::CrtcId)->current();
|
Q_UNUSED(c2)
|
||||||
|
return connector->getProp(DrmConnector::PropertyIndex::CrtcId)->pending() == c1->id();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
auto connectors = connectedConnectors;
|
auto encoders = connector->encoders();
|
||||||
|
for (const auto &encoder : encoders) {
|
||||||
|
DrmScopedPointer<drmModeEncoder> enc(drmModeGetEncoder(m_fd, encoder));
|
||||||
|
if (!enc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const auto &crtc : qAsConst(crtcs)) {
|
||||||
|
if ((enc->possible_crtcs & (1 << crtc->pipeIndex()))) {
|
||||||
|
auto crtcsLeft = crtcs;
|
||||||
|
crtcsLeft.removeOne(crtc);
|
||||||
|
pipeline->pending.crtc = crtc;
|
||||||
|
if (checkCrtcAssignment(connectors, crtcsLeft)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrmGpu::testPendingConfiguration()
|
||||||
|
{
|
||||||
|
QVector<DrmConnector *> connectors;
|
||||||
|
for (const auto &conn : qAsConst(m_connectors)) {
|
||||||
|
if (conn->isConnected()) {
|
||||||
|
connectors << conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto crtcs = m_crtcs;
|
auto crtcs = m_crtcs;
|
||||||
// don't touch resources that are leased
|
// don't touch resources that are leased
|
||||||
for (const auto &output : qAsConst(m_leaseOutputs)) {
|
for (const auto &output : qAsConst(m_leaseOutputs)) {
|
||||||
if (output->lease()) {
|
if (output->lease()) {
|
||||||
connectors.removeOne(output->pipeline()->connector());
|
connectors.removeOne(output->pipeline()->connector());
|
||||||
crtcs.removeOne(output->pipeline()->crtc());
|
crtcs.removeOne(output->pipeline()->pending.crtc);
|
||||||
} else {
|
|
||||||
m_pipelines.removeOne(output->pipeline());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto config = findWorkingCombination({}, connectors, crtcs);
|
|
||||||
if (config.isEmpty() && !connectors.isEmpty()) {
|
|
||||||
qCCritical(KWIN_DRM) << "DrmGpu::findWorkingCombination failed to find any functional combinations! Reverting to the old configuration!";
|
|
||||||
for (auto it = oldPipelines.begin(); it != oldPipelines.end(); it++) {
|
|
||||||
it.value()->setOutput(it.key());
|
|
||||||
config << it.value();
|
|
||||||
}
|
|
||||||
for (const auto &leaseOutput : qAsConst(m_leaseOutputs)) {
|
|
||||||
if (!leaseOutput->lease()) {
|
|
||||||
config << leaseOutput->pipeline();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const auto &pipeline : qAsConst(oldPipelines)) {
|
|
||||||
delete pipeline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_pipelines << config;
|
|
||||||
|
|
||||||
for (auto it = config.crbegin(); it != config.crend(); it++) {
|
|
||||||
const auto &pipeline = *it;
|
|
||||||
auto output = pipeline->output();
|
|
||||||
if (pipeline->connector()->isNonDesktop()) {
|
|
||||||
if (const auto &leaseOutput = findLeaseOutput(pipeline->connector()->id())) {
|
|
||||||
leaseOutput->setPipeline(pipeline);
|
|
||||||
} else {
|
|
||||||
qCDebug(KWIN_DRM, "New non-desktop output on GPU %s: %s", qPrintable(m_devNode), qPrintable(pipeline->connector()->modelName()));
|
|
||||||
m_leaseOutputs << new DrmLeaseOutput(pipeline, m_leaseDevice);
|
|
||||||
}
|
|
||||||
pipeline->setActive(false);
|
|
||||||
} else if (m_outputs.contains(output)) {
|
|
||||||
// restore output properties
|
|
||||||
if (output->isEnabled()) {
|
|
||||||
output->updateTransform(output->transform());
|
|
||||||
if (output->dpmsMode() != AbstractWaylandOutput::DpmsMode::On) {
|
|
||||||
pipeline->setActive(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pipeline->setActive(false);
|
|
||||||
Q_EMIT outputDisabled(output);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qCDebug(KWIN_DRM).nospace() << "New output on GPU " << m_devNode << ": " << pipeline->connector()->modelName();
|
|
||||||
if (!output->initCursor(m_cursorSize)) {
|
|
||||||
m_platform->setSoftwareCursorForced(true);
|
|
||||||
}
|
|
||||||
m_outputs << output;
|
|
||||||
m_drmOutputs << output;
|
|
||||||
Q_EMIT outputAdded(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_leaseDevice->setDrmMaster(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<DrmPipeline *> DrmGpu::findWorkingCombination(const QVector<DrmPipeline *> &pipelines, QVector<DrmConnector *> connectors, QVector<DrmCrtc *> crtcs)
|
|
||||||
{
|
|
||||||
if (connectors.isEmpty() || crtcs.isEmpty()) {
|
|
||||||
// no further pipelines can be added -> test configuration
|
|
||||||
if (pipelines.isEmpty() || commitCombination(pipelines)) {
|
|
||||||
return pipelines;
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto connector = connectors.takeFirst();
|
|
||||||
const auto encoders = connector->encoders();
|
|
||||||
|
|
||||||
if (m_atomicModeSetting) {
|
if (m_atomicModeSetting) {
|
||||||
// try the crtc that this connector is already connected to first
|
// sort outputs by being already connected (to any CRTC) so that already working outputs get preferred
|
||||||
std::sort(crtcs.begin(), crtcs.end(), [connector](auto c1, auto c2){
|
std::sort(connectors.begin(), connectors.end(), [](auto c1, auto c2){
|
||||||
Q_UNUSED(c2)
|
return c1->getProp(DrmConnector::PropertyIndex::CrtcId)->current() > c2->getProp(DrmConnector::PropertyIndex::CrtcId)->current();
|
||||||
if (connector->getProp(DrmConnector::PropertyIndex::CrtcId)->current() == c1->id()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return checkCrtcAssignment(connectors, crtcs);
|
||||||
auto recurse = [this, connector, connectors, crtcs, pipelines] (DrmCrtc *crtc) {
|
|
||||||
auto pipeline = new DrmPipeline(this, connector, crtc);
|
|
||||||
auto crtcsLeft = crtcs;
|
|
||||||
crtcsLeft.removeOne(crtc);
|
|
||||||
auto allPipelines = pipelines;
|
|
||||||
allPipelines << pipeline;
|
|
||||||
auto ret = findWorkingCombination(allPipelines, connectors, crtcsLeft);
|
|
||||||
if (ret.isEmpty()) {
|
|
||||||
delete pipeline;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
for (const auto &encoderId : encoders) {
|
|
||||||
DrmScopedPointer<drmModeEncoder> encoder(drmModeGetEncoder(m_fd, encoderId));
|
|
||||||
for (const auto &crtc : qAsConst(crtcs)) {
|
|
||||||
if (auto workingPipelines = recurse(crtc); !workingPipelines.isEmpty()) {
|
|
||||||
return workingPipelines;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmGpu::commitCombination(const QVector<DrmPipeline *> &pipelines)
|
|
||||||
{
|
|
||||||
for (const auto &pipeline : pipelines) {
|
|
||||||
auto output = findOutput(pipeline->connector()->id());
|
|
||||||
if (output) {
|
|
||||||
output->setPipeline(pipeline);
|
|
||||||
pipeline->setOutput(output);
|
|
||||||
} else if (!pipeline->connector()->isNonDesktop()) {
|
|
||||||
output = new DrmOutput(this, pipeline);
|
|
||||||
Q_EMIT outputEnabled(output);// create render resources for the test
|
|
||||||
}
|
|
||||||
pipeline->setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DrmPipeline::commitPipelines(pipelines, DrmPipeline::CommitMode::Test)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
for (const auto &pipeline : qAsConst(pipelines)) {
|
|
||||||
if (!m_outputs.contains(pipeline->output())) {
|
|
||||||
Q_EMIT outputDisabled(pipeline->output());
|
|
||||||
delete pipeline->output();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmOutput *DrmGpu::findOutput(quint32 connector)
|
DrmOutput *DrmGpu::findOutput(quint32 connector)
|
||||||
|
@ -585,10 +525,7 @@ void DrmGpu::removeOutput(DrmOutput *output)
|
||||||
m_drmOutputs.removeOne(output);
|
m_drmOutputs.removeOne(output);
|
||||||
m_outputs.removeOne(output);
|
m_outputs.removeOne(output);
|
||||||
Q_EMIT outputRemoved(output);
|
Q_EMIT outputRemoved(output);
|
||||||
auto pipeline = output->m_pipeline;
|
|
||||||
delete output;
|
delete output;
|
||||||
m_pipelines.removeOne(pipeline);
|
|
||||||
delete pipeline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractEglDrmBackend *DrmGpu::eglBackend() const
|
AbstractEglDrmBackend *DrmGpu::eglBackend() const
|
||||||
|
@ -662,7 +599,10 @@ void DrmGpu::handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseReques
|
||||||
for (const auto &connector : conns) {
|
for (const auto &connector : conns) {
|
||||||
auto output = qobject_cast<DrmLeaseOutput*>(connector);
|
auto output = qobject_cast<DrmLeaseOutput*>(connector);
|
||||||
if (m_leaseOutputs.contains(output) && !output->lease()) {
|
if (m_leaseOutputs.contains(output) && !output->lease()) {
|
||||||
output->addLeaseObjects(objects);
|
if (!output->addLeaseObjects(objects)) {
|
||||||
|
leaseRequest->deny();
|
||||||
|
return;
|
||||||
|
}
|
||||||
outputs << output;
|
outputs << output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -704,10 +644,7 @@ void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output)
|
||||||
{
|
{
|
||||||
qCDebug(KWIN_DRM) << "Removing leased output" << output;
|
qCDebug(KWIN_DRM) << "Removing leased output" << output;
|
||||||
m_leaseOutputs.removeOne(output);
|
m_leaseOutputs.removeOne(output);
|
||||||
auto pipeline = output->pipeline();
|
|
||||||
delete output;
|
delete output;
|
||||||
m_pipelines.removeOne(pipeline);
|
|
||||||
delete pipeline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<DrmAbstractOutput*> DrmGpu::outputs() const
|
QVector<DrmAbstractOutput*> DrmGpu::outputs() const
|
||||||
|
|
|
@ -69,6 +69,7 @@ public:
|
||||||
|
|
||||||
QVector<DrmAbstractOutput*> outputs() const;
|
QVector<DrmAbstractOutput*> outputs() const;
|
||||||
const QVector<DrmPipeline*> pipelines() const;
|
const QVector<DrmPipeline*> pipelines() const;
|
||||||
|
bool testPendingConfiguration();
|
||||||
|
|
||||||
void setGbmDevice(gbm_device *d);
|
void setGbmDevice(gbm_device *d);
|
||||||
void setEglDisplay(EGLDisplay display);
|
void setEglDisplay(EGLDisplay display);
|
||||||
|
@ -96,8 +97,7 @@ private:
|
||||||
void removeLeaseOutput(DrmLeaseOutput *output);
|
void removeLeaseOutput(DrmLeaseOutput *output);
|
||||||
void initDrmResources();
|
void initDrmResources();
|
||||||
|
|
||||||
QVector<DrmPipeline *> findWorkingCombination(const QVector<DrmPipeline *> &pipelines, QVector<DrmConnector *> connectors, QVector<DrmCrtc *> crtcs);
|
bool checkCrtcAssignment(QVector<DrmConnector*> connectors, QVector<DrmCrtc*> crtcs);
|
||||||
bool commitCombination(const QVector<DrmPipeline *> &pipelines);
|
|
||||||
|
|
||||||
void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest);
|
void handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest);
|
||||||
void handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease);
|
void handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease);
|
||||||
|
|
|
@ -39,14 +39,19 @@ DrmLeaseOutput::~DrmLeaseOutput()
|
||||||
qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id();
|
qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmLeaseOutput::addLeaseObjects(QVector<uint32_t> &objectList)
|
bool DrmLeaseOutput::addLeaseObjects(QVector<uint32_t> &objectList)
|
||||||
{
|
{
|
||||||
|
if (!m_pipeline->pending.crtc) {
|
||||||
|
qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
|
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
|
||||||
objectList << m_pipeline->connector()->id();
|
objectList << m_pipeline->connector()->id();
|
||||||
objectList << m_pipeline->crtc()->id();
|
objectList << m_pipeline->pending.crtc->id();
|
||||||
if (m_pipeline->primaryPlane()) {
|
if (m_pipeline->pending.crtc->primaryPlane()) {
|
||||||
objectList << m_pipeline->primaryPlane()->id();
|
objectList << m_pipeline->pending.crtc->primaryPlane()->id();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmLeaseOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
|
void DrmLeaseOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
|
||||||
|
@ -60,11 +65,4 @@ void DrmLeaseOutput::leaseEnded()
|
||||||
m_lease = nullptr;
|
m_lease = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmLeaseOutput::setPipeline(DrmPipeline *pipeline)
|
|
||||||
{
|
|
||||||
Q_ASSERT_X(pipeline->connector() == m_pipeline->connector(), "DrmLeaseOutput::setPipeline", "Pipeline with wrong connector set!");
|
|
||||||
delete m_pipeline;
|
|
||||||
m_pipeline = pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public:
|
||||||
DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
|
DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
|
||||||
~DrmLeaseOutput() override;
|
~DrmLeaseOutput() override;
|
||||||
|
|
||||||
void addLeaseObjects(QVector<uint32_t> &objectList);
|
bool addLeaseObjects(QVector<uint32_t> &objectList);
|
||||||
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
|
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
|
||||||
void leaseEnded();
|
void leaseEnded();
|
||||||
|
|
||||||
|
@ -42,7 +42,6 @@ public:
|
||||||
DrmPipeline *pipeline() const {
|
DrmPipeline *pipeline() const {
|
||||||
return m_pipeline;
|
return m_pipeline;
|
||||||
}
|
}
|
||||||
void setPipeline(DrmPipeline *pipeline);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DrmPipeline *m_pipeline;
|
DrmPipeline *m_pipeline;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "drm_gpu.h"
|
#include "drm_gpu.h"
|
||||||
#include "drm_pointer.h"
|
#include "drm_pointer.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "drm_pipeline.h"
|
||||||
|
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
// frameworks
|
// frameworks
|
||||||
|
@ -42,6 +43,7 @@ DrmConnector::DrmConnector(DrmGpu *gpu, uint32_t connectorId)
|
||||||
QByteArrayLiteral("Limited 16:235")
|
QByteArrayLiteral("Limited 16:235")
|
||||||
}),
|
}),
|
||||||
}, DRM_MODE_OBJECT_CONNECTOR)
|
}, DRM_MODE_OBJECT_CONNECTOR)
|
||||||
|
, m_pipeline(new DrmPipeline(this))
|
||||||
, m_conn(drmModeGetConnector(gpu->fd(), connectorId))
|
, m_conn(drmModeGetConnector(gpu->fd(), connectorId))
|
||||||
{
|
{
|
||||||
if (m_conn) {
|
if (m_conn) {
|
||||||
|
@ -363,6 +365,11 @@ const Edid *DrmConnector::edid() const
|
||||||
return &m_edid;
|
return &m_edid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DrmPipeline *DrmConnector::pipeline() const
|
||||||
|
{
|
||||||
|
return m_pipeline.data();
|
||||||
|
}
|
||||||
|
|
||||||
QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj)
|
QDebug& operator<<(QDebug& s, const KWin::DrmConnector *obj)
|
||||||
{
|
{
|
||||||
QDebugStateSaver saver(s);
|
QDebugStateSaver saver(s);
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class DrmPipeline;
|
||||||
|
|
||||||
class DrmConnector : public DrmObject
|
class DrmConnector : public DrmObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -56,6 +58,7 @@ public:
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
bool isNonDesktop() const;
|
bool isNonDesktop() const;
|
||||||
bool isInternal() const;
|
bool isInternal() const;
|
||||||
|
DrmPipeline *pipeline() const;
|
||||||
|
|
||||||
const Edid *edid() const;
|
const Edid *edid() const;
|
||||||
QString connectorName() const;
|
QString connectorName() const;
|
||||||
|
@ -83,6 +86,7 @@ public:
|
||||||
AbstractWaylandOutput::RgbRange rgbRange() const;
|
AbstractWaylandOutput::RgbRange rgbRange() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QScopedPointer<DrmPipeline> m_pipeline;
|
||||||
DrmScopedPointer<drmModeConnector> m_conn;
|
DrmScopedPointer<drmModeConnector> m_conn;
|
||||||
QVector<uint32_t> m_encoders;
|
QVector<uint32_t> m_encoders;
|
||||||
Edid m_edid;
|
Edid m_edid;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "renderloop_p.h"
|
#include "renderloop_p.h"
|
||||||
#include "screens.h"
|
#include "screens.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
|
#include "waylandoutputconfig.h"
|
||||||
// Qt
|
// Qt
|
||||||
#include <QMatrix4x4>
|
#include <QMatrix4x4>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
|
@ -34,8 +35,8 @@
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
DrmOutput::DrmOutput(DrmGpu *gpu, DrmPipeline *pipeline)
|
DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
||||||
: DrmAbstractOutput(gpu)
|
: DrmAbstractOutput(pipeline->connector()->gpu())
|
||||||
, m_pipeline(pipeline)
|
, m_pipeline(pipeline)
|
||||||
, m_connector(pipeline->connector())
|
, m_connector(pipeline->connector())
|
||||||
{
|
{
|
||||||
|
@ -68,7 +69,6 @@ DrmOutput::DrmOutput(DrmGpu *gpu, DrmPipeline *pipeline)
|
||||||
|
|
||||||
DrmOutput::~DrmOutput()
|
DrmOutput::~DrmOutput()
|
||||||
{
|
{
|
||||||
hideCursor();
|
|
||||||
if (m_pageFlipPending) {
|
if (m_pageFlipPending) {
|
||||||
pageFlipped();
|
pageFlipped();
|
||||||
}
|
}
|
||||||
|
@ -78,14 +78,14 @@ DrmOutput::~DrmOutput()
|
||||||
bool DrmOutput::initCursor(const QSize &cursorSize)
|
bool DrmOutput::initCursor(const QSize &cursorSize)
|
||||||
{
|
{
|
||||||
m_cursor = QSharedPointer<DrmDumbBuffer>::create(m_gpu, cursorSize);
|
m_cursor = QSharedPointer<DrmDumbBuffer>::create(m_gpu, cursorSize);
|
||||||
if (!m_cursor->map(QImage::Format_ARGB32_Premultiplied)) {
|
return m_cursor->map(QImage::Format_ARGB32_Premultiplied);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return updateCursor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmOutput::hideCursor()
|
bool DrmOutput::hideCursor()
|
||||||
{
|
{
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool visibleBefore = m_pipeline->isCursorVisible();
|
bool visibleBefore = m_pipeline->isCursorVisible();
|
||||||
if (m_pipeline->setCursor(nullptr)) {
|
if (m_pipeline->setCursor(nullptr)) {
|
||||||
if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive
|
if (RenderLoopPrivate::get(m_renderLoop)->presentMode == RenderLoopPrivate::SyncMode::Adaptive
|
||||||
|
@ -100,6 +100,9 @@ bool DrmOutput::hideCursor()
|
||||||
|
|
||||||
bool DrmOutput::showCursor()
|
bool DrmOutput::showCursor()
|
||||||
{
|
{
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool visibleBefore = m_pipeline->isCursorVisible();
|
bool visibleBefore = m_pipeline->isCursorVisible();
|
||||||
const Cursor * const cursor = Cursors::self()->currentCursor();
|
const Cursor * const cursor = Cursors::self()->currentCursor();
|
||||||
if (m_pipeline->setCursor(m_cursor, logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot()) )) {
|
if (m_pipeline->setCursor(m_cursor, logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot()) )) {
|
||||||
|
@ -126,6 +129,9 @@ static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite)
|
||||||
|
|
||||||
bool DrmOutput::updateCursor()
|
bool DrmOutput::updateCursor()
|
||||||
{
|
{
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const Cursor *cursor = Cursors::self()->currentCursor();
|
const Cursor *cursor = Cursors::self()->currentCursor();
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
hideCursor();
|
hideCursor();
|
||||||
|
@ -161,6 +167,9 @@ bool DrmOutput::updateCursor()
|
||||||
|
|
||||||
bool DrmOutput::moveCursor()
|
bool DrmOutput::moveCursor()
|
||||||
{
|
{
|
||||||
|
if (!isEnabled()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Cursor *cursor = Cursors::self()->currentCursor();
|
Cursor *cursor = Cursors::self()->currentCursor();
|
||||||
const QMatrix4x4 hotspotMatrix = logicalToNativeMatrix(cursor->rect(), scale(), transform());
|
const QMatrix4x4 hotspotMatrix = logicalToNativeMatrix(cursor->rect(), scale(), transform());
|
||||||
const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform());
|
const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform());
|
||||||
|
@ -222,11 +231,7 @@ void DrmOutput::initOutputDevice()
|
||||||
|
|
||||||
void DrmOutput::updateEnablement(bool enable)
|
void DrmOutput::updateEnablement(bool enable)
|
||||||
{
|
{
|
||||||
if (m_pipeline->setActive(enable)) {
|
m_gpu->platform()->enableOutput(this, enable);
|
||||||
m_gpu->platform()->enableOutput(this, enable);
|
|
||||||
} else {
|
|
||||||
qCCritical(KWIN_DRM) << "failed to update enablement to" << enable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::setDpmsMode(DpmsMode mode)
|
void DrmOutput::setDpmsMode(DpmsMode mode)
|
||||||
|
@ -241,27 +246,26 @@ void DrmOutput::setDpmsMode(DpmsMode mode)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m_turnOffTimer.stop();
|
m_turnOffTimer.stop();
|
||||||
setDrmDpmsMode(mode);
|
if (mode != dpmsMode() && setDrmDpmsMode(mode)) {
|
||||||
|
|
||||||
if (mode != dpmsMode()) {
|
|
||||||
setDpmsModeInternal(mode);
|
|
||||||
Q_EMIT wakeUp();
|
Q_EMIT wakeUp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::setDrmDpmsMode(DpmsMode mode)
|
bool DrmOutput::setDrmDpmsMode(DpmsMode mode)
|
||||||
{
|
{
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
bool active = mode == DpmsMode::On;
|
bool active = mode == DpmsMode::On;
|
||||||
bool isActive = dpmsMode() == DpmsMode::On;
|
bool isActive = dpmsMode() == DpmsMode::On;
|
||||||
if (active == isActive) {
|
if (active == isActive) {
|
||||||
setDpmsModeInternal(mode);
|
setDpmsModeInternal(mode);
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
if (m_pipeline->setActive(active)) {
|
m_pipeline->pending.active = active;
|
||||||
|
if (DrmPipeline::commitPipelines({m_pipeline}, active ? DrmPipeline::CommitMode::Test : DrmPipeline::CommitMode::Commit)) {
|
||||||
|
m_pipeline->applyPendingChanges();
|
||||||
setDpmsModeInternal(mode);
|
setDpmsModeInternal(mode);
|
||||||
if (active) {
|
if (active) {
|
||||||
m_renderLoop->uninhibit();
|
m_renderLoop->uninhibit();
|
||||||
|
@ -273,6 +277,14 @@ void DrmOutput::setDrmDpmsMode(DpmsMode mode)
|
||||||
m_renderLoop->inhibit();
|
m_renderLoop->inhibit();
|
||||||
m_gpu->platform()->createDpmsFilter();
|
m_gpu->platform()->createDpmsFilter();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
qCWarning(KWIN_DRM) << "Setting dpms mode failed!";
|
||||||
|
m_pipeline->revertPendingChanges();
|
||||||
|
if (isEnabled() && isActive && !active) {
|
||||||
|
m_gpu->platform()->checkOutputsAreOn();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,21 +316,21 @@ DrmPlane::Transformations outputToPlaneTransform(DrmOutput::Transform transform)
|
||||||
void DrmOutput::updateTransform(Transform transform)
|
void DrmOutput::updateTransform(Transform transform)
|
||||||
{
|
{
|
||||||
setTransformInternal(transform);
|
setTransformInternal(transform);
|
||||||
const auto planeTransform = outputToPlaneTransform(transform);
|
|
||||||
static bool valid;
|
static bool valid;
|
||||||
// If not set or wrong value, assume KWIN_DRM_SW_ROTATIONS_ONLY=1 until DRM transformations are reliable
|
// If not set or wrong value, assume KWIN_DRM_SW_ROTATIONS_ONLY=1 until DRM transformations are reliable
|
||||||
static int envOnlySoftwareRotations = qEnvironmentVariableIntValue("KWIN_DRM_SW_ROTATIONS_ONLY", &valid) != 0;
|
static int envOnlySoftwareRotations = qEnvironmentVariableIntValue("KWIN_DRM_SW_ROTATIONS_ONLY", &valid) != 0;
|
||||||
if (valid && !envOnlySoftwareRotations && !m_pipeline->setTransformation(planeTransform)) {
|
if (valid && !envOnlySoftwareRotations) {
|
||||||
qCDebug(KWIN_DRM) << "setting transformation to" << planeTransform << "failed!";
|
m_pipeline->pending.transformation = outputToPlaneTransform(transform);
|
||||||
// just in case, if we had any rotation before, clear it
|
if (!DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||||
m_pipeline->setTransformation(DrmPlane::Transformation::Rotate0);
|
// just in case, if we had any rotation before, clear it
|
||||||
}
|
m_pipeline->pending.transformation = DrmPlane::Transformation::Rotate0;
|
||||||
|
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||||
// show cursor only if is enabled, i.e if pointer device is present
|
m_pipeline->applyPendingChanges();
|
||||||
if (!m_gpu->platform()->isCursorHidden() && !m_gpu->platform()->usesSoftwareCursor()) {
|
} else {
|
||||||
// the cursor might need to get rotated
|
m_pipeline->revertPendingChanges();
|
||||||
showCursor();
|
qCWarning(KWIN_DRM) << "Can't switch rotation back to Rotate0!";
|
||||||
updateCursor();
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,40 +352,22 @@ void DrmOutput::updateModes()
|
||||||
|
|
||||||
// mode changed
|
// mode changed
|
||||||
if (mode.size != modeSize() || mode.refreshRate != refreshRate()) {
|
if (mode.size != modeSize() || mode.refreshRate != refreshRate()) {
|
||||||
applyMode(mode.id);
|
m_pipeline->pending.modeIndex = mode.id;
|
||||||
|
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||||
|
m_pipeline->applyPendingChanges();
|
||||||
|
auto mode = m_pipeline->connector()->currentMode();
|
||||||
|
setCurrentModeInternal(mode.size, mode.refreshRate);
|
||||||
|
m_renderLoop->setRefreshRate(mode.refreshRate);
|
||||||
|
} else {
|
||||||
|
qCWarning(KWIN_DRM) << "Setting changed mode failed!";
|
||||||
|
m_pipeline->revertPendingChanges();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmOutput::needsSoftwareTransformation() const
|
bool DrmOutput::needsSoftwareTransformation() const
|
||||||
{
|
{
|
||||||
return m_pipeline->transformation() != outputToPlaneTransform(transform());
|
return m_pipeline->pending.transformation != outputToPlaneTransform(transform());
|
||||||
}
|
|
||||||
|
|
||||||
void DrmOutput::updateMode(const QSize &size, uint32_t refreshRate)
|
|
||||||
{
|
|
||||||
auto conn = m_pipeline->connector();
|
|
||||||
if (conn->currentMode().size == size && conn->currentMode().refreshRate == refreshRate) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// try to find a fitting mode
|
|
||||||
auto modelist = conn->modes();
|
|
||||||
for (int i = 0; i < modelist.size(); i++) {
|
|
||||||
if (modelist[i].size == size && modelist[i].refreshRate == refreshRate) {
|
|
||||||
applyMode(i);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qCWarning(KWIN_DRM, "Could not find a fitting mode with size=%dx%d and refresh rate %d for output %s",
|
|
||||||
size.width(), size.height(), refreshRate, qPrintable(name()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrmOutput::applyMode(int modeIndex)
|
|
||||||
{
|
|
||||||
if (m_pipeline->modeset(modeIndex)) {
|
|
||||||
auto mode = m_pipeline->connector()->currentMode();
|
|
||||||
AbstractWaylandOutput::setCurrentModeInternal(mode.size, mode.refreshRate);
|
|
||||||
m_renderLoop->setRefreshRate(mode.refreshRate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::pageFlipped()
|
void DrmOutput::pageFlipped()
|
||||||
|
@ -389,8 +383,14 @@ bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damaged
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop);
|
RenderLoopPrivate *renderLoopPrivate = RenderLoopPrivate::get(m_renderLoop);
|
||||||
if (!m_pipeline->setSyncMode(renderLoopPrivate->presentMode)) {
|
if (m_pipeline->pending.syncMode != renderLoopPrivate->presentMode) {
|
||||||
setVrrPolicy(RenderLoop::VrrPolicy::Never);
|
m_pipeline->pending.syncMode = renderLoopPrivate->presentMode;
|
||||||
|
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||||
|
m_pipeline->applyPendingChanges();
|
||||||
|
} else {
|
||||||
|
m_pipeline->revertPendingChanges();
|
||||||
|
setVrrPolicy(RenderLoop::VrrPolicy::Never);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (m_pipeline->present(buffer)) {
|
if (m_pipeline->present(buffer)) {
|
||||||
m_pageFlipPending = true;
|
m_pageFlipPending = true;
|
||||||
|
@ -403,27 +403,22 @@ bool DrmOutput::present(const QSharedPointer<DrmBuffer> &buffer, QRegion damaged
|
||||||
|
|
||||||
int DrmOutput::gammaRampSize() const
|
int DrmOutput::gammaRampSize() const
|
||||||
{
|
{
|
||||||
return m_pipeline->crtc()->gammaRampSize();
|
return m_pipeline->pending.crtc ? m_pipeline->pending.crtc->gammaRampSize() : 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmOutput::setGammaRamp(const GammaRamp &gamma)
|
bool DrmOutput::setGammaRamp(const GammaRamp &gamma)
|
||||||
{
|
{
|
||||||
if (m_pipeline->setGammaRamp(gamma)) {
|
m_pipeline->pending.gamma = QSharedPointer<DrmGammaRamp>::create(m_gpu, gamma);
|
||||||
|
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test)) {
|
||||||
|
m_pipeline->applyPendingChanges();
|
||||||
m_renderLoop->scheduleRepaint();
|
m_renderLoop->scheduleRepaint();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
m_pipeline->revertPendingChanges();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::setOverscan(uint32_t overscan)
|
|
||||||
{
|
|
||||||
if (m_pipeline->setOverscan(overscan)) {
|
|
||||||
setOverscanInternal(overscan);
|
|
||||||
m_renderLoop->scheduleRepaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DrmConnector *DrmOutput::connector() const
|
DrmConnector *DrmOutput::connector() const
|
||||||
{
|
{
|
||||||
return m_connector;
|
return m_connector;
|
||||||
|
@ -449,18 +444,54 @@ QVector<uint64_t> DrmOutput::supportedModifiers(uint32_t drmFormat) const
|
||||||
return m_pipeline->supportedModifiers(drmFormat);
|
return m_pipeline->supportedModifiers(drmFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::setRgbRange(RgbRange range)
|
bool DrmOutput::queueChanges(const WaylandOutputConfig &config)
|
||||||
{
|
{
|
||||||
if (m_pipeline->setRgbRange(range)) {
|
auto props = config.constChangeSet(this);
|
||||||
setRgbRangeInternal(range);
|
m_pipeline->pending.active = props->enabled;
|
||||||
m_renderLoop->scheduleRepaint();
|
auto modelist = m_connector->modes();
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < modelist.size(); i++) {
|
||||||
|
if (modelist[i].size == props->modeSize && modelist[i].refreshRate == props->refreshRate) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (index == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_pipeline->pending.modeIndex = index;
|
||||||
|
m_pipeline->pending.overscan = props->overscan;
|
||||||
|
m_pipeline->pending.rgbRange = props->rgbRange;
|
||||||
|
m_pipeline->pending.transformation = DrmPlane::Transformation::Rotate0;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmOutput::setPipeline(DrmPipeline *pipeline)
|
void DrmOutput::applyQueuedChanges(const WaylandOutputConfig &config)
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(!pipeline || pipeline->connector() == m_connector, "DrmOutput::setPipeline", "Pipeline with wrong connector set!");
|
Q_EMIT aboutToChange();
|
||||||
m_pipeline = pipeline;
|
m_pipeline->applyPendingChanges();
|
||||||
|
|
||||||
|
auto props = config.constChangeSet(this);
|
||||||
|
setEnabled(props->enabled && m_pipeline->pending.crtc);
|
||||||
|
moveTo(props->pos);
|
||||||
|
setScale(props->scale);
|
||||||
|
updateTransform(props->transform);
|
||||||
|
|
||||||
|
m_connector->setModeIndex(m_pipeline->pending.modeIndex);
|
||||||
|
auto mode = m_connector->currentMode();
|
||||||
|
setCurrentModeInternal(mode.size, mode.refreshRate);
|
||||||
|
m_renderLoop->setRefreshRate(mode.refreshRate);
|
||||||
|
setOverscanInternal(m_pipeline->pending.overscan);
|
||||||
|
setRgbRangeInternal(m_pipeline->pending.rgbRange);
|
||||||
|
setVrrPolicy(props->vrrPolicy);
|
||||||
|
|
||||||
|
m_renderLoop->scheduleRepaint();
|
||||||
|
Q_EMIT changed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrmOutput::revertQueuedChanges()
|
||||||
|
{
|
||||||
|
m_pipeline->revertPendingChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,25 +51,26 @@ public:
|
||||||
|
|
||||||
DrmConnector *connector() const;
|
DrmConnector *connector() const;
|
||||||
DrmPipeline *pipeline() const;
|
DrmPipeline *pipeline() const;
|
||||||
void setPipeline(DrmPipeline *pipeline);
|
|
||||||
|
|
||||||
QSize sourceSize() const override;
|
QSize sourceSize() const override;
|
||||||
bool isFormatSupported(uint32_t drmFormat) const override;
|
bool isFormatSupported(uint32_t drmFormat) const override;
|
||||||
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
|
QVector<uint64_t> supportedModifiers(uint32_t drmFormat) const override;
|
||||||
bool needsSoftwareTransformation() const override;
|
bool needsSoftwareTransformation() const override;
|
||||||
|
|
||||||
|
bool queueChanges(const WaylandOutputConfig &config);
|
||||||
|
void applyQueuedChanges(const WaylandOutputConfig &config);
|
||||||
|
void revertQueuedChanges();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class DrmGpu;
|
friend class DrmGpu;
|
||||||
friend class DrmBackend;
|
friend class DrmBackend;
|
||||||
DrmOutput(DrmGpu* gpu, DrmPipeline *pipeline);
|
DrmOutput(DrmPipeline *pipeline);
|
||||||
|
|
||||||
void initOutputDevice();
|
void initOutputDevice();
|
||||||
|
|
||||||
void updateEnablement(bool enable) override;
|
void updateEnablement(bool enable) override;
|
||||||
void setDrmDpmsMode(DpmsMode mode);
|
bool setDrmDpmsMode(DpmsMode mode);
|
||||||
void setDpmsMode(DpmsMode mode) override;
|
void setDpmsMode(DpmsMode mode) override;
|
||||||
void applyMode(int modeIndex) override;
|
|
||||||
void updateMode(const QSize &size, uint32_t refreshRate) override;
|
|
||||||
void updateModes();
|
void updateModes();
|
||||||
|
|
||||||
QVector<AbstractWaylandOutput::Mode> getModes() const;
|
QVector<AbstractWaylandOutput::Mode> getModes() const;
|
||||||
|
@ -78,8 +79,6 @@ private:
|
||||||
|
|
||||||
int gammaRampSize() const override;
|
int gammaRampSize() const override;
|
||||||
bool setGammaRamp(const GammaRamp &gamma) override;
|
bool setGammaRamp(const GammaRamp &gamma) override;
|
||||||
void setOverscan(uint32_t overscan) override;
|
|
||||||
void setRgbRange(RgbRange range) override;
|
|
||||||
|
|
||||||
DrmPipeline *m_pipeline;
|
DrmPipeline *m_pipeline;
|
||||||
DrmConnector *m_connector;
|
DrmConnector *m_connector;
|
||||||
|
|
|
@ -34,16 +34,13 @@
|
||||||
namespace KWin
|
namespace KWin
|
||||||
{
|
{
|
||||||
|
|
||||||
DrmPipeline::DrmPipeline(DrmGpu *gpu, DrmConnector *conn, DrmCrtc *crtc)
|
DrmPipeline::DrmPipeline(DrmConnector *conn)
|
||||||
: m_output(nullptr)
|
: m_output(nullptr)
|
||||||
, m_gpu(gpu)
|
|
||||||
, m_connector(conn)
|
, m_connector(conn)
|
||||||
, m_crtc(crtc)
|
|
||||||
, m_primaryPlane(crtc->primaryPlane())
|
|
||||||
{
|
{
|
||||||
m_allObjects << m_connector << m_crtc;
|
if (!gpu()->atomicModeSetting()) {
|
||||||
if (m_primaryPlane) {
|
m_formats.insert(DRM_FORMAT_XRGB8888, {});
|
||||||
m_allObjects << m_primaryPlane;
|
m_formats.insert(DRM_FORMAT_ARGB8888, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,47 +48,22 @@ DrmPipeline::~DrmPipeline()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmPipeline::setup()
|
|
||||||
{
|
|
||||||
if (m_gpu->atomicModeSetting()) {
|
|
||||||
if (m_connector->getProp(DrmConnector::PropertyIndex::CrtcId)->current() == m_crtc->id()) {
|
|
||||||
m_connector->findCurrentMode(m_crtc->queryCurrentMode());
|
|
||||||
}
|
|
||||||
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, m_crtc->id());
|
|
||||||
m_crtc->setPending(DrmCrtc::PropertyIndex::Active, 1);
|
|
||||||
auto mode = m_connector->currentMode();
|
|
||||||
m_crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, &mode.mode, sizeof(drmModeModeInfo));
|
|
||||||
m_primaryPlane->setPending(DrmPlane::PropertyIndex::CrtcId, m_crtc->id());
|
|
||||||
m_primaryPlane->set(QPoint(0, 0), sourceSize(), QPoint(0, 0), mode.size);
|
|
||||||
m_primaryPlane->setTransformation(DrmPlane::Transformation::Rotate0);
|
|
||||||
m_formats = m_primaryPlane->formats();
|
|
||||||
} else {
|
|
||||||
m_formats.insert(DRM_FORMAT_XRGB8888, {});
|
|
||||||
m_formats.insert(DRM_FORMAT_ARGB8888, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::test()
|
|
||||||
{
|
|
||||||
return checkTestBuffer() && commitPipelines(m_gpu->pipelines(), CommitMode::Test);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(pending.crtc);
|
||||||
m_primaryBuffer = buffer;
|
m_primaryBuffer = buffer;
|
||||||
if (m_gpu->useEglStreams() && m_gpu->eglBackend() != nullptr && m_gpu == m_gpu->platform()->primaryGpu()) {
|
if (gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu()) {
|
||||||
// EglStreamBackend queues normal page flips through EGL,
|
// EglStreamBackend queues normal page flips through EGL,
|
||||||
// modesets etc are performed through DRM-KMS
|
// modesets etc are performed through DRM-KMS
|
||||||
bool needsCommit = std::any_of(m_allObjects.constBegin(), m_allObjects.constEnd(), [](auto obj){return obj->needsCommit();});
|
if (!m_connector->needsCommit() && !pending.crtc->needsCommit()) {
|
||||||
if (!needsCommit) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_gpu->atomicModeSetting()) {
|
if (gpu()->atomicModeSetting()) {
|
||||||
if (!atomicCommit()) {
|
if (!commitPipelines({this}, CommitMode::Commit)) {
|
||||||
// update properties and try again
|
// update properties and try again
|
||||||
updateProperties();
|
updateProperties();
|
||||||
if (!atomicCommit()) {
|
if (!commitPipelines({this}, CommitMode::Commit)) {
|
||||||
qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
|
qCWarning(KWIN_DRM) << "Atomic present failed!" << strerror(errno);
|
||||||
printDebugInfo();
|
printDebugInfo();
|
||||||
return false;
|
return false;
|
||||||
|
@ -106,22 +78,16 @@ bool DrmPipeline::present(const QSharedPointer<DrmBuffer> &buffer)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::atomicCommit()
|
|
||||||
{
|
|
||||||
return commitPipelines({this}, CommitMode::Commit);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode)
|
bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, CommitMode mode)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!pipelines.isEmpty());
|
Q_ASSERT(!pipelines.isEmpty());
|
||||||
|
if (pipelines[0]->gpu()->atomicModeSetting()) {
|
||||||
if (pipelines[0]->m_gpu->atomicModeSetting()) {
|
|
||||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||||
if (!req) {
|
if (!req) {
|
||||||
qCDebug(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
qCDebug(KWIN_DRM) << "Failed to allocate drmModeAtomicReq!" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint32_t flags = 0;
|
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
|
||||||
const auto &failed = [pipelines, req](){
|
const auto &failed = [pipelines, req](){
|
||||||
drmModeAtomicFree(req);
|
drmModeAtomicFree(req);
|
||||||
for (const auto &pipeline : pipelines) {
|
for (const auto &pipeline : pipelines) {
|
||||||
|
@ -130,8 +96,10 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
||||||
pipeline->m_primaryBuffer = pipeline->m_oldTestBuffer;
|
pipeline->m_primaryBuffer = pipeline->m_oldTestBuffer;
|
||||||
pipeline->m_oldTestBuffer = nullptr;
|
pipeline->m_oldTestBuffer = nullptr;
|
||||||
}
|
}
|
||||||
for (const auto &obj : qAsConst(pipeline->m_allObjects)) {
|
pipeline->m_connector->rollbackPending();
|
||||||
obj->rollbackPending();
|
if (pipeline->pending.crtc) {
|
||||||
|
pipeline->pending.crtc->rollbackPending();
|
||||||
|
pipeline->pending.crtc->primaryPlane()->rollbackPending();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -146,131 +114,103 @@ bool DrmPipeline::commitPipelines(const QVector<DrmPipeline*> &pipelines, Commit
|
||||||
return failed();
|
return failed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (drmModeAtomicCommit(pipelines[0]->m_gpu->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, pipelines[0]->output()) != 0) {
|
if (drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, (flags & (~DRM_MODE_PAGE_FLIP_EVENT)) | DRM_MODE_ATOMIC_TEST_ONLY, pipelines[0]->output()) != 0) {
|
||||||
qCWarning(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
|
qCWarning(KWIN_DRM) << "Atomic test for" << mode << "failed!" << strerror(errno);
|
||||||
return failed();
|
return failed();
|
||||||
}
|
}
|
||||||
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->m_gpu->fd(), req, flags, pipelines[0]->output()) != 0) {
|
if (mode != CommitMode::Test && drmModeAtomicCommit(pipelines[0]->gpu()->fd(), req, flags, pipelines[0]->output()) != 0) {
|
||||||
qCWarning(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
|
qCWarning(KWIN_DRM) << "Atomic commit failed! This should never happen!" << strerror(errno);
|
||||||
return failed();
|
return failed();
|
||||||
}
|
}
|
||||||
for (const auto &pipeline : pipelines) {
|
for (const auto &pipeline : pipelines) {
|
||||||
pipeline->m_oldTestBuffer = nullptr;
|
pipeline->m_oldTestBuffer = nullptr;
|
||||||
for (const auto &obj : qAsConst(pipeline->m_allObjects)) {
|
pipeline->m_connector->commitPending();
|
||||||
obj->commitPending();
|
if (pipeline->pending.crtc) {
|
||||||
|
pipeline->pending.crtc->commitPending();
|
||||||
|
pipeline->pending.crtc->primaryPlane()->commitPending();
|
||||||
}
|
}
|
||||||
if (mode != CommitMode::Test) {
|
if (mode != CommitMode::Test) {
|
||||||
pipeline->m_primaryPlane->setNext(pipeline->m_primaryBuffer);
|
pipeline->m_connector->commit();
|
||||||
for (const auto &obj : qAsConst(pipeline->m_allObjects)) {
|
if (pipeline->pending.crtc) {
|
||||||
obj->commit();
|
pipeline->pending.crtc->primaryPlane()->setNext(pipeline->m_primaryBuffer);
|
||||||
|
pipeline->pending.crtc->commit();
|
||||||
|
pipeline->pending.crtc->primaryPlane()->commit();
|
||||||
}
|
}
|
||||||
|
pipeline->m_current = pipeline->pending;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drmModeAtomicFree(req);
|
drmModeAtomicFree(req);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
bool failure = false;
|
||||||
for (const auto &pipeline : pipelines) {
|
for (const auto &pipeline : pipelines) {
|
||||||
if (pipeline->m_legacyNeedsModeset && pipeline->isActive() && !pipeline->modeset(0)) {
|
if (!pipeline->applyPendingChangesLegacy()) {
|
||||||
return false;
|
failure = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
if (failure) {
|
||||||
|
// at least try to revert the config
|
||||||
|
for (const auto &pipeline : pipelines) {
|
||||||
|
pipeline->pending = pipeline->m_next;
|
||||||
|
pipeline->applyPendingChangesLegacy();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
bool DrmPipeline::populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags)
|
||||||
{
|
{
|
||||||
bool usesEglStreams = m_gpu->useEglStreams() && m_gpu->eglBackend() != nullptr && m_gpu == m_gpu->platform()->primaryGpu();
|
if (needsModeset()) {
|
||||||
if (!usesEglStreams && isActive()) {
|
prepareModeset();
|
||||||
|
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||||
|
}
|
||||||
|
bool usesEglStreams = gpu()->useEglStreams() && gpu()->eglBackend() != nullptr && gpu() == gpu()->platform()->primaryGpu();
|
||||||
|
if (!usesEglStreams && activePending()) {
|
||||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||||
}
|
}
|
||||||
bool needsModeset = std::any_of(m_allObjects.constBegin(), m_allObjects.constEnd(), [](auto obj){return obj->needsModeset();});
|
|
||||||
if (needsModeset) {
|
|
||||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
||||||
} else {
|
|
||||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
||||||
}
|
|
||||||
m_lastFlags = flags;
|
m_lastFlags = flags;
|
||||||
|
if (pending.crtc) {
|
||||||
auto modeSize = m_connector->currentMode().size;
|
auto modeSize = m_connector->modes()[pending.modeIndex].size;
|
||||||
m_primaryPlane->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : modeSize, QPoint(0, 0), modeSize);
|
pending.crtc->primaryPlane()->set(QPoint(0, 0), m_primaryBuffer ? m_primaryBuffer->size() : modeSize, QPoint(0, 0), modeSize);
|
||||||
m_primaryPlane->setBuffer(isActive() ? m_primaryBuffer.get() : nullptr);
|
pending.crtc->primaryPlane()->setBuffer(activePending() ? m_primaryBuffer.get() : nullptr);
|
||||||
for (const auto &obj : qAsConst(m_allObjects)) {
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive);
|
||||||
if (!obj->atomicPopulate(req)) {
|
if (pending.gamma != m_current.gamma) {
|
||||||
return false;
|
if (pending.gamma) {
|
||||||
|
pending.crtc->setPendingBlob(DrmCrtc::PropertyIndex::Gamma_LUT, pending.gamma->atomicLut, pending.gamma->size * sizeof(drm_color_lut));
|
||||||
|
} else {
|
||||||
|
pending.crtc->setPendingBlob(DrmCrtc::PropertyIndex::Gamma_LUT, nullptr, 256 * sizeof(drm_color_lut));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return m_connector->atomicPopulate(req) && (!pending.crtc || (pending.crtc->atomicPopulate(req) && pending.crtc->primaryPlane()->atomicPopulate(req)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::presentLegacy()
|
bool DrmPipeline::presentLegacy()
|
||||||
{
|
{
|
||||||
if ((!m_crtc->current() || m_crtc->current()->needsModeChange(m_primaryBuffer.get())) && !modeset(m_connector->currentModeIndex())) {
|
if ((!pending.crtc->current() || pending.crtc->current()->needsModeChange(m_primaryBuffer.get())) && !legacyModeset()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_lastFlags = DRM_MODE_PAGE_FLIP_EVENT;
|
m_lastFlags = DRM_MODE_PAGE_FLIP_EVENT;
|
||||||
m_crtc->setNext(m_primaryBuffer);
|
QVector<DrmPipeline*> *userData = new QVector<DrmPipeline*>();
|
||||||
if (drmModePageFlip(m_gpu->fd(), m_crtc->id(), m_primaryBuffer ? m_primaryBuffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, m_output) != 0) {
|
*userData << this;
|
||||||
|
if (drmModePageFlip(gpu()->fd(), pending.crtc->id(), m_primaryBuffer ? m_primaryBuffer->bufferId() : 0, DRM_MODE_PAGE_FLIP_EVENT, userData) != 0) {
|
||||||
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer;
|
qCWarning(KWIN_DRM) << "Page flip failed:" << strerror(errno) << m_primaryBuffer;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
pending.crtc->setNext(m_primaryBuffer);
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::modeset(int modeIndex)
|
|
||||||
{
|
|
||||||
int oldModeIndex = m_connector->currentModeIndex();
|
|
||||||
m_connector->setModeIndex(modeIndex);
|
|
||||||
auto mode = m_connector->currentMode().mode;
|
|
||||||
if (m_gpu->atomicModeSetting()) {
|
|
||||||
m_crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, &mode, sizeof(drmModeModeInfo));
|
|
||||||
if (m_connector->hasOverscan()) {
|
|
||||||
m_connector->setOverscan(m_connector->overscan(), m_connector->currentMode().size);
|
|
||||||
}
|
|
||||||
bool works = test();
|
|
||||||
// hardware rotation could fail in some modes, try again with soft rotation if possible
|
|
||||||
if (!works
|
|
||||||
&& transformation() != DrmPlane::Transformations(DrmPlane::Transformation::Rotate0)
|
|
||||||
&& setPendingTransformation(DrmPlane::Transformation::Rotate0)) {
|
|
||||||
// values are reset on the failing test, set them again
|
|
||||||
m_crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, &mode, sizeof(drmModeModeInfo));
|
|
||||||
if (m_connector->hasOverscan()) {
|
|
||||||
m_connector->setOverscan(m_connector->overscan(), m_connector->currentMode().size);
|
|
||||||
}
|
|
||||||
works = test();
|
|
||||||
}
|
|
||||||
if (!works) {
|
|
||||||
qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
|
|
||||||
m_connector->setModeIndex(oldModeIndex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
uint32_t connId = m_connector->id();
|
|
||||||
if (!checkTestBuffer() || drmModeSetCrtc(m_gpu->fd(), m_crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, &mode) != 0) {
|
|
||||||
qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
|
|
||||||
m_connector->setModeIndex(oldModeIndex);
|
|
||||||
m_primaryBuffer = m_oldTestBuffer;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_oldTestBuffer = nullptr;
|
|
||||||
m_legacyNeedsModeset = false;
|
|
||||||
// make sure the buffer gets kept alive, or the modeset gets reverted by the kernel
|
|
||||||
if (m_crtc->current()) {
|
|
||||||
m_crtc->setNext(m_primaryBuffer);
|
|
||||||
} else {
|
|
||||||
m_crtc->setCurrent(m_primaryBuffer);
|
|
||||||
}
|
|
||||||
m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setCurrent(DRM_MODE_DPMS_ON);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::checkTestBuffer()
|
bool DrmPipeline::checkTestBuffer()
|
||||||
{
|
{
|
||||||
if (m_primaryBuffer && m_primaryBuffer->size() == sourceSize()) {
|
if (!pending.crtc || (m_primaryBuffer && m_primaryBuffer->size() == sourceSize())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
auto backend = m_gpu->eglBackend();
|
auto backend = gpu()->eglBackend();
|
||||||
QSharedPointer<DrmBuffer> buffer;
|
QSharedPointer<DrmBuffer> buffer;
|
||||||
// try to re-use buffers if possible.
|
// try to re-use buffers if possible.
|
||||||
const auto &checkBuffer = [this, backend, &buffer](const QSharedPointer<DrmBuffer> &buf){
|
const auto &checkBuffer = [this, backend, &buffer](const QSharedPointer<DrmBuffer> &buf){
|
||||||
|
@ -281,29 +221,29 @@ bool DrmPipeline::checkTestBuffer()
|
||||||
buffer = buf;
|
buffer = buf;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (m_primaryPlane && m_primaryPlane->next()) {
|
if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->next()) {
|
||||||
checkBuffer(m_primaryPlane->next());
|
checkBuffer(pending.crtc->primaryPlane()->next());
|
||||||
} else if (m_primaryPlane && m_primaryPlane->current()) {
|
} else if (pending.crtc->primaryPlane() && pending.crtc->primaryPlane()->current()) {
|
||||||
checkBuffer(m_primaryPlane->current());
|
checkBuffer(pending.crtc->primaryPlane()->current());
|
||||||
} else if (m_crtc->next()) {
|
} else if (pending.crtc->next()) {
|
||||||
checkBuffer(m_crtc->next());
|
checkBuffer(pending.crtc->next());
|
||||||
} else if (m_crtc->current()) {
|
} else if (pending.crtc->current()) {
|
||||||
checkBuffer(m_crtc->current());
|
checkBuffer(pending.crtc->current());
|
||||||
}
|
}
|
||||||
// if we don't have a fitting buffer already, get or create one
|
// if we don't have a fitting buffer already, get or create one
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
#if HAVE_GBM
|
#if HAVE_GBM
|
||||||
} else if (backend && m_output) {
|
} else if (backend && m_output) {
|
||||||
buffer = backend->renderTestFrame(m_output);
|
buffer = backend->renderTestFrame(m_output);
|
||||||
} else if (backend && m_gpu->gbmDevice()) {
|
} else if (backend && gpu()->gbmDevice()) {
|
||||||
gbm_bo *bo = gbm_bo_create(m_gpu->gbmDevice(), sourceSize().width(), sourceSize().height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
gbm_bo *bo = gbm_bo_create(gpu()->gbmDevice(), sourceSize().width(), sourceSize().height(), GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
|
||||||
if (!bo) {
|
if (!bo) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buffer = QSharedPointer<DrmGbmBuffer>::create(m_gpu, bo, nullptr);
|
buffer = QSharedPointer<DrmGbmBuffer>::create(gpu(), bo, nullptr);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
buffer = QSharedPointer<DrmDumbBuffer>::create(m_gpu, sourceSize());
|
buffer = QSharedPointer<DrmDumbBuffer>::create(gpu(), sourceSize());
|
||||||
}
|
}
|
||||||
if (buffer && buffer->bufferId()) {
|
if (buffer && buffer->bufferId()) {
|
||||||
m_oldTestBuffer = m_primaryBuffer;
|
m_oldTestBuffer = m_primaryBuffer;
|
||||||
|
@ -315,175 +255,117 @@ bool DrmPipeline::checkTestBuffer()
|
||||||
|
|
||||||
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot)
|
bool DrmPipeline::setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot)
|
||||||
{
|
{
|
||||||
return m_crtc->setLegacyCursor(buffer, hotspot);
|
return pending.crtc->setLegacyCursor(buffer, hotspot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::moveCursor(QPoint pos)
|
bool DrmPipeline::moveCursor(QPoint pos)
|
||||||
{
|
{
|
||||||
return m_crtc->moveLegacyCursor(pos);
|
return pending.crtc->moveLegacyCursor(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::setActive(bool active)
|
void DrmPipeline::prepareModeset()
|
||||||
{
|
{
|
||||||
// disable the cursor before the primary plane to circumvent a crash in amdgpu
|
if (!pending.crtc) {
|
||||||
if (isActive() && !active) {
|
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, 0);
|
||||||
if (drmModeSetCursor(m_gpu->fd(), m_crtc->id(), 0, 0, 0) != 0) {
|
return;
|
||||||
qCWarning(KWIN_DRM) << "Could not set cursor:" << strerror(errno);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bool success = false;
|
auto mode = m_connector->modes()[pending.modeIndex];
|
||||||
auto mode = m_connector->currentMode().mode;
|
|
||||||
if (m_gpu->atomicModeSetting()) {
|
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
|
||||||
m_connector->setPending(DrmConnector::PropertyIndex::CrtcId, active ? m_crtc->id() : 0);
|
if (const auto &prop = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
|
||||||
m_crtc->setPending(DrmCrtc::PropertyIndex::Active, active);
|
prop->setEnum(pending.rgbRange);
|
||||||
m_crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, active ? &mode : nullptr, sizeof(drmModeModeInfo));
|
|
||||||
m_primaryPlane->setPending(DrmPlane::PropertyIndex::CrtcId, active ? m_crtc->id() : 0);
|
|
||||||
if (active) {
|
|
||||||
success = test();
|
|
||||||
if (!success) {
|
|
||||||
updateProperties();
|
|
||||||
success = test();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// immediately commit if disabling as there will be no present
|
|
||||||
success = atomicCommit();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
success = modeset(m_connector->currentModeIndex());
|
|
||||||
if (success) {
|
|
||||||
success = m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setPropertyLegacy(active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!success) {
|
|
||||||
qCWarning(KWIN_DRM) << "Setting active to" << active << "failed" << strerror(errno);
|
pending.crtc->setPending(DrmCrtc::PropertyIndex::Active, activePending());
|
||||||
}
|
pending.crtc->setPendingBlob(DrmCrtc::PropertyIndex::ModeId, activePending() ? &mode.mode : nullptr, sizeof(drmModeModeInfo));
|
||||||
if (isActive()) {
|
|
||||||
// enable cursor (again)
|
pending.crtc->primaryPlane()->setPending(DrmPlane::PropertyIndex::CrtcId, activePending() ? pending.crtc->id() : 0);
|
||||||
m_crtc->setLegacyCursor();
|
pending.crtc->primaryPlane()->setTransformation(DrmPlane::Transformation::Rotate0);
|
||||||
}
|
pending.crtc->primaryPlane()->set(QPoint(0, 0), sourceSize(), QPoint(0, 0), mode.size);
|
||||||
return success;
|
|
||||||
|
m_formats = pending.crtc->primaryPlane()->formats();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::setGammaRamp(const GammaRamp &ramp)
|
void DrmPipeline::applyPendingChanges()
|
||||||
{
|
{
|
||||||
// There are old Intel iGPUs that don't have full support for setting
|
if (!pending.crtc) {
|
||||||
// the gamma ramp with AMS -> fall back to legacy without the property
|
pending.active = false;
|
||||||
if (m_gpu->atomicModeSetting() && m_crtc->getProp(DrmCrtc::PropertyIndex::Gamma_LUT)) {
|
}
|
||||||
struct drm_color_lut *gamma = new drm_color_lut[ramp.size()];
|
m_next = pending;
|
||||||
for (uint32_t i = 0; i < ramp.size(); i++) {
|
}
|
||||||
gamma[i].red = ramp.red()[i];
|
|
||||||
gamma[i].green = ramp.green()[i];
|
bool DrmPipeline::applyPendingChangesLegacy()
|
||||||
gamma[i].blue = ramp.blue()[i];
|
{
|
||||||
}
|
if (!pending.active && pending.crtc) {
|
||||||
bool result = m_crtc->setPendingBlob(DrmCrtc::PropertyIndex::Gamma_LUT, gamma, ramp.size() * sizeof(drm_color_lut));
|
drmModeSetCursor(gpu()->fd(), pending.crtc->id(), 0, 0, 0);
|
||||||
delete[] gamma;
|
}
|
||||||
if (!result) {
|
if (pending.active) {
|
||||||
qCWarning(KWIN_DRM) << "Could not create gamma LUT property blob" << strerror(errno);
|
Q_ASSERT(pending.crtc);
|
||||||
|
if (auto vrr = pending.crtc->getProp(DrmCrtc::PropertyIndex::VrrEnabled); !vrr->setPropertyLegacy(pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive)) {
|
||||||
|
qCWarning(KWIN_DRM) << "Setting vrr failed!" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!test()) {
|
if (const auto &rgbRange = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
|
||||||
|
rgbRange->setEnumLegacy(pending.rgbRange);
|
||||||
|
}
|
||||||
|
if (needsModeset() &&!legacyModeset()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setCurrent(DRM_MODE_DPMS_ON);
|
||||||
|
if (pending.gamma && drmModeCrtcSetGamma(gpu()->fd(), pending.crtc->id(), pending.gamma->size,
|
||||||
|
const_cast<uint16_t*>(pending.gamma->lut.red()),
|
||||||
|
const_cast<uint16_t*>(pending.gamma->lut.blue()),
|
||||||
|
const_cast<uint16_t*>(pending.gamma->lut.green())) != 0) {
|
||||||
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
uint16_t *red = const_cast<uint16_t*>(ramp.red());
|
|
||||||
uint16_t *green = const_cast<uint16_t*>(ramp.green());
|
|
||||||
uint16_t *blue = const_cast<uint16_t*>(ramp.blue());
|
|
||||||
if (drmModeCrtcSetGamma(m_gpu->fd(), m_crtc->id(), ramp.size(), red, green, blue) != 0) {
|
|
||||||
qCWarning(KWIN_DRM) << "setting gamma failed!" << strerror(errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::setTransformation(const DrmPlane::Transformations &transformation)
|
pending.crtc->setLegacyCursor();
|
||||||
{
|
|
||||||
return setPendingTransformation(transformation) && test();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::setPendingTransformation(const DrmPlane::Transformations &transformation)
|
|
||||||
{
|
|
||||||
if (this->transformation() == transformation) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if (!m_gpu->atomicModeSetting()) {
|
if (!m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->setPropertyLegacy(pending.active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF)) {
|
||||||
return false;
|
qCWarning(KWIN_DRM) << "Setting legacy dpms failed!" << strerror(errno);
|
||||||
}
|
|
||||||
if (!m_primaryPlane->setTransformation(transformation)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::setSyncMode(RenderLoopPrivate::SyncMode syncMode)
|
bool DrmPipeline::legacyModeset()
|
||||||
{
|
{
|
||||||
auto vrrProp = m_crtc->getProp(DrmCrtc::PropertyIndex::VrrEnabled);
|
auto mode = m_connector->modes()[pending.modeIndex];
|
||||||
if (!vrrProp || !m_connector->vrrCapable()) {
|
uint32_t connId = m_connector->id();
|
||||||
return syncMode == RenderLoopPrivate::SyncMode::Fixed;
|
if (!checkTestBuffer() || drmModeSetCrtc(gpu()->fd(), pending.crtc->id(), m_primaryBuffer->bufferId(), 0, 0, &connId, 1, &mode.mode) != 0) {
|
||||||
}
|
qCWarning(KWIN_DRM) << "Modeset failed!" << strerror(errno);
|
||||||
bool vrr = syncMode == RenderLoopPrivate::SyncMode::Adaptive;
|
pending = m_next;
|
||||||
if (vrrProp->pending() == vrr) {
|
m_primaryBuffer = m_oldTestBuffer;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (m_gpu->atomicModeSetting()) {
|
|
||||||
vrrProp->setPending(vrr);
|
|
||||||
return test();
|
|
||||||
} else {
|
|
||||||
return vrrProp->setPropertyLegacy(vrr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::setOverscan(uint32_t overscan)
|
|
||||||
{
|
|
||||||
if (overscan > 100 || (overscan != 0 && !m_connector->hasOverscan())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_connector->setOverscan(overscan, m_connector->currentMode().size);
|
m_oldTestBuffer = nullptr;
|
||||||
return test();
|
// make sure the buffer gets kept alive, or the modeset gets reverted by the kernel
|
||||||
}
|
if (pending.crtc->current()) {
|
||||||
|
pending.crtc->setNext(m_primaryBuffer);
|
||||||
bool DrmPipeline::setRgbRange(AbstractWaylandOutput::RgbRange rgbRange)
|
|
||||||
{
|
|
||||||
if (const auto &prop = m_connector->getProp(DrmConnector::PropertyIndex::Broadcast_RGB)) {
|
|
||||||
prop->setEnum(rgbRange);
|
|
||||||
return test();
|
|
||||||
} else {
|
} else {
|
||||||
return false;
|
pending.crtc->setCurrent(m_primaryBuffer);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize DrmPipeline::sourceSize() const
|
QSize DrmPipeline::sourceSize() const
|
||||||
{
|
{
|
||||||
auto mode = m_connector->currentMode();
|
auto mode = m_connector->modes()[pending.modeIndex];
|
||||||
if (transformation() & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) {
|
if (pending.transformation & (DrmPlane::Transformation::Rotate90 | DrmPlane::Transformation::Rotate270)) {
|
||||||
return mode.size.transposed();
|
return mode.size.transposed();
|
||||||
}
|
}
|
||||||
return mode.size;
|
return mode.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmPlane::Transformations DrmPipeline::transformation() const
|
|
||||||
{
|
|
||||||
return m_primaryPlane ? m_primaryPlane->transformation() : DrmPlane::Transformation::Rotate0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::isActive() const
|
|
||||||
{
|
|
||||||
if (m_gpu->atomicModeSetting()) {
|
|
||||||
return m_crtc->getProp(DrmCrtc::PropertyIndex::Active)->pending() != 0;
|
|
||||||
} else {
|
|
||||||
return m_connector->getProp(DrmConnector::PropertyIndex::Dpms)->current() == DRM_MODE_DPMS_ON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DrmPipeline::isCursorVisible() const
|
bool DrmPipeline::isCursorVisible() const
|
||||||
{
|
{
|
||||||
return m_crtc->isCursorVisible(QRect(QPoint(0, 0), m_connector->currentMode().size));
|
return pending.crtc && pending.crtc->isCursorVisible(QRect(QPoint(0, 0), m_connector->currentMode().size));
|
||||||
}
|
}
|
||||||
|
|
||||||
QPoint DrmPipeline::cursorPos() const
|
QPoint DrmPipeline::cursorPos() const
|
||||||
{
|
{
|
||||||
return m_crtc->cursorPos();
|
return pending.crtc->cursorPos();
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmConnector *DrmPipeline::connector() const
|
DrmConnector *DrmPipeline::connector() const
|
||||||
|
@ -491,21 +373,16 @@ DrmConnector *DrmPipeline::connector() const
|
||||||
return m_connector;
|
return m_connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrmCrtc *DrmPipeline::crtc() const
|
DrmGpu *DrmPipeline::gpu() const
|
||||||
{
|
{
|
||||||
return m_crtc;
|
return m_connector->gpu();
|
||||||
}
|
|
||||||
|
|
||||||
DrmPlane *DrmPipeline::primaryPlane() const
|
|
||||||
{
|
|
||||||
return m_primaryPlane;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmPipeline::pageFlipped()
|
void DrmPipeline::pageFlipped()
|
||||||
{
|
{
|
||||||
m_crtc->flipBuffer();
|
m_current.crtc->flipBuffer();
|
||||||
if (m_primaryPlane) {
|
if (m_current.crtc->primaryPlane()) {
|
||||||
m_primaryPlane->flipBuffer();
|
m_current.crtc->primaryPlane()->flipBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,12 +398,16 @@ DrmOutput *DrmPipeline::output() const
|
||||||
|
|
||||||
void DrmPipeline::updateProperties()
|
void DrmPipeline::updateProperties()
|
||||||
{
|
{
|
||||||
for (const auto &obj : qAsConst(m_allObjects)) {
|
m_connector->updateProperties();
|
||||||
obj->updateProperties();
|
if (pending.crtc) {
|
||||||
|
pending.crtc->updateProperties();
|
||||||
|
if (pending.crtc->primaryPlane()) {
|
||||||
|
pending.crtc->primaryPlane()->updateProperties();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// with legacy we don't know what happened to the cursor after VT switch
|
// with legacy we don't know what happened to the cursor after VT switch
|
||||||
// so make sure it gets set again
|
// so make sure it gets set again
|
||||||
m_crtc->setLegacyCursor();
|
pending.crtc->setLegacyCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
|
bool DrmPipeline::isFormatSupported(uint32_t drmFormat) const
|
||||||
|
@ -539,6 +420,44 @@ QVector<uint64_t> DrmPipeline::supportedModifiers(uint32_t drmFormat) const
|
||||||
return m_formats[drmFormat];
|
return m_formats[drmFormat];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DrmPipeline::needsModeset() const
|
||||||
|
{
|
||||||
|
return pending.crtc != m_current.crtc
|
||||||
|
|| pending.active != m_current.active
|
||||||
|
|| pending.modeIndex != m_current.modeIndex
|
||||||
|
|| pending.rgbRange != m_current.rgbRange
|
||||||
|
|| pending.transformation != m_current.transformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DrmPipeline::activePending() const
|
||||||
|
{
|
||||||
|
return pending.crtc && pending.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrmPipeline::revertPendingChanges()
|
||||||
|
{
|
||||||
|
pending = m_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrmGammaRamp::DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut)
|
||||||
|
: lut(lut)
|
||||||
|
, size(lut.size())
|
||||||
|
{
|
||||||
|
if (gpu->atomicModeSetting()) {
|
||||||
|
atomicLut = new drm_color_lut[size];
|
||||||
|
for (uint32_t i = 0; i < size; i++) {
|
||||||
|
atomicLut[i].red = lut.red()[i];
|
||||||
|
atomicLut[i].green = lut.green()[i];
|
||||||
|
atomicLut[i].blue = lut.blue()[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrmGammaRamp::~DrmGammaRamp()
|
||||||
|
{
|
||||||
|
delete[] atomicLut;
|
||||||
|
}
|
||||||
|
|
||||||
static void printProps(DrmObject *object)
|
static void printProps(DrmObject *object)
|
||||||
{
|
{
|
||||||
auto list = object->properties();
|
auto list = object->properties();
|
||||||
|
@ -574,11 +493,13 @@ void DrmPipeline::printDebugInfo() const
|
||||||
qCWarning(KWIN_DRM) << "Drm objects:";
|
qCWarning(KWIN_DRM) << "Drm objects:";
|
||||||
qCWarning(KWIN_DRM) << "connector" << m_connector->id();
|
qCWarning(KWIN_DRM) << "connector" << m_connector->id();
|
||||||
printProps(m_connector);
|
printProps(m_connector);
|
||||||
qCWarning(KWIN_DRM) << "crtc" << m_crtc->id();
|
if (pending.crtc) {
|
||||||
printProps(m_crtc);
|
qCWarning(KWIN_DRM) << "crtc" << pending.crtc->id();
|
||||||
if (m_primaryPlane) {
|
printProps(pending.crtc);
|
||||||
qCWarning(KWIN_DRM) << "primary plane" << m_primaryPlane->id();
|
if (pending.crtc->primaryPlane()) {
|
||||||
printProps(m_primaryPlane);
|
qCWarning(KWIN_DRM) << "primary plane" << pending.crtc->primaryPlane()->id();
|
||||||
|
printProps(pending.crtc->primaryPlane());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,40 +30,42 @@ class DrmBuffer;
|
||||||
class DrmDumbBuffer;
|
class DrmDumbBuffer;
|
||||||
class GammaRamp;
|
class GammaRamp;
|
||||||
|
|
||||||
|
class DrmGammaRamp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DrmGammaRamp(DrmGpu *gpu, const GammaRamp &lut);
|
||||||
|
~DrmGammaRamp();
|
||||||
|
|
||||||
|
const GammaRamp lut;
|
||||||
|
drm_color_lut *atomicLut = nullptr;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
class DrmPipeline
|
class DrmPipeline
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DrmPipeline(DrmGpu *gpu, DrmConnector *conn, DrmCrtc *crtc);
|
DrmPipeline(DrmConnector *conn);
|
||||||
~DrmPipeline();
|
~DrmPipeline();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the necessary initial drm properties for the pipeline to work
|
|
||||||
*/
|
|
||||||
void setup();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tests the pending commit first and commits it if the test passes
|
* tests the pending commit first and commits it if the test passes
|
||||||
* if the test fails, there is a guarantee for no lasting changes
|
* if the test fails, there is a guarantee for no lasting changes
|
||||||
*/
|
*/
|
||||||
bool present(const QSharedPointer<DrmBuffer> &buffer);
|
bool present(const QSharedPointer<DrmBuffer> &buffer);
|
||||||
|
|
||||||
bool modeset(int modeIndex);
|
bool needsModeset() const;
|
||||||
bool setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot = QPoint());
|
void prepareModeset();
|
||||||
bool setActive(bool enable);
|
void applyPendingChanges();
|
||||||
bool setGammaRamp(const GammaRamp &ramp);
|
void revertPendingChanges();
|
||||||
bool setTransformation(const DrmPlane::Transformations &transform);
|
|
||||||
bool moveCursor(QPoint pos);
|
bool setCursor(const QSharedPointer<DrmDumbBuffer> &buffer, const QPoint &hotspot = QPoint());
|
||||||
bool setSyncMode(RenderLoopPrivate::SyncMode syncMode);
|
bool moveCursor(QPoint pos);
|
||||||
bool setOverscan(uint32_t overscan);
|
|
||||||
bool setRgbRange(AbstractWaylandOutput::RgbRange rgbRange);
|
|
||||||
|
|
||||||
DrmPlane::Transformations transformation() const;
|
|
||||||
bool isCursorVisible() const;
|
bool isCursorVisible() const;
|
||||||
QPoint cursorPos() const;
|
QPoint cursorPos() const;
|
||||||
|
|
||||||
DrmConnector *connector() const;
|
DrmConnector *connector() const;
|
||||||
DrmCrtc *crtc() const;
|
DrmGpu *gpu() const;
|
||||||
DrmPlane *primaryPlane() const;
|
|
||||||
|
|
||||||
void pageFlipped();
|
void pageFlipped();
|
||||||
void printDebugInfo() const;
|
void printDebugInfo() const;
|
||||||
|
@ -76,6 +78,18 @@ public:
|
||||||
void setOutput(DrmOutput *output);
|
void setOutput(DrmOutput *output);
|
||||||
DrmOutput *output() const;
|
DrmOutput *output() const;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
DrmCrtc *crtc = nullptr;
|
||||||
|
bool active = true;
|
||||||
|
int modeIndex = 0;
|
||||||
|
uint32_t overscan = 0;
|
||||||
|
AbstractWaylandOutput::RgbRange rgbRange = AbstractWaylandOutput::RgbRange::Automatic;
|
||||||
|
RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed;
|
||||||
|
QSharedPointer<DrmGammaRamp> gamma;
|
||||||
|
DrmPlane::Transformations transformation = DrmPlane::Transformation::Rotate0;
|
||||||
|
};
|
||||||
|
State pending;
|
||||||
|
|
||||||
enum class CommitMode {
|
enum class CommitMode {
|
||||||
Test,
|
Test,
|
||||||
Commit
|
Commit
|
||||||
|
@ -85,29 +99,26 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);
|
bool populateAtomicValues(drmModeAtomicReq *req, uint32_t &flags);
|
||||||
bool test();
|
|
||||||
bool atomicCommit();
|
|
||||||
bool presentLegacy();
|
bool presentLegacy();
|
||||||
bool checkTestBuffer();
|
bool checkTestBuffer();
|
||||||
bool isActive() const;
|
bool activePending() const;
|
||||||
|
|
||||||
bool setPendingTransformation(const DrmPlane::Transformations &transformation);
|
bool applyPendingChangesLegacy();
|
||||||
|
bool legacyModeset();
|
||||||
|
|
||||||
DrmOutput *m_output = nullptr;
|
DrmOutput *m_output = nullptr;
|
||||||
DrmGpu *m_gpu = nullptr;
|
|
||||||
DrmConnector *m_connector = nullptr;
|
DrmConnector *m_connector = nullptr;
|
||||||
DrmCrtc *m_crtc = nullptr;
|
|
||||||
|
|
||||||
DrmPlane *m_primaryPlane = nullptr;
|
|
||||||
QSharedPointer<DrmBuffer> m_primaryBuffer;
|
QSharedPointer<DrmBuffer> m_primaryBuffer;
|
||||||
QSharedPointer<DrmBuffer> m_oldTestBuffer;
|
QSharedPointer<DrmBuffer> m_oldTestBuffer;
|
||||||
|
|
||||||
bool m_legacyNeedsModeset = true;
|
|
||||||
|
|
||||||
QVector<DrmObject*> m_allObjects;
|
|
||||||
QMap<uint32_t, QVector<uint64_t>> m_formats;
|
QMap<uint32_t, QVector<uint64_t>> m_formats;
|
||||||
|
|
||||||
int m_lastFlags = 0;
|
int m_lastFlags = 0;
|
||||||
|
|
||||||
|
// the state that will be applied at the next real atomic commit
|
||||||
|
State m_next;
|
||||||
|
// the state that is already committed
|
||||||
|
State m_current;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,13 @@ public:
|
||||||
bool needsCommit() const;
|
bool needsCommit() const;
|
||||||
|
|
||||||
bool setPropertyLegacy(uint64_t value);
|
bool setPropertyLegacy(uint64_t value);
|
||||||
|
template<typename T>
|
||||||
|
bool setEnumLegacy(T value) {
|
||||||
|
if (hasEnum(static_cast<uint64_t>(value))) {
|
||||||
|
return setPropertyLegacy(m_enumMap[static_cast<uint32_t>(value)]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_propId = 0;
|
uint32_t m_propId = 0;
|
||||||
|
|
|
@ -65,17 +65,6 @@ void DrmVirtualOutput::vblank(std::chrono::nanoseconds timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrmVirtualOutput::applyMode(int modeIndex)
|
|
||||||
{
|
|
||||||
Q_UNUSED(modeIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrmVirtualOutput::updateMode(const QSize &size, uint32_t refreshRate)
|
|
||||||
{
|
|
||||||
Q_UNUSED(size)
|
|
||||||
Q_UNUSED(refreshRate)
|
|
||||||
}
|
|
||||||
|
|
||||||
void DrmVirtualOutput::setDpmsMode(DpmsMode mode)
|
void DrmVirtualOutput::setDpmsMode(DpmsMode mode)
|
||||||
{
|
{
|
||||||
setDpmsModeInternal(mode);
|
setDpmsModeInternal(mode);
|
||||||
|
|
|
@ -47,8 +47,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void vblank(std::chrono::nanoseconds timestamp);
|
void vblank(std::chrono::nanoseconds timestamp);
|
||||||
void applyMode(int modeIndex) override;
|
|
||||||
void updateMode(const QSize &size, uint32_t refreshRate) override;
|
|
||||||
void setDpmsMode(DpmsMode mode) override;
|
void setDpmsMode(DpmsMode mode) override;
|
||||||
void updateEnablement(bool enable) override;
|
void updateEnablement(bool enable) override;
|
||||||
|
|
||||||
|
|
|
@ -307,7 +307,7 @@ bool EglStreamBackend::resetOutput(Output &o)
|
||||||
if (isPrimary()) {
|
if (isPrimary()) {
|
||||||
// dumb buffer used for modesetting
|
// dumb buffer used for modesetting
|
||||||
o.buffer = QSharedPointer<DrmDumbBuffer>::create(m_gpu, sourceSize);
|
o.buffer = QSharedPointer<DrmDumbBuffer>::create(m_gpu, sourceSize);
|
||||||
o.targetPlane = drmOutput->pipeline()->primaryPlane();
|
o.targetPlane = drmOutput->pipeline()->pending.crtc->primaryPlane();
|
||||||
|
|
||||||
EGLAttrib streamAttribs[] = {
|
EGLAttrib streamAttribs[] = {
|
||||||
EGL_STREAM_FIFO_LENGTH_KHR, 0, // mailbox mode
|
EGL_STREAM_FIFO_LENGTH_KHR, 0, // mailbox mode
|
||||||
|
@ -476,7 +476,7 @@ SurfaceTexture *EglStreamBackend::createSurfaceTextureWayland(SurfacePixmapWayla
|
||||||
|
|
||||||
bool EglStreamBackend::needsReset(const Output &o) const
|
bool EglStreamBackend::needsReset(const Output &o) const
|
||||||
{
|
{
|
||||||
if (o.targetPlane != o.output->pipeline()->primaryPlane()) {
|
if (o.targetPlane != o.output->pipeline()->pending.crtc->primaryPlane()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
QSize surfaceSize = o.dumbSwapchain ? o.dumbSwapchain->size() : o.buffer->size();
|
QSize surfaceSize = o.dumbSwapchain ? o.dumbSwapchain->size() : o.buffer->size();
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "screenedge.h"
|
#include "screenedge.h"
|
||||||
#include "touch_input.h"
|
#include "touch_input.h"
|
||||||
#include "wayland_server.h"
|
#include "wayland_server.h"
|
||||||
|
#include "waylandoutputconfig.h"
|
||||||
|
|
||||||
#include <KWaylandServer/outputconfiguration_v2_interface.h>
|
#include <KWaylandServer/outputconfiguration_v2_interface.h>
|
||||||
#include <KWaylandServer/outputchangeset_v2.h>
|
#include <KWaylandServer/outputchangeset_v2.h>
|
||||||
|
@ -120,55 +121,61 @@ void Platform::requestOutputsChange(KWaylandServer::OutputConfigurationV2Interfa
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WaylandOutputConfig cfg;
|
||||||
const auto changes = config->changes();
|
const auto changes = config->changes();
|
||||||
|
|
||||||
//process all non-disabling changes
|
|
||||||
for (auto it = changes.begin(); it != changes.end(); it++) {
|
for (auto it = changes.begin(); it != changes.end(); it++) {
|
||||||
const KWaylandServer::OutputChangeSetV2 *changeset = it.value();
|
const KWaylandServer::OutputChangeSetV2 *changeset = it.value();
|
||||||
|
auto output = qobject_cast<AbstractWaylandOutput*>(findOutput(it.key()->uuid()));
|
||||||
AbstractOutput* output = findOutput(it.key()->uuid());
|
|
||||||
if (!output) {
|
if (!output) {
|
||||||
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
|
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto props = cfg.changeSet(output);
|
||||||
|
props->enabled = changeset->enabled();
|
||||||
|
props->pos = changeset->position();
|
||||||
|
props->scale = changeset->scale();
|
||||||
|
props->modeSize = changeset->size();
|
||||||
|
props->refreshRate = changeset->refreshRate();
|
||||||
|
props->transform = static_cast<AbstractWaylandOutput::Transform>(changeset->transform());
|
||||||
|
props->overscan = changeset->overscan();
|
||||||
|
props->rgbRange = static_cast<AbstractWaylandOutput::RgbRange>(changeset->rgbRange());
|
||||||
|
props->vrrPolicy = static_cast<RenderLoop::VrrPolicy>(changeset->vrrPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
qDebug(KWIN_CORE) << "Platform::requestOutputsChange enabling" << changeset << it.key()->uuid() << changeset->enabledChanged() << changeset->enabled();
|
const auto outputs = enabledOutputs();
|
||||||
|
bool allDisabled = !std::any_of(outputs.begin(), outputs.end(), [&cfg](const auto &output){
|
||||||
if (changeset->enabledChanged() &&
|
auto o = qobject_cast<AbstractWaylandOutput*>(output);
|
||||||
changeset->enabled()) {
|
if (!o) {
|
||||||
output->setEnabled(true);
|
qCWarning(KWIN_CORE) << "Platform::requestOutputsChange should only be called for Wayland platforms!";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return cfg.changeSet(o)->enabled;
|
||||||
output->applyChanges(changeset);
|
});
|
||||||
|
if (allDisabled) {
|
||||||
|
qCWarning(KWIN_CORE) << "Disabling all outputs through configuration changes is not allowed";
|
||||||
|
config->setFailed();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//process any disable requests
|
if (applyOutputChanges(cfg)) {
|
||||||
for (auto it = changes.begin(); it != changes.end(); it++) {
|
if (config->primaryChanged()) {
|
||||||
const KWaylandServer::OutputChangeSetV2 *changeset = it.value();
|
setPrimaryOutput(findOutput(config->primary()->uuid()));
|
||||||
|
|
||||||
if (changeset->enabledChanged() && !changeset->enabled()) {
|
|
||||||
if (enabledOutputs().count() == 1) {
|
|
||||||
// TODO: check beforehand this condition and set failed otherwise
|
|
||||||
// TODO: instead create a dummy output?
|
|
||||||
qCWarning(KWIN_CORE) << "Not disabling final screen" << it.key()->uuid();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto output = findOutput(it.key()->uuid());
|
|
||||||
if (!output) {
|
|
||||||
qCWarning(KWIN_CORE) << "Could NOT find output matching " << it.key()->uuid();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
qDebug(KWIN_CORE) << "Platform::requestOutputsChange disabling false" << it.key()->uuid();
|
|
||||||
output->setEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
Q_EMIT screens()->changed();
|
||||||
|
config->setApplied();
|
||||||
|
} else {
|
||||||
|
qCDebug(KWIN_CORE) << "Applying config failed";
|
||||||
|
config->setFailed();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config->primaryChanged()) {
|
bool Platform::applyOutputChanges(const WaylandOutputConfig &config)
|
||||||
setPrimaryOutput(findOutput(config->primary()->uuid()));
|
{
|
||||||
|
const auto outputs = enabledOutputs();
|
||||||
|
for (const auto &output : outputs) {
|
||||||
|
static_cast<AbstractWaylandOutput*>(output)->applyChanges(config);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
Q_EMIT screens()->changed();
|
|
||||||
config->setApplied();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractOutput *Platform::findOutput(int screenId) const
|
AbstractOutput *Platform::findOutput(int screenId) const
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Scene;
|
||||||
class ScreenEdges;
|
class ScreenEdges;
|
||||||
class Session;
|
class Session;
|
||||||
class Toplevel;
|
class Toplevel;
|
||||||
|
class WaylandOutputConfig;
|
||||||
|
|
||||||
class KWIN_EXPORT Outputs : public QVector<AbstractOutput*>
|
class KWIN_EXPORT Outputs : public QVector<AbstractOutput*>
|
||||||
{
|
{
|
||||||
|
@ -521,6 +522,11 @@ protected:
|
||||||
virtual void doShowCursor();
|
virtual void doShowCursor();
|
||||||
virtual void doSetSoftwareCursor();
|
virtual void doSetSoftwareCursor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the output changes. Default implementation only sets values common between platforms
|
||||||
|
*/
|
||||||
|
virtual bool applyOutputChanges(const WaylandOutputConfig &config);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void triggerCursorRepaint();
|
void triggerCursorRepaint();
|
||||||
bool m_softwareCursor = false;
|
bool m_softwareCursor = false;
|
||||||
|
|
39
src/waylandoutputconfig.cpp
Normal file
39
src/waylandoutputconfig.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
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 "waylandoutputconfig.h"
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
|
||||||
|
QSharedPointer<OutputChangeSet> WaylandOutputConfig::changeSet(AbstractWaylandOutput *output)
|
||||||
|
{
|
||||||
|
const auto ptr = constChangeSet(output);
|
||||||
|
m_properties[output] = ptr;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<OutputChangeSet> WaylandOutputConfig::constChangeSet(AbstractWaylandOutput *output) const
|
||||||
|
{
|
||||||
|
if (!m_properties.contains(output)) {
|
||||||
|
auto props = QSharedPointer<OutputChangeSet>::create();
|
||||||
|
props->enabled = output->isEnabled();
|
||||||
|
props->pos = output->geometry().topLeft();
|
||||||
|
props->scale = output->scale();
|
||||||
|
props->modeSize = output->modeSize();
|
||||||
|
props->refreshRate = output->refreshRate();
|
||||||
|
props->transform = output->transform();
|
||||||
|
props->overscan = output->overscan();
|
||||||
|
props->rgbRange = output->rgbRange();
|
||||||
|
props->vrrPolicy = output->vrrPolicy();
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
return m_properties[output];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
src/waylandoutputconfig.h
Normal file
44
src/waylandoutputconfig.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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 <QPoint>
|
||||||
|
#include <QSize>
|
||||||
|
|
||||||
|
#include "abstract_wayland_output.h"
|
||||||
|
#include "kwin_export.h"
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
|
||||||
|
class KWIN_EXPORT OutputChangeSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool enabled;
|
||||||
|
QPoint pos;
|
||||||
|
float scale;
|
||||||
|
QSize modeSize;
|
||||||
|
uint32_t refreshRate;
|
||||||
|
AbstractWaylandOutput::Transform transform;
|
||||||
|
uint32_t overscan;
|
||||||
|
AbstractWaylandOutput::RgbRange rgbRange;
|
||||||
|
RenderLoop::VrrPolicy vrrPolicy;
|
||||||
|
};
|
||||||
|
|
||||||
|
class KWIN_EXPORT WaylandOutputConfig
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QSharedPointer<OutputChangeSet> changeSet(AbstractWaylandOutput *output);
|
||||||
|
QSharedPointer<OutputChangeSet> constChangeSet(AbstractWaylandOutput *output) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<AbstractWaylandOutput*, QSharedPointer<OutputChangeSet>> m_properties;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue