backends/drm: support CTM for simple color transformations
In order to work around hardware and drivers that aren't capable of applying a LUT, calculate a per-channel factor for brightness and color temperature modification. While this ignores color calibration, this makes night color work until a proper shader based color pipeline is implemented. BUG: 455720
This commit is contained in:
parent
f4a35ed619
commit
f2417a8523
11 changed files with 118 additions and 18 deletions
|
@ -19,7 +19,7 @@ namespace KWin
|
|||
{
|
||||
|
||||
DrmCrtc::DrmCrtc(DrmGpu *gpu, uint32_t crtcId, int pipeIndex, DrmPlane *primaryPlane, DrmPlane *cursorPlane)
|
||||
: DrmObject(gpu, crtcId, {PropertyDefinition(QByteArrayLiteral("MODE_ID"), Requirement::Required), PropertyDefinition(QByteArrayLiteral("ACTIVE"), Requirement::Required), PropertyDefinition(QByteArrayLiteral("VRR_ENABLED"), Requirement::Optional), PropertyDefinition(QByteArrayLiteral("GAMMA_LUT"), Requirement::Optional), PropertyDefinition(QByteArrayLiteral("GAMMA_LUT_SIZE"), Requirement::Optional)}, DRM_MODE_OBJECT_CRTC)
|
||||
: DrmObject(gpu, crtcId, {PropertyDefinition(QByteArrayLiteral("MODE_ID"), Requirement::Required), PropertyDefinition(QByteArrayLiteral("ACTIVE"), Requirement::Required), PropertyDefinition(QByteArrayLiteral("VRR_ENABLED"), Requirement::Optional), PropertyDefinition(QByteArrayLiteral("GAMMA_LUT"), Requirement::Optional), PropertyDefinition(QByteArrayLiteral("GAMMA_LUT_SIZE"), Requirement::Optional), PropertyDefinition(QByteArrayLiteral("CTM"), Requirement::Optional)}, DRM_MODE_OBJECT_CRTC)
|
||||
, m_crtc(drmModeGetCrtc(gpu->fd(), crtcId))
|
||||
, m_pipeIndex(pipeIndex)
|
||||
, m_primaryPlane(primaryPlane)
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
VrrEnabled,
|
||||
Gamma_LUT,
|
||||
Gamma_LUT_Size,
|
||||
CTM,
|
||||
Count
|
||||
};
|
||||
|
||||
|
|
|
@ -469,15 +469,30 @@ DrmOutputLayer *DrmOutput::cursorLayer() const
|
|||
return m_pipeline->cursorLayer();
|
||||
}
|
||||
|
||||
void DrmOutput::setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
bool DrmOutput::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
m_pipeline->setColorTransformation(transformation);
|
||||
m_pipeline->setGammaRamp(transformation);
|
||||
m_pipeline->setCTM(QMatrix3x3());
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_renderLoop->scheduleRepaint();
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->revertPendingChanges();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::setCTM(const QMatrix3x3 &ctm)
|
||||
{
|
||||
m_pipeline->setCTM(ctm);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_renderLoop->scheduleRepaint();
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->revertPendingChanges();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,8 @@ public:
|
|||
void leased(DrmLease *lease);
|
||||
void leaseEnded();
|
||||
|
||||
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
bool setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
bool setCTM(const QMatrix3x3 &ctm) override;
|
||||
|
||||
private:
|
||||
bool setDrmDpmsMode(DpmsMode mode);
|
||||
|
|
|
@ -238,7 +238,14 @@ void DrmPipeline::prepareAtomicPresentation()
|
|||
}
|
||||
|
||||
m_pending.crtc->setPending(DrmCrtc::PropertyIndex::VrrEnabled, m_pending.syncMode == RenderLoopPrivate::SyncMode::Adaptive || m_pending.syncMode == RenderLoopPrivate::SyncMode::AdaptiveAsync);
|
||||
m_pending.crtc->setPending(DrmCrtc::PropertyIndex::Gamma_LUT, m_pending.gamma ? m_pending.gamma->blobId() : 0);
|
||||
// use LUT if available, CTM if not
|
||||
if (const auto gamma = m_pending.crtc->getProp(DrmCrtc::PropertyIndex::Gamma_LUT)) {
|
||||
gamma->setPending(m_pending.gamma ? m_pending.gamma->blobId() : 0);
|
||||
m_pending.crtc->setPending(DrmCrtc::PropertyIndex::CTM, 0);
|
||||
} else {
|
||||
m_pending.crtc->setPending(DrmCrtc::PropertyIndex::CTM, m_pending.ctm ? m_pending.ctm->blobId() : 0);
|
||||
}
|
||||
|
||||
const auto fb = m_pending.layer->currentBuffer().get();
|
||||
m_pending.crtc->primaryPlane()->set(QPoint(0, 0), fb->buffer()->size(), centerBuffer(orientateSize(fb->buffer()->size(), m_pending.bufferOrientation), m_pending.mode->size()));
|
||||
m_pending.crtc->primaryPlane()->setBuffer(fb);
|
||||
|
@ -759,14 +766,57 @@ void DrmPipeline::setRgbRange(Output::RgbRange range)
|
|||
m_pending.rgbRange = range;
|
||||
}
|
||||
|
||||
void DrmPipeline::setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
void DrmPipeline::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
m_pending.colorTransformation = transformation;
|
||||
m_pending.gamma = std::make_shared<DrmGammaRamp>(m_pending.crtc, transformation);
|
||||
}
|
||||
|
||||
void DrmPipeline::setCTM(const QMatrix3x3 &ctm)
|
||||
{
|
||||
if (ctm.isIdentity()) {
|
||||
m_pending.ctm.reset();
|
||||
} else {
|
||||
m_pending.ctm = std::make_shared<DrmCTM>(gpu(), ctm);
|
||||
}
|
||||
}
|
||||
|
||||
void DrmPipeline::setContentType(DrmConnector::DrmContentType type)
|
||||
{
|
||||
m_pending.contentType = type;
|
||||
}
|
||||
|
||||
static uint64_t doubleToFixed(double value)
|
||||
{
|
||||
// ctm values are in S31.32 sign-magnitude format
|
||||
uint64_t ret = std::abs(value) * (1ul << 32);
|
||||
if (value < 0) {
|
||||
ret |= 1ul << 63;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
DrmCTM::DrmCTM(DrmGpu *gpu, const QMatrix3x3 &ctm)
|
||||
: m_gpu(gpu)
|
||||
{
|
||||
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))},
|
||||
};
|
||||
drmModeCreatePropertyBlob(m_gpu->fd(), &blob, sizeof(drm_color_ctm), &m_blobId);
|
||||
}
|
||||
|
||||
DrmCTM::~DrmCTM()
|
||||
{
|
||||
if (m_blobId) {
|
||||
drmModeDestroyPropertyBlob(m_gpu->fd(), m_blobId);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DrmCTM::blobId() const
|
||||
{
|
||||
return m_blobId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,19 @@ private:
|
|||
uint32_t m_blobId = 0;
|
||||
};
|
||||
|
||||
class DrmCTM
|
||||
{
|
||||
public:
|
||||
DrmCTM(DrmGpu *gpu, const QMatrix3x3 &ctm);
|
||||
~DrmCTM();
|
||||
|
||||
uint32_t blobId() const;
|
||||
|
||||
private:
|
||||
DrmGpu *const m_gpu;
|
||||
uint32_t m_blobId = 0;
|
||||
};
|
||||
|
||||
class DrmPipeline
|
||||
{
|
||||
public:
|
||||
|
@ -125,7 +138,8 @@ public:
|
|||
void setSyncMode(RenderLoopPrivate::SyncMode mode);
|
||||
void setOverscan(uint32_t overscan);
|
||||
void setRgbRange(Output::RgbRange range);
|
||||
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
void setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
void setCTM(const QMatrix3x3 &ctm);
|
||||
void setContentType(DrmConnector::DrmContentType type);
|
||||
|
||||
enum class CommitMode {
|
||||
|
@ -188,6 +202,7 @@ private:
|
|||
RenderLoopPrivate::SyncMode syncMode = RenderLoopPrivate::SyncMode::Fixed;
|
||||
std::shared_ptr<ColorTransformation> colorTransformation;
|
||||
std::shared_ptr<DrmGammaRamp> gamma;
|
||||
std::shared_ptr<DrmCTM> ctm;
|
||||
DrmConnector::DrmContentType contentType = DrmConnector::DrmContentType::Graphics;
|
||||
|
||||
std::shared_ptr<DrmPipelineLayer> layer;
|
||||
|
|
|
@ -39,13 +39,14 @@ void X11Output::setXineramaNumber(int number)
|
|||
m_xineramaNumber = number;
|
||||
}
|
||||
|
||||
void X11Output::setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
bool X11Output::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
if (m_crtc == XCB_NONE) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
ColorLUT lut(transformation, m_gammaRampSize);
|
||||
xcb_randr_set_crtc_gamma(kwinApp()->x11Connection(), m_crtc, lut.size(), lut.red(), lut.green(), lut.blue());
|
||||
return true;
|
||||
}
|
||||
|
||||
void X11Output::setCrtc(xcb_randr_crtc_t crtc)
|
||||
|
|
|
@ -39,7 +39,7 @@ public:
|
|||
int xineramaNumber() const;
|
||||
void setXineramaNumber(int number);
|
||||
|
||||
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
bool setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
|
||||
private:
|
||||
void setCrtc(xcb_randr_crtc_t crtc);
|
||||
|
|
|
@ -54,10 +54,14 @@ public:
|
|||
uint temperature = 6500;
|
||||
|
||||
std::unique_ptr<ColorPipelineStage> temperatureStage;
|
||||
QVector3D temperatureFactors;
|
||||
std::unique_ptr<ColorPipelineStage> brightnessStage;
|
||||
QVector3D brightnessFactors;
|
||||
std::unique_ptr<ColorPipelineStage> calibrationStage;
|
||||
|
||||
std::shared_ptr<ColorTransformation> transformation;
|
||||
// used if only limited per-channel multiplication is available
|
||||
QVector3D simpleTransformation;
|
||||
};
|
||||
|
||||
void ColorDevicePrivate::rebuildPipeline()
|
||||
|
@ -99,6 +103,7 @@ void ColorDevicePrivate::rebuildPipeline()
|
|||
const auto tmp = std::make_shared<ColorTransformation>(std::move(stages));
|
||||
if (tmp->valid()) {
|
||||
transformation = tmp;
|
||||
simpleTransformation = brightnessFactors * temperatureFactors;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,6 +139,8 @@ void ColorDevicePrivate::updateTemperatureToneCurves()
|
|||
blackbodyColor[blackBodyColorIndex + 5],
|
||||
blendFactor);
|
||||
|
||||
temperatureFactors = QVector3D(xWhitePoint, yWhitePoint, zWhitePoint);
|
||||
|
||||
const double redCurveParams[] = {1.0, xWhitePoint, 0.0};
|
||||
const double greenCurveParams[] = {1.0, yWhitePoint, 0.0};
|
||||
const double blueCurveParams[] = {1.0, zWhitePoint, 0.0};
|
||||
|
@ -172,6 +179,7 @@ void ColorDevicePrivate::updateBrightnessToneCurves()
|
|||
}
|
||||
|
||||
const double curveParams[] = {1.0, brightness / 100.0, 0.0};
|
||||
brightnessFactors = QVector3D(brightness / 100.0, brightness / 100.0, brightness / 100.0);
|
||||
|
||||
UniqueToneCurvePtr redCurve(cmsBuildParametricToneCurve(nullptr, 2, curveParams));
|
||||
if (!redCurve) {
|
||||
|
@ -291,11 +299,6 @@ void ColorDevice::setTemperature(uint temperature)
|
|||
Q_EMIT temperatureChanged();
|
||||
}
|
||||
|
||||
// QString ColorDevice::profile() const
|
||||
// {
|
||||
// return d->profile;
|
||||
// }
|
||||
|
||||
void ColorDevice::setProfile(const QString &profile)
|
||||
{
|
||||
if (d->profile == profile) {
|
||||
|
@ -310,7 +313,13 @@ void ColorDevice::setProfile(const QString &profile)
|
|||
void ColorDevice::update()
|
||||
{
|
||||
d->rebuildPipeline();
|
||||
d->output->setColorTransformation(d->transformation);
|
||||
if (!d->output->setGammaRamp(d->transformation)) {
|
||||
QMatrix3x3 ctm;
|
||||
ctm(0, 0) = d->simpleTransformation.x();
|
||||
ctm(1, 1) = d->simpleTransformation.y();
|
||||
ctm(2, 2) = d->simpleTransformation.z();
|
||||
d->output->setCTM(ctm);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorDevice::scheduleUpdate()
|
||||
|
|
|
@ -397,8 +397,14 @@ Output::RgbRange Output::rgbRange() const
|
|||
return m_state.rgbRange;
|
||||
}
|
||||
|
||||
void Output::setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
bool Output::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Output::setCTM(const QMatrix3x3 &ctm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentType Output::contentType() const
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "renderloop.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMatrix3x3>
|
||||
#include <QMatrix4x4>
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
|
@ -258,7 +259,8 @@ public:
|
|||
bool isNonDesktop() const;
|
||||
Transform panelOrientation() const;
|
||||
|
||||
virtual void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
virtual bool setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
virtual bool setCTM(const QMatrix3x3 &ctm);
|
||||
|
||||
virtual bool setCursor(CursorSource *source);
|
||||
virtual bool moveCursor(const QPointF &position);
|
||||
|
|
Loading…
Reference in a new issue