diff --git a/src/backends/drm/drm_crtc.cpp b/src/backends/drm/drm_crtc.cpp index cd9d4a2f72..9b2fcce965 100644 --- a/src/backends/drm/drm_crtc.cpp +++ b/src/backends/drm/drm_crtc.cpp @@ -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) diff --git a/src/backends/drm/drm_crtc.h b/src/backends/drm/drm_crtc.h index 1750f52f5d..29241879b9 100644 --- a/src/backends/drm/drm_crtc.h +++ b/src/backends/drm/drm_crtc.h @@ -34,6 +34,7 @@ public: VrrEnabled, Gamma_LUT, Gamma_LUT_Size, + CTM, Count }; diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 44b42ba2f3..5bfa4017b4 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -469,15 +469,30 @@ DrmOutputLayer *DrmOutput::cursorLayer() const return m_pipeline->cursorLayer(); } -void DrmOutput::setColorTransformation(const std::shared_ptr &transformation) +bool DrmOutput::setGammaRamp(const std::shared_ptr &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; + } +} } diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index 4195dc8fd4..57eb0f2480 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -58,7 +58,8 @@ public: void leased(DrmLease *lease); void leaseEnded(); - void setColorTransformation(const std::shared_ptr &transformation) override; + bool setGammaRamp(const std::shared_ptr &transformation) override; + bool setCTM(const QMatrix3x3 &ctm) override; private: bool setDrmDpmsMode(DpmsMode mode); diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index a069abcfd7..03e8c96a1d 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -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 &transformation) +void DrmPipeline::setGammaRamp(const std::shared_ptr &transformation) { m_pending.colorTransformation = transformation; m_pending.gamma = std::make_shared(m_pending.crtc, transformation); } +void DrmPipeline::setCTM(const QMatrix3x3 &ctm) +{ + if (ctm.isIdentity()) { + m_pending.ctm.reset(); + } else { + m_pending.ctm = std::make_shared(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; +} } diff --git a/src/backends/drm/drm_pipeline.h b/src/backends/drm/drm_pipeline.h index 7e63ee0a53..c3c83ff3ae 100644 --- a/src/backends/drm/drm_pipeline.h +++ b/src/backends/drm/drm_pipeline.h @@ -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 &transformation); + void setGammaRamp(const std::shared_ptr &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; std::shared_ptr gamma; + std::shared_ptr ctm; DrmConnector::DrmContentType contentType = DrmConnector::DrmContentType::Graphics; std::shared_ptr layer; diff --git a/src/backends/x11/standalone/x11_standalone_output.cpp b/src/backends/x11/standalone/x11_standalone_output.cpp index eca9d30562..49cbf2e04e 100644 --- a/src/backends/x11/standalone/x11_standalone_output.cpp +++ b/src/backends/x11/standalone/x11_standalone_output.cpp @@ -39,13 +39,14 @@ void X11Output::setXineramaNumber(int number) m_xineramaNumber = number; } -void X11Output::setColorTransformation(const std::shared_ptr &transformation) +bool X11Output::setGammaRamp(const std::shared_ptr &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) diff --git a/src/backends/x11/standalone/x11_standalone_output.h b/src/backends/x11/standalone/x11_standalone_output.h index f44012b69c..e702bd2b19 100644 --- a/src/backends/x11/standalone/x11_standalone_output.h +++ b/src/backends/x11/standalone/x11_standalone_output.h @@ -39,7 +39,7 @@ public: int xineramaNumber() const; void setXineramaNumber(int number); - void setColorTransformation(const std::shared_ptr &transformation) override; + bool setGammaRamp(const std::shared_ptr &transformation) override; private: void setCrtc(xcb_randr_crtc_t crtc); diff --git a/src/colors/colordevice.cpp b/src/colors/colordevice.cpp index 1fc2e6e137..6f8cc3bbc4 100644 --- a/src/colors/colordevice.cpp +++ b/src/colors/colordevice.cpp @@ -54,10 +54,14 @@ public: uint temperature = 6500; std::unique_ptr temperatureStage; + QVector3D temperatureFactors; std::unique_ptr brightnessStage; + QVector3D brightnessFactors; std::unique_ptr calibrationStage; std::shared_ptr 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(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() diff --git a/src/core/output.cpp b/src/core/output.cpp index 92ee9d4512..5a1dfff773 100644 --- a/src/core/output.cpp +++ b/src/core/output.cpp @@ -397,8 +397,14 @@ Output::RgbRange Output::rgbRange() const return m_state.rgbRange; } -void Output::setColorTransformation(const std::shared_ptr &transformation) +bool Output::setGammaRamp(const std::shared_ptr &transformation) { + return false; +} + +bool Output::setCTM(const QMatrix3x3 &ctm) +{ + return false; } ContentType Output::contentType() const diff --git a/src/core/output.h b/src/core/output.h index 065f5f9b25..946af1d960 100644 --- a/src/core/output.h +++ b/src/core/output.h @@ -13,6 +13,7 @@ #include "renderloop.h" #include +#include #include #include #include @@ -258,7 +259,8 @@ public: bool isNonDesktop() const; Transform panelOrientation() const; - virtual void setColorTransformation(const std::shared_ptr &transformation); + virtual bool setGammaRamp(const std::shared_ptr &transformation); + virtual bool setCTM(const QMatrix3x3 &ctm); virtual bool setCursor(CursorSource *source); virtual bool moveCursor(const QPointF &position);