backends/drm: implement support for post blending color pipelines
This exposes the degamma->ctm->gamma pipeline as a drm color op, which can be set to a generic color pipeline. The same code can later be adapted to program the upcoming per-plane color pipeline properties.
This commit is contained in:
parent
5aaab715b0
commit
2799c270b4
12 changed files with 406 additions and 145 deletions
|
@ -4,6 +4,7 @@ set(mockDRM_SRCS
|
|||
../../src/backends/drm/drm_backend.cpp
|
||||
../../src/backends/drm/drm_blob.cpp
|
||||
../../src/backends/drm/drm_buffer.cpp
|
||||
../../src/backends/drm/drm_colorop.cpp
|
||||
../../src/backends/drm/drm_commit.cpp
|
||||
../../src/backends/drm/drm_commit_thread.cpp
|
||||
../../src/backends/drm/drm_connector.cpp
|
||||
|
|
|
@ -3,6 +3,7 @@ target_sources(kwin PRIVATE
|
|||
drm_backend.cpp
|
||||
drm_blob.cpp
|
||||
drm_buffer.cpp
|
||||
drm_colorop.cpp
|
||||
drm_commit.cpp
|
||||
drm_commit_thread.cpp
|
||||
drm_connector.cpp
|
||||
|
|
252
src/backends/drm/drm_colorop.cpp
Normal file
252
src/backends/drm/drm_colorop.cpp
Normal file
|
@ -0,0 +1,252 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "drm_colorop.h"
|
||||
#include "drm_blob.h"
|
||||
#include "drm_commit.h"
|
||||
#include "drm_object.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DrmAbstractColorOp::DrmAbstractColorOp(DrmAbstractColorOp *next)
|
||||
: m_next(next)
|
||||
{
|
||||
}
|
||||
|
||||
DrmAbstractColorOp::~DrmAbstractColorOp()
|
||||
{
|
||||
}
|
||||
|
||||
DrmAbstractColorOp *DrmAbstractColorOp::next() const
|
||||
{
|
||||
return m_next;
|
||||
}
|
||||
|
||||
bool DrmAbstractColorOp::matchPipeline(DrmAtomicCommit *commit, const ColorPipeline &pipeline)
|
||||
{
|
||||
if (m_cachedPipeline && *m_cachedPipeline == pipeline) {
|
||||
commit->merge(m_cache.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
DrmAbstractColorOp *currentOp = this;
|
||||
const auto needsLimitedRange = [](const ColorOp &op) {
|
||||
// KMS LUTs have an input and output range of [0, 1]
|
||||
return std::holds_alternative<ColorTransferFunction>(op.operation)
|
||||
|| std::holds_alternative<InverseColorTransferFunction>(op.operation);
|
||||
};
|
||||
|
||||
// first, only check if the pipeline can be programmed in the first place
|
||||
// don't calculate LUTs just yet
|
||||
std::optional<ColorOp> initialOp;
|
||||
double valueScaling = 1;
|
||||
if (!pipeline.ops.empty() && needsLimitedRange(pipeline.ops.front()) && pipeline.ops.front().input.max > 1) {
|
||||
valueScaling = 1.0 / pipeline.ops.front().input.max;
|
||||
initialOp = ColorOp{
|
||||
.input = pipeline.ops.front().input,
|
||||
.operation = ColorMultiplier{valueScaling},
|
||||
.output = ValueRange{
|
||||
.min = pipeline.ops.front().input.min * valueScaling,
|
||||
.max = 1.0,
|
||||
},
|
||||
};
|
||||
while (currentOp && !currentOp->canBeUsedFor(*initialOp)) {
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
if (!currentOp) {
|
||||
return false;
|
||||
}
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
for (auto it = pipeline.ops.begin(); it != pipeline.ops.end(); it++) {
|
||||
while (currentOp && !currentOp->canBeUsedFor(*it)) {
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
if (!currentOp) {
|
||||
return false;
|
||||
}
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
|
||||
// now actually program the properties
|
||||
currentOp = this;
|
||||
m_cache = std::make_unique<DrmAtomicCommit>(commit->gpu());
|
||||
if (initialOp) {
|
||||
while (!currentOp->canBeUsedFor(*initialOp)) {
|
||||
currentOp->bypass(m_cache.get());
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
currentOp->program(m_cache.get(), *initialOp, 1, 1);
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
for (auto it = pipeline.ops.begin(); it != pipeline.ops.end(); it++) {
|
||||
while (!currentOp->canBeUsedFor(*it)) {
|
||||
currentOp->bypass(m_cache.get());
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
if (it == pipeline.ops.end() - 1) {
|
||||
// this is the last op, we need to un-do the factor
|
||||
// this assumes that the output is always limited range
|
||||
currentOp->program(m_cache.get(), *it, valueScaling, 1.0);
|
||||
valueScaling = 1.0;
|
||||
} else if (needsLimitedRange(*it) || needsLimitedRange(*(it + 1))) {
|
||||
// this op can only output limited range or the next op needs a limited range input,
|
||||
// adjust the factor to make it happen
|
||||
currentOp->program(m_cache.get(), *it, valueScaling, 1.0 / it->output.max);
|
||||
valueScaling = 1.0 / it->output.max;
|
||||
} else {
|
||||
// this and the next op are both fine with extended range, set the factor to 1.0 to use all the resolution we can get
|
||||
currentOp->program(m_cache.get(), *it, valueScaling, 1.0);
|
||||
valueScaling = 1.0;
|
||||
}
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
while (currentOp) {
|
||||
currentOp->bypass(m_cache.get());
|
||||
currentOp = currentOp->next();
|
||||
}
|
||||
commit->merge(m_cache.get());
|
||||
m_cachedPipeline = pipeline;
|
||||
return true;
|
||||
}
|
||||
|
||||
LegacyLutColorOp::LegacyLutColorOp(DrmAbstractColorOp *next, DrmProperty *prop, uint32_t maxSize)
|
||||
: DrmAbstractColorOp(next)
|
||||
, m_prop(prop)
|
||||
, m_maxSize(maxSize)
|
||||
, m_components(m_maxSize)
|
||||
{
|
||||
}
|
||||
|
||||
bool LegacyLutColorOp::canBeUsedFor(const ColorOp &op)
|
||||
{
|
||||
if (std::holds_alternative<ColorTransferFunction>(op.operation) || std::holds_alternative<InverseColorTransferFunction>(op.operation)) {
|
||||
// the required resolution depends heavily on the function and on the input and output ranges / multipliers
|
||||
// but this is good enough for now
|
||||
return m_maxSize >= 1024;
|
||||
} else if (std::holds_alternative<ColorMultiplier>(op.operation)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LegacyLutColorOp::program(DrmAtomicCommit *commit, const ColorOp &op, double inputScale, double outputScale)
|
||||
{
|
||||
if (auto tf = std::get_if<ColorTransferFunction>(&op.operation)) {
|
||||
for (uint32_t i = 0; i < m_maxSize; i++) {
|
||||
const double nits = tf->tf.encodedToNits(i / double(m_maxSize - 1) / inputScale, tf->referenceLuminance);
|
||||
const uint16_t output = std::round(std::clamp(nits * outputScale, 0.0, 1.0) * std::numeric_limits<uint16_t>::max());
|
||||
m_components[i] = {
|
||||
.red = output,
|
||||
.green = output,
|
||||
.blue = output,
|
||||
.reserved = 0,
|
||||
};
|
||||
}
|
||||
commit->addBlob(*m_prop, DrmBlob::create(m_prop->drmObject()->gpu(), m_components.data(), sizeof(drm_color_lut) * m_components.size()));
|
||||
} else if (auto tf = std::get_if<InverseColorTransferFunction>(&op.operation)) {
|
||||
for (uint32_t i = 0; i < m_maxSize; i++) {
|
||||
const double nits = tf->tf.nitsToEncoded(i / double(m_maxSize - 1) / inputScale, tf->referenceLuminance);
|
||||
const uint16_t output = std::round(std::clamp(nits * outputScale, 0.0, 1.0) * std::numeric_limits<uint16_t>::max());
|
||||
m_components[i] = {
|
||||
.red = output,
|
||||
.green = output,
|
||||
.blue = output,
|
||||
.reserved = 0,
|
||||
};
|
||||
}
|
||||
commit->addBlob(*m_prop, DrmBlob::create(m_prop->drmObject()->gpu(), m_components.data(), sizeof(drm_color_lut) * m_components.size()));
|
||||
} else if (auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
for (uint32_t i = 0; i < m_maxSize; i++) {
|
||||
m_components[i] = {
|
||||
.red = uint16_t(std::round(std::clamp(mult->factors.x() * outputScale / inputScale * i / double(m_maxSize - 1), 0.0, 1.0) * std::numeric_limits<uint16_t>::max())),
|
||||
.green = uint16_t(std::round(std::clamp(mult->factors.y() * outputScale / inputScale * i / double(m_maxSize - 1), 0.0, 1.0) * std::numeric_limits<uint16_t>::max())),
|
||||
.blue = uint16_t(std::round(std::clamp(mult->factors.z() * outputScale / inputScale * i / double(m_maxSize - 1), 0.0, 1.0) * std::numeric_limits<uint16_t>::max())),
|
||||
.reserved = 0,
|
||||
};
|
||||
}
|
||||
commit->addBlob(*m_prop, DrmBlob::create(m_prop->drmObject()->gpu(), m_components.data(), sizeof(drm_color_lut) * m_maxSize));
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void LegacyLutColorOp::bypass(DrmAtomicCommit *commit)
|
||||
{
|
||||
commit->addBlob(*m_prop, nullptr);
|
||||
}
|
||||
|
||||
LegacyMatrixColorOp::LegacyMatrixColorOp(DrmAbstractColorOp *next, DrmProperty *prop)
|
||||
: DrmAbstractColorOp(next)
|
||||
, m_prop(prop)
|
||||
{
|
||||
}
|
||||
|
||||
bool LegacyMatrixColorOp::canBeUsedFor(const ColorOp &op)
|
||||
{
|
||||
// this isn't necessarily true, but let's keep things simple for now
|
||||
if (auto matrix = std::get_if<ColorMatrix>(&op.operation)) {
|
||||
return matrix->mat(3, 0) == 0
|
||||
&& matrix->mat(3, 1) == 0
|
||||
&& matrix->mat(3, 2) == 0
|
||||
&& matrix->mat(3, 3) == 1
|
||||
&& matrix->mat(0, 3) == 0
|
||||
&& matrix->mat(1, 3) == 0
|
||||
&& matrix->mat(2, 3) == 0;
|
||||
} else if (std::holds_alternative<ColorMultiplier>(op.operation)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint64_t doubleToFixed(double value)
|
||||
{
|
||||
// ctm values are in S31.32 sign-magnitude format
|
||||
uint64_t ret = std::abs(value) * (1ull << 32);
|
||||
if (value < 0) {
|
||||
ret |= 1ull << 63;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void LegacyMatrixColorOp::program(DrmAtomicCommit *commit, const ColorOp &op, double inputScale, double outputScale)
|
||||
{
|
||||
if (auto matrix = std::get_if<ColorMatrix>(&op.operation)) {
|
||||
QMatrix4x4 scaled = matrix->mat;
|
||||
scaled.scale(outputScale / inputScale);
|
||||
drm_color_ctm data = {
|
||||
.matrix = {
|
||||
doubleToFixed(scaled(0, 0)), doubleToFixed(scaled(0, 1)), doubleToFixed(scaled(0, 2)), //
|
||||
doubleToFixed(scaled(1, 0)), doubleToFixed(scaled(1, 1)), doubleToFixed(scaled(1, 2)), //
|
||||
doubleToFixed(scaled(2, 0)), doubleToFixed(scaled(2, 1)), doubleToFixed(scaled(2, 2)), //
|
||||
},
|
||||
};
|
||||
commit->addBlob(*m_prop, DrmBlob::create(m_prop->drmObject()->gpu(), &data, sizeof(data)));
|
||||
} else if (auto mult = std::get_if<ColorMultiplier>(&op.operation)) {
|
||||
QVector3D scaled = mult->factors;
|
||||
scaled *= outputScale / inputScale;
|
||||
drm_color_ctm data = {
|
||||
.matrix = {
|
||||
doubleToFixed(scaled.x()), doubleToFixed(0), doubleToFixed(0), //
|
||||
doubleToFixed(0), doubleToFixed(scaled.y()), doubleToFixed(0), //
|
||||
doubleToFixed(0), doubleToFixed(0), doubleToFixed(scaled.z()), //
|
||||
},
|
||||
};
|
||||
commit->addBlob(*m_prop, DrmBlob::create(m_prop->drmObject()->gpu(), &data, sizeof(data)));
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void LegacyMatrixColorOp::bypass(DrmAtomicCommit *commit)
|
||||
{
|
||||
commit->addBlob(*m_prop, nullptr);
|
||||
}
|
||||
|
||||
}
|
70
src/backends/drm/drm_colorop.h
Normal file
70
src/backends/drm/drm_colorop.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2024 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
#include "core/colorpipeline.h"
|
||||
|
||||
#include <drm.h>
|
||||
#include <memory>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmBlob;
|
||||
class DrmProperty;
|
||||
class DrmAtomicCommit;
|
||||
|
||||
class DrmAbstractColorOp
|
||||
{
|
||||
public:
|
||||
explicit DrmAbstractColorOp(DrmAbstractColorOp *next);
|
||||
virtual ~DrmAbstractColorOp();
|
||||
|
||||
bool matchPipeline(DrmAtomicCommit *commit, const ColorPipeline &pipeline);
|
||||
virtual bool canBeUsedFor(const ColorOp &op) = 0;
|
||||
virtual void program(DrmAtomicCommit *commit, const ColorOp &op, double inputScale, double outputScale) = 0;
|
||||
virtual void bypass(DrmAtomicCommit *commit) = 0;
|
||||
|
||||
DrmAbstractColorOp *next() const;
|
||||
|
||||
protected:
|
||||
DrmAbstractColorOp *m_next = nullptr;
|
||||
|
||||
std::optional<ColorPipeline> m_cachedPipeline;
|
||||
std::unique_ptr<DrmAtomicCommit> m_cache;
|
||||
};
|
||||
|
||||
class LegacyLutColorOp : public DrmAbstractColorOp
|
||||
{
|
||||
public:
|
||||
explicit LegacyLutColorOp(DrmAbstractColorOp *next, DrmProperty *prop, uint32_t maxSize);
|
||||
|
||||
bool canBeUsedFor(const ColorOp &op) override;
|
||||
void program(DrmAtomicCommit *commit, const ColorOp &op, double inputScale, double outputScale) override;
|
||||
void bypass(DrmAtomicCommit *commit) override;
|
||||
|
||||
private:
|
||||
DrmProperty *const m_prop;
|
||||
const uint32_t m_maxSize;
|
||||
QList<drm_color_lut> m_components;
|
||||
};
|
||||
|
||||
class LegacyMatrixColorOp : public DrmAbstractColorOp
|
||||
{
|
||||
public:
|
||||
explicit LegacyMatrixColorOp(DrmAbstractColorOp *next, DrmProperty *prop);
|
||||
|
||||
bool canBeUsedFor(const ColorOp &op) override;
|
||||
void program(DrmAtomicCommit *commit, const ColorOp &op, double inputScale, double outputScale) override;
|
||||
void bypass(DrmAtomicCommit *commit) override;
|
||||
|
||||
private:
|
||||
DrmProperty *const m_prop;
|
||||
};
|
||||
|
||||
}
|
|
@ -39,6 +39,11 @@ DrmGpu *DrmCommit::gpu() const
|
|||
return m_gpu;
|
||||
}
|
||||
|
||||
DrmAtomicCommit::DrmAtomicCommit(DrmGpu *gpu)
|
||||
: DrmCommit(gpu)
|
||||
{
|
||||
}
|
||||
|
||||
DrmAtomicCommit::DrmAtomicCommit(const QList<DrmPipeline *> &pipelines)
|
||||
: DrmCommit(pipelines.front()->gpu())
|
||||
, m_pipelines(pipelines)
|
||||
|
|
|
@ -51,8 +51,9 @@ protected:
|
|||
class DrmAtomicCommit : public DrmCommit
|
||||
{
|
||||
public:
|
||||
DrmAtomicCommit(const QList<DrmPipeline *> &pipelines);
|
||||
DrmAtomicCommit(const DrmAtomicCommit ©) = default;
|
||||
explicit DrmAtomicCommit(DrmGpu *gpu);
|
||||
explicit DrmAtomicCommit(const QList<DrmPipeline *> &pipelines);
|
||||
explicit DrmAtomicCommit(const DrmAtomicCommit ©) = default;
|
||||
|
||||
void addProperty(const DrmProperty &prop, uint64_t value);
|
||||
template<typename T>
|
||||
|
|
|
@ -49,6 +49,23 @@ bool DrmCrtc::updateProperties()
|
|||
degammaLut.update(props);
|
||||
degammaLutSize.update(props);
|
||||
|
||||
if (!postBlendingPipeline) {
|
||||
DrmAbstractColorOp *next = nullptr;
|
||||
if (gammaLut.isValid() && gammaLutSize.isValid() && gammaLutSize.value() > 0) {
|
||||
m_postBlendingColorOps.push_back(std::make_unique<LegacyLutColorOp>(next, &gammaLut, gammaLutSize.value()));
|
||||
next = m_postBlendingColorOps.back().get();
|
||||
}
|
||||
if (ctm.isValid()) {
|
||||
m_postBlendingColorOps.push_back(std::make_unique<LegacyMatrixColorOp>(next, &ctm));
|
||||
next = m_postBlendingColorOps.back().get();
|
||||
}
|
||||
if (degammaLut.isValid() && degammaLutSize.isValid() && degammaLutSize.value() > 0) {
|
||||
m_postBlendingColorOps.push_back(std::make_unique<LegacyLutColorOp>(next, °ammaLut, degammaLutSize.value()));
|
||||
next = m_postBlendingColorOps.back().get();
|
||||
}
|
||||
postBlendingPipeline = next;
|
||||
}
|
||||
|
||||
const bool ret = !gpu()->atomicModeSetting() || (modeId.isValid() && active.isValid());
|
||||
if (!ret) {
|
||||
qCWarning(KWIN_DRM) << "Failed to update the basic crtc properties. modeId:" << modeId.isValid() << "active:" << active.isValid();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "drm_colorop.h"
|
||||
#include "drm_object.h"
|
||||
|
||||
#include <QPoint>
|
||||
|
@ -25,7 +26,7 @@ class DrmPlane;
|
|||
class DrmCrtc : public DrmObject
|
||||
{
|
||||
public:
|
||||
DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane, DrmPlane *cursorPlane);
|
||||
explicit DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane, DrmPlane *cursorPlane);
|
||||
|
||||
void disable(DrmAtomicCommit *commit) override;
|
||||
bool updateProperties() override;
|
||||
|
@ -49,12 +50,16 @@ public:
|
|||
DrmProperty degammaLut;
|
||||
DrmProperty degammaLutSize;
|
||||
|
||||
DrmAbstractColorOp *postBlendingPipeline = nullptr;
|
||||
|
||||
private:
|
||||
DrmUniquePtr<drmModeCrtc> m_crtc;
|
||||
std::shared_ptr<DrmFramebuffer> m_currentBuffer;
|
||||
int m_pipeIndex;
|
||||
DrmPlane *m_primaryPlane;
|
||||
DrmPlane *m_cursorPlane;
|
||||
|
||||
std::vector<std::unique_ptr<DrmAbstractColorOp>> m_postBlendingColorOps;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -345,8 +345,7 @@ bool DrmOutput::queueChanges(const std::shared_ptr<OutputChangeSet> &props)
|
|||
}
|
||||
if (bt2020 || hdr || props->colorProfileSource.value_or(m_state.colorProfileSource) != ColorProfileSource::sRGB) {
|
||||
// remove unused gamma ramp and ctm, if present
|
||||
m_pipeline->setGammaRamp(nullptr);
|
||||
m_pipeline->setCTM(QMatrix3x3{});
|
||||
m_pipeline->setCrtcColorPipeline(ColorPipeline{});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -458,36 +457,17 @@ bool DrmOutput::doSetChannelFactors(const QVector3D &rgb)
|
|||
if (!m_pipeline->activePending()) {
|
||||
return false;
|
||||
}
|
||||
ColorPipeline pipeline{ValueRange{}};
|
||||
const auto inOutputSpace = m_state.colorDescription.transferFunction().nitsToEncoded(rgb, 1);
|
||||
if (m_pipeline->hasCTM()) {
|
||||
QMatrix3x3 ctm;
|
||||
ctm(0, 0) = inOutputSpace.x();
|
||||
ctm(1, 1) = inOutputSpace.y();
|
||||
ctm(2, 2) = inOutputSpace.z();
|
||||
m_pipeline->setCTM(ctm);
|
||||
m_pipeline->setGammaRamp(nullptr);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_channelFactorsNeedShaderFallback = false;
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->setCTM(QMatrix3x3());
|
||||
m_pipeline->applyPendingChanges();
|
||||
}
|
||||
}
|
||||
if (m_pipeline->hasGammaRamp()) {
|
||||
auto lut = ColorTransformation::createScalingTransform(inOutputSpace);
|
||||
if (lut) {
|
||||
m_pipeline->setGammaRamp(std::move(lut));
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_channelFactorsNeedShaderFallback = false;
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->setGammaRamp(nullptr);
|
||||
m_pipeline->applyPendingChanges();
|
||||
}
|
||||
}
|
||||
pipeline.addMultiplier(inOutputSpace);
|
||||
m_pipeline->setCrtcColorPipeline(pipeline);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_channelFactorsNeedShaderFallback = false;
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->setCrtcColorPipeline(ColorPipeline{});
|
||||
m_pipeline->applyPendingChanges();
|
||||
}
|
||||
m_channelFactorsNeedShaderFallback = m_channelFactors != QVector3D{1, 1, 1};
|
||||
return true;
|
||||
|
|
|
@ -212,15 +212,15 @@ DrmPipeline::Error DrmPipeline::prepareAtomicPresentation(DrmAtomicCommit *commi
|
|||
if (m_pending.crtc->vrrEnabled.isValid()) {
|
||||
commit->setVrr(m_pending.crtc, m_pending.presentationMode == PresentationMode::AdaptiveSync || m_pending.presentationMode == PresentationMode::AdaptiveAsync);
|
||||
}
|
||||
if (m_pending.crtc->gammaLut.isValid()) {
|
||||
commit->addBlob(m_pending.crtc->gammaLut, m_pending.gamma ? m_pending.gamma->blob() : nullptr);
|
||||
} else if (m_pending.gamma) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
if (m_pending.crtc->ctm.isValid()) {
|
||||
commit->addBlob(m_pending.crtc->ctm, m_pending.ctm);
|
||||
} else if (m_pending.ctm) {
|
||||
return Error::InvalidArguments;
|
||||
|
||||
if (!m_pending.crtc->postBlendingPipeline) {
|
||||
if (!m_pending.crtcColorPipeline.isIdentity()) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
} else {
|
||||
if (!m_pending.crtc->postBlendingPipeline->matchPipeline(commit, m_pending.crtcColorPipeline)) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_primaryLayer->checkTestBuffer()) {
|
||||
|
@ -480,20 +480,6 @@ QHash<uint32_t, QList<uint64_t>> DrmPipeline::formats(DrmPlane::TypeIndex planeT
|
|||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
bool DrmPipeline::hasCTM() const
|
||||
{
|
||||
return gpu()->atomicModeSetting() && m_pending.crtc && m_pending.crtc->ctm.isValid();
|
||||
}
|
||||
|
||||
bool DrmPipeline::hasGammaRamp() const
|
||||
{
|
||||
if (gpu()->atomicModeSetting()) {
|
||||
return m_pending.crtc && m_pending.crtc->gammaLut.isValid();
|
||||
} else {
|
||||
return m_pending.crtc && m_pending.crtc->gammaRampSize() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmPipeline::pruneModifier()
|
||||
{
|
||||
const DmaBufAttributes *dmabufAttributes = m_primaryLayer->currentBuffer() ? m_primaryLayer->currentBuffer()->buffer()->dmabufAttributes() : nullptr;
|
||||
|
@ -539,30 +525,6 @@ void DrmPipeline::resetModesetPresentPending()
|
|||
m_modesetPresentPending = false;
|
||||
}
|
||||
|
||||
DrmGammaRamp::DrmGammaRamp(DrmCrtc *crtc, const std::shared_ptr<ColorTransformation> &transformation)
|
||||
: m_lut(transformation, crtc->gammaRampSize())
|
||||
{
|
||||
if (crtc->gpu()->atomicModeSetting()) {
|
||||
QList<drm_color_lut> atomicLut(m_lut.size());
|
||||
for (uint32_t i = 0; i < m_lut.size(); i++) {
|
||||
atomicLut[i].red = m_lut.red()[i];
|
||||
atomicLut[i].green = m_lut.green()[i];
|
||||
atomicLut[i].blue = m_lut.blue()[i];
|
||||
}
|
||||
m_blob = DrmBlob::create(crtc->gpu(), atomicLut.data(), sizeof(drm_color_lut) * atomicLut.size());
|
||||
}
|
||||
}
|
||||
|
||||
const ColorLUT &DrmGammaRamp::lut() const
|
||||
{
|
||||
return m_lut;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrmBlob> DrmGammaRamp::blob() const
|
||||
{
|
||||
return m_blob;
|
||||
}
|
||||
|
||||
DrmCrtc *DrmPipeline::crtc() const
|
||||
{
|
||||
return m_pending.crtc;
|
||||
|
@ -625,9 +587,6 @@ const std::shared_ptr<IccProfile> &DrmPipeline::iccProfile() const
|
|||
|
||||
void DrmPipeline::setCrtc(DrmCrtc *crtc)
|
||||
{
|
||||
if (crtc && m_pending.crtc && crtc->gammaRampSize() != m_pending.crtc->gammaRampSize() && m_pending.colorTransformation) {
|
||||
m_pending.gamma = std::make_shared<DrmGammaRamp>(crtc, m_pending.colorTransformation);
|
||||
}
|
||||
m_pending.crtc = crtc;
|
||||
if (crtc) {
|
||||
m_pending.formats = crtc->primaryPlane() ? crtc->primaryPlane()->formats() : legacyFormats;
|
||||
|
@ -672,39 +631,9 @@ void DrmPipeline::setRgbRange(Output::RgbRange range)
|
|||
m_pending.rgbRange = range;
|
||||
}
|
||||
|
||||
void DrmPipeline::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
void DrmPipeline::setCrtcColorPipeline(const ColorPipeline &pipeline)
|
||||
{
|
||||
m_pending.colorTransformation = transformation;
|
||||
if (transformation) {
|
||||
m_pending.gamma = std::make_shared<DrmGammaRamp>(m_pending.crtc, transformation);
|
||||
} else {
|
||||
m_pending.gamma.reset();
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t doubleToFixed(double value)
|
||||
{
|
||||
// ctm values are in S31.32 sign-magnitude format
|
||||
uint64_t ret = std::abs(value) * (1ull << 32);
|
||||
if (value < 0) {
|
||||
ret |= 1ull << 63;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DrmPipeline::setCTM(const QMatrix3x3 &ctm)
|
||||
{
|
||||
if (ctm.isIdentity()) {
|
||||
m_pending.ctm.reset();
|
||||
} else {
|
||||
drm_color_ctm blob = {
|
||||
.matrix = {
|
||||
doubleToFixed(ctm(0, 0)), doubleToFixed(ctm(1, 0)), doubleToFixed(ctm(2, 0)),
|
||||
doubleToFixed(ctm(0, 1)), doubleToFixed(ctm(1, 1)), doubleToFixed(ctm(2, 1)),
|
||||
doubleToFixed(ctm(0, 2)), doubleToFixed(ctm(1, 2)), doubleToFixed(ctm(2, 2))},
|
||||
};
|
||||
m_pending.ctm = DrmBlob::create(gpu(), &blob, sizeof(blob));
|
||||
}
|
||||
m_pending.crtcColorPipeline = pipeline;
|
||||
}
|
||||
|
||||
void DrmPipeline::setColorDescription(const ColorDescription &description)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <xf86drmMode.h>
|
||||
|
||||
#include "core/colorlut.h"
|
||||
#include "core/colorpipeline.h"
|
||||
#include "core/colorspace.h"
|
||||
#include "core/output.h"
|
||||
#include "core/renderloop_p.h"
|
||||
|
@ -30,25 +31,11 @@ namespace KWin
|
|||
class DrmGpu;
|
||||
class DrmConnector;
|
||||
class DrmCrtc;
|
||||
class GammaRamp;
|
||||
class DrmConnectorMode;
|
||||
class DrmPipelineLayer;
|
||||
class DrmCommitThread;
|
||||
class OutputFrame;
|
||||
|
||||
class DrmGammaRamp
|
||||
{
|
||||
public:
|
||||
DrmGammaRamp(DrmCrtc *crtc, const std::shared_ptr<ColorTransformation> &transformation);
|
||||
|
||||
const ColorLUT &lut() const;
|
||||
std::shared_ptr<DrmBlob> blob() const;
|
||||
|
||||
private:
|
||||
const ColorLUT m_lut;
|
||||
std::shared_ptr<DrmBlob> m_blob;
|
||||
};
|
||||
|
||||
class DrmPipeline
|
||||
{
|
||||
public:
|
||||
|
@ -90,8 +77,6 @@ public:
|
|||
void resetModesetPresentPending();
|
||||
|
||||
QHash<uint32_t, QList<uint64_t>> formats(DrmPlane::TypeIndex planeType) const;
|
||||
bool hasCTM() const;
|
||||
bool hasGammaRamp() const;
|
||||
bool pruneModifier();
|
||||
|
||||
void setOutput(DrmOutput *output);
|
||||
|
@ -121,8 +106,7 @@ public:
|
|||
void setPresentationMode(PresentationMode mode);
|
||||
void setOverscan(uint32_t overscan);
|
||||
void setRgbRange(Output::RgbRange range);
|
||||
void setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
void setCTM(const QMatrix3x3 &ctm);
|
||||
void setCrtcColorPipeline(const ColorPipeline &pipeline);
|
||||
void setContentType(DrmConnector::DrmContentType type);
|
||||
void setColorDescription(const ColorDescription &description);
|
||||
void setIccProfile(const std::shared_ptr<IccProfile> &profile);
|
||||
|
@ -167,7 +151,7 @@ private:
|
|||
|
||||
bool m_modesetPresentPending = false;
|
||||
bool m_didLegacyScanoutHack = false;
|
||||
std::shared_ptr<DrmGammaRamp> m_currentLegacyGamma;
|
||||
ColorPipeline m_currentLegacyGamma;
|
||||
|
||||
struct State
|
||||
{
|
||||
|
@ -181,9 +165,7 @@ private:
|
|||
uint32_t overscan = 0;
|
||||
Output::RgbRange rgbRange = Output::RgbRange::Automatic;
|
||||
PresentationMode presentationMode = PresentationMode::VSync;
|
||||
std::shared_ptr<ColorTransformation> colorTransformation;
|
||||
std::shared_ptr<DrmGammaRamp> gamma;
|
||||
std::shared_ptr<DrmBlob> ctm;
|
||||
ColorPipeline crtcColorPipeline;
|
||||
DrmConnector::DrmContentType contentType = DrmConnector::DrmContentType::Graphics;
|
||||
|
||||
std::shared_ptr<IccProfile> iccProfile;
|
||||
|
|
|
@ -140,7 +140,7 @@ DrmPipeline::Error DrmPipeline::applyPendingChangesLegacy()
|
|||
return err;
|
||||
}
|
||||
}
|
||||
if (m_pending.gamma != m_currentLegacyGamma) {
|
||||
if (m_pending.crtcColorPipeline != m_currentLegacyGamma) {
|
||||
if (Error err = setLegacyGamma(); err != Error::None) {
|
||||
return err;
|
||||
}
|
||||
|
@ -159,13 +159,31 @@ DrmPipeline::Error DrmPipeline::applyPendingChangesLegacy()
|
|||
|
||||
DrmPipeline::Error DrmPipeline::setLegacyGamma()
|
||||
{
|
||||
if (m_pending.gamma) {
|
||||
if (drmModeCrtcSetGamma(gpu()->fd(), m_pending.crtc->id(), m_pending.gamma->lut().size(), m_pending.gamma->lut().red(), m_pending.gamma->lut().green(), m_pending.gamma->lut().blue()) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
||||
return errnoToError();
|
||||
}
|
||||
m_currentLegacyGamma = m_pending.gamma;
|
||||
if (m_pending.crtcColorPipeline.ops.size() > 1) {
|
||||
return DrmPipeline::Error::InvalidArguments;
|
||||
}
|
||||
QVector3D factors(1, 1, 1);
|
||||
if (m_pending.crtcColorPipeline.ops.size() == 1) {
|
||||
auto mult = std::get_if<ColorMultiplier>(&m_pending.crtcColorPipeline.ops.front().operation);
|
||||
if (!mult) {
|
||||
return DrmPipeline::Error::InvalidArguments;
|
||||
}
|
||||
factors = mult->factors;
|
||||
}
|
||||
QList<uint16_t> red(m_pending.crtc->gammaRampSize());
|
||||
QList<uint16_t> green(m_pending.crtc->gammaRampSize());
|
||||
QList<uint16_t> blue(m_pending.crtc->gammaRampSize());
|
||||
for (int i = 0; i < m_pending.crtc->gammaRampSize(); i++) {
|
||||
const double relative = i / double(m_pending.crtc->gammaRampSize() - 1) * std::numeric_limits<uint16_t>::max();
|
||||
red[i] = std::round(relative * factors.x());
|
||||
green[i] = std::round(relative * factors.y());
|
||||
blue[i] = std::round(relative * factors.z());
|
||||
}
|
||||
if (drmModeCrtcSetGamma(gpu()->fd(), m_pending.crtc->id(), m_pending.crtc->gammaRampSize(), red.data(), green.data(), blue.data()) != 0) {
|
||||
qCWarning(KWIN_DRM) << "Setting gamma failed!" << strerror(errno);
|
||||
return errnoToError();
|
||||
}
|
||||
m_currentLegacyGamma = m_pending.crtcColorPipeline;
|
||||
return DrmPipeline::Error::None;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue