backends/drm: add brightness metadata overrides and sdr gamut wideness setting

The brightness overrides are for displays with missing or broken brightness
data in their EDID, and allow the user to work around those displays. In
the future we could also offer an HDR calibration process that allows determining
the correct brightness values for the screen.

The gamut wideness setting allows the user to tweak what gamut KWin assumes
sRGB applications to have. This is useful for working around the gamut mapping
displays do, which make sRGB content look washed out, and also to allow
users to make colors of sRGB apps look more saturated if they wish to.
This commit is contained in:
Xaver Hugl 2023-11-20 14:43:07 +01:00
parent 118bc93144
commit ae884dd19e
16 changed files with 319 additions and 39 deletions

View file

@ -111,7 +111,8 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(cons
if (enableColormanagement) {
m_surface->intermediaryColorDescription = ColorDescription(colorDescription.colorimetry(), NamedTransferFunction::linear,
colorDescription.sdrBrightness(), colorDescription.minHdrBrightness(),
colorDescription.maxFrameAverageBrightness(), colorDescription.maxHdrHighlightBrightness());
colorDescription.maxFrameAverageBrightness(), colorDescription.maxHdrHighlightBrightness(),
colorDescription.sdrGamutWideness());
} else {
m_surface->intermediaryColorDescription = colorDescription;
}

View file

@ -73,6 +73,7 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
const Edid *edid = conn->edid();
const auto hdrData = edid->hdrMetadata();
setInformation(Information{
.name = conn->connectorName(),
.manufacturer = edid->manufacturerString(),
@ -87,6 +88,9 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
.internal = conn->isInternal(),
.nonDesktop = conn->isNonDesktop(),
.mstPath = conn->mstPath(),
.maxPeakBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredContentMaxLuminance : 0,
.maxAverageBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredMaxFrameAverageLuminance : 0,
.minBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredContentMinLuminance : 0,
});
initialState.modes = getModes();
@ -324,6 +328,10 @@ bool DrmOutput::queueChanges(const std::shared_ptr<OutputChangeSet> &props)
m_pipeline->setBT2020(props->wideColorGamut.value_or(m_state.wideColorGamut));
m_pipeline->setNamedTransferFunction(props->highDynamicRange.value_or(m_state.highDynamicRange) ? NamedTransferFunction::PerceptualQuantizer : NamedTransferFunction::sRGB);
m_pipeline->setSdrBrightness(props->sdrBrightness.value_or(m_state.sdrBrightness));
m_pipeline->setSdrGamutWideness(props->sdrGamutWideness.value_or(m_state.sdrGamutWideness));
m_pipeline->setBrightnessOverrides(props->maxPeakBrightnessOverride.value_or(m_state.maxPeakBrightnessOverride),
props->maxAverageBrightnessOverride.value_or(m_state.maxAverageBrightnessOverride),
props->minBrightnessOverride.value_or(m_state.minBrightnessOverride));
return true;
}
@ -348,6 +356,10 @@ void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props
next.sdrBrightness = props->sdrBrightness.value_or(m_state.sdrBrightness);
next.wideColorGamut = props->wideColorGamut.value_or(m_state.wideColorGamut);
next.autoRotatePolicy = props->autoRotationPolicy.value_or(m_state.autoRotatePolicy);
next.maxPeakBrightnessOverride = props->maxPeakBrightnessOverride.value_or(m_state.maxPeakBrightnessOverride);
next.maxAverageBrightnessOverride = props->maxAverageBrightnessOverride.value_or(m_state.maxAverageBrightnessOverride);
next.minBrightnessOverride = props->minBrightnessOverride.value_or(m_state.minBrightnessOverride);
next.sdrGamutWideness = props->sdrGamutWideness.value_or(m_state.sdrGamutWideness);
if (props->iccProfilePath) {
next.iccProfilePath = *props->iccProfilePath;
next.iccProfile = IccProfile::load(*props->iccProfilePath);
@ -358,9 +370,6 @@ void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props
// ICC profiles don't support HDR (yet)
m_pipeline->setIccProfile(nullptr);
}
if (m_state.highDynamicRange != next.highDynamicRange || m_state.sdrBrightness != next.sdrBrightness || m_state.wideColorGamut != next.wideColorGamut || m_state.iccProfile != next.iccProfile) {
m_renderLoop->scheduleRepaint();
}
next.colorDescription = m_pipeline->colorDescription();
setState(next);

View file

@ -718,6 +718,24 @@ void DrmPipeline::setSdrBrightness(double sdrBrightness)
}
}
void DrmPipeline::setSdrGamutWideness(double sdrGamutWideness)
{
if (m_pending.sdrGamutWideness != sdrGamutWideness) {
m_pending.sdrGamutWideness = sdrGamutWideness;
m_pending.colorDescription = createColorDescription();
}
}
void DrmPipeline::setBrightnessOverrides(std::optional<double> peakBrightnessOverride, std::optional<double> averageBrightnessOverride, std::optional<double> minBrightnessOverride)
{
if (m_pending.peakBrightnessOverride != peakBrightnessOverride || m_pending.averageBrightnessOverride != averageBrightnessOverride || m_pending.minBrightnessOverride != minBrightnessOverride) {
m_pending.peakBrightnessOverride = peakBrightnessOverride;
m_pending.averageBrightnessOverride = averageBrightnessOverride;
m_pending.minBrightnessOverride = minBrightnessOverride;
m_pending.colorDescription = createColorDescription();
}
}
void DrmPipeline::setIccProfile(const std::shared_ptr<IccProfile> &profile)
{
if (m_pending.iccProfile != profile) {
@ -731,12 +749,14 @@ ColorDescription DrmPipeline::createColorDescription() const
if (m_pending.transferFunction == NamedTransferFunction::PerceptualQuantizer && m_connector->edid()) {
const auto colorimetry = m_pending.BT2020 ? NamedColorimetry::BT2020 : NamedColorimetry::BT709;
if (const auto hdr = m_connector->edid()->hdrMetadata(); hdr && hdr->hasValidBrightnessValues) {
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, hdr->desiredContentMinLuminance, hdr->desiredMaxFrameAverageLuminance, hdr->desiredContentMaxLuminance);
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, hdr->desiredContentMinLuminance, hdr->desiredMaxFrameAverageLuminance, hdr->desiredContentMaxLuminance, m_pending.sdrGamutWideness);
} else if (m_pending.peakBrightnessOverride && m_pending.averageBrightnessOverride) {
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, m_pending.minBrightnessOverride.value_or(0), *m_pending.averageBrightnessOverride, *m_pending.peakBrightnessOverride, m_pending.sdrGamutWideness);
} else {
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, 0, m_pending.sdrBrightness, m_pending.sdrBrightness);
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, 0, m_pending.sdrBrightness, m_pending.sdrBrightness, m_pending.sdrGamutWideness);
}
} else if (m_pending.iccProfile) {
return ColorDescription(m_pending.iccProfile->colorimetry(), NamedTransferFunction::sRGB, 200, 0, 200, 200);
return ColorDescription(m_pending.iccProfile->colorimetry(), NamedTransferFunction::sRGB, 200, 0, 200, 200, 0);
} else {
return ColorDescription::sRGB;
}

View file

@ -132,6 +132,8 @@ public:
void setNamedTransferFunction(NamedTransferFunction tf);
void setIccProfile(const std::shared_ptr<IccProfile> &profile);
void setSdrBrightness(double sdrBrightness);
void setSdrGamutWideness(double sdrGamutWideness);
void setBrightnessOverrides(std::optional<double> peakBrightnessOverride, std::optional<double> averageBrightnessOverride, std::optional<double> minBrightnessOverride);
enum class CommitMode {
Test,
@ -189,8 +191,12 @@ private:
bool BT2020 = false;
NamedTransferFunction transferFunction = NamedTransferFunction::sRGB;
double sdrBrightness = 200;
double sdrGamutWideness = 0;
std::shared_ptr<IccProfile> iccProfile;
ColorDescription colorDescription = ColorDescription::sRGB;
std::optional<double> peakBrightnessOverride;
std::optional<double> averageBrightnessOverride;
std::optional<double> minBrightnessOverride;
// the transformation that buffers submitted to the pipeline should have
DrmPlane::Transformations renderOrientation = DrmPlane::Transformation::Rotate0;

View file

@ -134,25 +134,29 @@ bool Colorimetry::operator==(const Colorimetry &other) const
: (red == other.red && green == other.green && blue == other.blue && white == other.white);
}
constexpr Colorimetry Colorimetry::fromName(NamedColorimetry name)
static const Colorimetry BT709 = Colorimetry{
.red = {0.64, 0.33},
.green = {0.30, 0.60},
.blue = {0.15, 0.06},
.white = {0.3127, 0.3290},
.name = NamedColorimetry::BT709,
};
static const Colorimetry BT2020 = Colorimetry{
.red = {0.708, 0.292},
.green = {0.170, 0.797},
.blue = {0.131, 0.046},
.white = {0.3127, 0.3290},
.name = NamedColorimetry::BT2020,
};
Colorimetry Colorimetry::fromName(NamedColorimetry name)
{
switch (name) {
case NamedColorimetry::BT709:
return Colorimetry{
.red = {0.64, 0.33},
.green = {0.30, 0.60},
.blue = {0.15, 0.06},
.white = {0.3127, 0.3290},
.name = name,
};
return BT709;
case NamedColorimetry::BT2020:
return Colorimetry{
.red = {0.708, 0.292},
.green = {0.170, 0.797},
.blue = {0.131, 0.046},
.white = {0.3127, 0.3290},
.name = name,
};
return BT2020;
}
Q_UNREACHABLE();
}
@ -168,11 +172,23 @@ Colorimetry Colorimetry::fromXYZ(QVector3D red, QVector3D green, QVector3D blue,
};
}
const ColorDescription ColorDescription::sRGB = ColorDescription(NamedColorimetry::BT709, NamedTransferFunction::sRGB, 100, 0, 100, 100);
const ColorDescription ColorDescription::sRGB = ColorDescription(NamedColorimetry::BT709, NamedTransferFunction::sRGB, 100, 0, 100, 100, 0);
ColorDescription::ColorDescription(const Colorimetry &colorimety, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness)
static Colorimetry sRGBColorimetry(double factor)
{
return Colorimetry{
.red = BT709.red * (1 - factor) + BT2020.red * factor,
.green = BT709.green * (1 - factor) + BT2020.green * factor,
.blue = BT709.blue * (1 - factor) + BT2020.blue * factor,
.white = BT709.white, // whitepoint is the same
};
}
ColorDescription::ColorDescription(const Colorimetry &colorimety, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness, double sdrGamutWideness)
: m_colorimetry(colorimety)
, m_transferFunction(tf)
, m_sdrColorimetry(sRGBColorimetry(sdrGamutWideness))
, m_sdrGamutWideness(sdrGamutWideness)
, m_sdrBrightness(sdrBrightness)
, m_minHdrBrightness(minHdrBrightness)
, m_maxFrameAverageBrightness(maxFrameAverageBrightness)
@ -180,9 +196,11 @@ ColorDescription::ColorDescription(const Colorimetry &colorimety, NamedTransferF
{
}
ColorDescription::ColorDescription(NamedColorimetry colorimetry, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness)
ColorDescription::ColorDescription(NamedColorimetry colorimetry, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness, double sdrGamutWideness)
: m_colorimetry(Colorimetry::fromName(colorimetry))
, m_transferFunction(tf)
, m_sdrColorimetry(sRGBColorimetry(sdrGamutWideness))
, m_sdrGamutWideness(sdrGamutWideness)
, m_sdrBrightness(sdrBrightness)
, m_minHdrBrightness(minHdrBrightness)
, m_maxFrameAverageBrightness(maxFrameAverageBrightness)
@ -195,6 +213,11 @@ const Colorimetry &ColorDescription::colorimetry() const
return m_colorimetry;
}
const Colorimetry &ColorDescription::sdrColorimetry() const
{
return m_sdrColorimetry;
}
NamedTransferFunction ColorDescription::transferFunction() const
{
return m_transferFunction;
@ -220,14 +243,20 @@ double ColorDescription::maxHdrHighlightBrightness() const
return m_maxHdrHighlightBrightness;
}
double ColorDescription::sdrGamutWideness() const
{
return m_sdrGamutWideness;
}
bool ColorDescription::operator==(const ColorDescription &other) const
{
return m_colorimetry == other.colorimetry()
&& m_transferFunction == other.transferFunction()
&& m_sdrBrightness == other.sdrBrightness()
&& m_minHdrBrightness == other.minHdrBrightness()
&& m_maxFrameAverageBrightness == other.maxFrameAverageBrightness()
&& m_maxHdrHighlightBrightness == other.maxHdrHighlightBrightness();
return m_colorimetry == other.m_colorimetry
&& m_transferFunction == other.m_transferFunction
&& m_sdrGamutWideness == other.m_sdrGamutWideness
&& m_sdrBrightness == other.m_sdrBrightness
&& m_minHdrBrightness == other.m_minHdrBrightness
&& m_maxFrameAverageBrightness == other.m_maxFrameAverageBrightness
&& m_maxHdrHighlightBrightness == other.m_maxHdrHighlightBrightness;
}
static float srgbToLinear(float sRGB)

View file

@ -27,7 +27,7 @@ enum class NamedColorimetry {
class KWIN_EXPORT Colorimetry
{
public:
static constexpr Colorimetry fromName(NamedColorimetry name);
static Colorimetry fromName(NamedColorimetry name);
static Colorimetry fromXYZ(QVector3D red, QVector3D green, QVector3D blue, QVector3D white);
/**
* @returns the XYZ representation of the xyY color passed in. Y is assumed to be one
@ -88,16 +88,19 @@ public:
* @param minHdrBrightness the minimum brightness of HDR content
* @param maxFrameAverageBrightness the maximum brightness of HDR content, if the whole screen is white
* @param maxHdrHighlightBrightness the maximum brightness of HDR content, for a small part of the screen only
* @param sdrGamutWideness the gamut wideness of sRGB content; 0 is rec.709, 1 is rec.2020, everything in between is interpolated
*/
explicit ColorDescription(const Colorimetry &colorimety, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness);
explicit ColorDescription(NamedColorimetry colorimetry, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness);
explicit ColorDescription(const Colorimetry &colorimety, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness, double sdrGamutWideness);
explicit ColorDescription(NamedColorimetry colorimetry, NamedTransferFunction tf, double sdrBrightness, double minHdrBrightness, double maxFrameAverageBrightness, double maxHdrHighlightBrightness, double sdrGamutWideness);
const Colorimetry &colorimetry() const;
const Colorimetry &sdrColorimetry() const;
NamedTransferFunction transferFunction() const;
double sdrBrightness() const;
double minHdrBrightness() const;
double maxFrameAverageBrightness() const;
double maxHdrHighlightBrightness() const;
double sdrGamutWideness() const;
bool operator==(const ColorDescription &other) const;
@ -108,6 +111,8 @@ public:
private:
Colorimetry m_colorimetry;
NamedTransferFunction m_transferFunction;
Colorimetry m_sdrColorimetry;
double m_sdrGamutWideness;
double m_sdrBrightness;
double m_minHdrBrightness;
double m_maxFrameAverageBrightness;

View file

@ -431,6 +431,14 @@ void Output::setState(const State &state)
if (oldState.iccProfilePath != state.iccProfilePath) {
Q_EMIT iccProfilePathChanged();
}
if (oldState.maxPeakBrightnessOverride != state.maxPeakBrightnessOverride
|| oldState.maxAverageBrightnessOverride != state.maxAverageBrightnessOverride
|| oldState.minBrightnessOverride != state.minBrightnessOverride) {
Q_EMIT brightnessMetadataChanged();
}
if (oldState.sdrGamutWideness != state.sdrGamutWideness) {
Q_EMIT sdrGamutWidenessChanged();
}
if (oldState.enabled != state.enabled) {
Q_EMIT enabledChanged();
}
@ -604,6 +612,40 @@ const ColorDescription &Output::colorDescription() const
return m_state.colorDescription;
}
double Output::maxPeakBrightness() const
{
return m_state.maxPeakBrightnessOverride.value_or(m_information.maxPeakBrightness);
}
double Output::maxAverageBrightness() const
{
return m_state.maxAverageBrightnessOverride.value_or(m_information.maxAverageBrightness);
}
double Output::minBrightness() const
{
return m_state.minBrightnessOverride.value_or(m_information.minBrightness);
}
std::optional<double> Output::maxPeakBrightnessOverride() const
{
return m_state.maxPeakBrightnessOverride;
}
std::optional<double> Output::maxAverageBrightnessOverride() const
{
return m_state.maxAverageBrightnessOverride;
}
std::optional<double> Output::minBrightnessOverride() const
{
return m_state.minBrightnessOverride;
}
double Output::sdrGamutWideness() const
{
return m_state.sdrGamutWideness;
}
} // namespace KWin
#include "moc_output.cpp"

View file

@ -335,6 +335,15 @@ public:
virtual bool updateCursorLayer();
double maxPeakBrightness() const;
double maxAverageBrightness() const;
double minBrightness() const;
std::optional<double> maxPeakBrightnessOverride() const;
std::optional<double> maxAverageBrightnessOverride() const;
std::optional<double> minBrightnessOverride() const;
double sdrGamutWideness() const;
const ColorDescription &colorDescription() const;
Q_SIGNALS:
@ -396,6 +405,8 @@ Q_SIGNALS:
void autoRotationPolicyChanged();
void iccProfileChanged();
void iccProfilePathChanged();
void brightnessMetadataChanged();
void sdrGamutWidenessChanged();
protected:
struct Information
@ -414,6 +425,9 @@ protected:
bool placeholder = false;
bool nonDesktop = false;
QByteArray mstPath;
double maxPeakBrightness = 0;
double maxAverageBrightness = 0;
double minBrightness = 0;
};
struct State
@ -436,6 +450,10 @@ protected:
QString iccProfilePath;
std::shared_ptr<IccProfile> iccProfile;
ColorDescription colorDescription = ColorDescription::sRGB;
std::optional<float> maxPeakBrightnessOverride;
std::optional<float> maxAverageBrightnessOverride;
std::optional<float> minBrightnessOverride;
double sdrGamutWideness = 0;
};
void setInformation(const Information &information);

View file

@ -37,6 +37,10 @@ public:
std::optional<bool> wideColorGamut;
std::optional<Output::AutoRotationPolicy> autoRotationPolicy;
std::optional<QString> iccProfilePath;
std::optional<std::optional<double>> maxPeakBrightnessOverride;
std::optional<std::optional<double>> maxAverageBrightnessOverride;
std::optional<std::optional<double>> minBrightnessOverride;
std::optional<double> sdrGamutWideness;
};
class KWIN_EXPORT OutputConfiguration

View file

@ -441,7 +441,8 @@ QMatrix4x4 GLShader::getUniformMatrix4x4(const char *name)
bool GLShader::setColorspaceUniforms(const ColorDescription &src, const ColorDescription &dst)
{
return setUniform(GLShader::MatrixUniform::ColorimetryTransformation, src.colorimetry().toOther(dst.colorimetry()))
const auto &srcColorimetry = src.colorimetry().name == NamedColorimetry::BT709 ? dst.sdrColorimetry() : src.colorimetry();
return setUniform(GLShader::MatrixUniform::ColorimetryTransformation, srcColorimetry.toOther(dst.colorimetry()))
&& setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(src.transferFunction()))
&& setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(dst.transferFunction()))
&& setUniform(FloatUniform::SdrBrightness, dst.sdrBrightness())
@ -455,7 +456,7 @@ bool GLShader::setColorspaceUniformsFromSRGB(const ColorDescription &dst)
bool GLShader::setColorspaceUniformsToSRGB(const ColorDescription &src)
{
return setUniform(GLShader::MatrixUniform::ColorimetryTransformation, src.colorimetry().toOther(ColorDescription::sRGB.colorimetry()))
return setUniform(GLShader::MatrixUniform::ColorimetryTransformation, src.colorimetry().toOther(src.sdrColorimetry()))
&& setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(src.transferFunction()))
&& setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(NamedTransferFunction::sRGB))
&& setUniform(FloatUniform::SdrBrightness, src.sdrBrightness())

View file

@ -209,6 +209,10 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.wideColorGamut = changeSet->wideColorGamut.value_or(output->wideColorGamut()),
.autoRotation = changeSet->autoRotationPolicy.value_or(output->autoRotationPolicy()),
.iccProfilePath = changeSet->iccProfilePath.value_or(output->iccProfilePath()),
.maxPeakBrightnessOverride = changeSet->maxPeakBrightnessOverride.value_or(output->maxPeakBrightnessOverride()),
.maxAverageBrightnessOverride = changeSet->maxAverageBrightnessOverride.value_or(output->maxAverageBrightnessOverride()),
.minBrightnessOverride = changeSet->minBrightnessOverride.value_or(output->minBrightnessOverride()),
.sdrGamutWideness = changeSet->sdrGamutWideness.value_or(output->sdrGamutWideness()),
};
*outputIt = SetupState{
.outputIndex = *outputIndex,
@ -237,6 +241,10 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.wideColorGamut = output->wideColorGamut(),
.autoRotation = output->autoRotationPolicy(),
.iccProfilePath = output->iccProfilePath(),
.maxPeakBrightnessOverride = output->maxPeakBrightnessOverride(),
.maxAverageBrightnessOverride = output->maxAverageBrightnessOverride(),
.minBrightnessOverride = output->minBrightnessOverride(),
.sdrGamutWideness = output->sdrGamutWideness(),
};
*outputIt = SetupState{
.outputIndex = *outputIndex,
@ -279,6 +287,10 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::setupT
.wideColorGamut = state.wideColorGamut,
.autoRotationPolicy = state.autoRotation,
.iccProfilePath = state.iccProfilePath,
.maxPeakBrightnessOverride = state.maxPeakBrightnessOverride,
.maxAverageBrightnessOverride = state.maxAverageBrightnessOverride,
.minBrightnessOverride = state.minBrightnessOverride,
.sdrGamutWideness = state.sdrGamutWideness,
};
if (setupState.enabled) {
priorities.push_back(std::make_pair(output, setupState.priority));
@ -652,6 +664,18 @@ void OutputConfigurationStore::load()
if (const auto it = data.find("iccProfilePath"); it != data.end()) {
state.iccProfilePath = it->toString();
}
if (const auto it = data.find("maxPeakBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxPeakBrightnessOverride = it->toDouble();
}
if (const auto it = data.find("maxAverageBrightnessOverride"); it != data.end() && it->isDouble()) {
state.maxAverageBrightnessOverride = it->toDouble();
}
if (const auto it = data.find("minBrightnessOverride"); it != data.end() && it->isDouble()) {
state.minBrightnessOverride = it->toDouble();
}
if (const auto it = data.find("sdrGamutWideness"); it != data.end() && it->isDouble()) {
state.sdrGamutWideness = it->toDouble();
}
outputDatas.push_back(state);
}
@ -846,6 +870,18 @@ void OutputConfigurationStore::save()
if (output.iccProfilePath) {
o["iccProfilePath"] = *output.iccProfilePath;
}
if (output.maxPeakBrightnessOverride) {
o["maxPeakBrightnessOverride"] = *output.maxPeakBrightnessOverride;
}
if (output.maxAverageBrightnessOverride) {
o["maxAverageBrightnessOverride"] = *output.maxAverageBrightnessOverride;
}
if (output.minBrightnessOverride) {
o["minBrightnessOverride"] = *output.minBrightnessOverride;
}
if (output.sdrGamutWideness) {
o["sdrGamutWideness"] = *output.sdrGamutWideness;
}
outputsData.append(o);
}
outputs["data"] = outputsData;

View file

@ -76,6 +76,10 @@ private:
std::optional<bool> wideColorGamut;
std::optional<Output::AutoRotationPolicy> autoRotation;
std::optional<QString> iccProfilePath;
std::optional<double> maxPeakBrightnessOverride;
std::optional<double> maxAverageBrightnessOverride;
std::optional<double> minBrightnessOverride;
std::optional<double> sdrGamutWideness;
};
struct SetupState
{

View file

@ -148,7 +148,7 @@ void FrogColorManagementSurfaceV1::updateColorDescription()
if (m_surface) {
// TODO make brightness values optional in ColorDescription
SurfaceInterfacePrivate *priv = SurfaceInterfacePrivate::get(m_surface);
priv->pending->colorDescription = ColorDescription(m_colorimetry, m_transferFunction, 0, 0, m_maxFrameAverageBrightness, m_maxPeakBrightness);
priv->pending->colorDescription = ColorDescription(m_colorimetry, m_transferFunction, 0, 0, m_maxFrameAverageBrightness, m_maxPeakBrightness, 0);
priv->pending->colorDescriptionIsSet = true;
}
}

View file

@ -22,7 +22,7 @@
namespace KWin
{
static const quint32 s_version = 5;
static const quint32 s_version = 6;
static QtWaylandServer::kde_output_device_v2::transform kwinTransformToOutputDeviceTransform(OutputTransform transform)
{
@ -102,6 +102,9 @@ public:
void sendWideColorGamut(Resource *resource);
void sendAutoRotationPolicy(Resource *resource);
void sendIccProfilePath(Resource *resource);
void sendBrightnessMetadata(Resource *resource);
void sendBrightnessOverrides(Resource *resource);
void sendSdrGamutWideness(Resource *resource);
OutputDeviceV2Interface *q;
QPointer<Display> m_display;
@ -130,6 +133,13 @@ public:
bool m_wideColorGamut = false;
auto_rotate_policy m_autoRotation = auto_rotate_policy::auto_rotate_policy_in_tablet_mode;
QString m_iccProfilePath;
double m_maxPeakBrightness = 0;
double m_maxAverageBrightness = 0;
double m_minBrightness = 0;
double m_sdrGamutWideness = 0;
std::optional<double> m_maxPeakBrightnessOverride;
std::optional<double> m_maxAverageBrightnessOverride;
std::optional<double> m_minBrightnessOverride;
protected:
void kde_output_device_v2_bind_resource(Resource *resource) override;
@ -212,6 +222,9 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, Output *handl
updateWideColorGamut();
updateAutoRotate();
updateIccProfilePath();
updateBrightnessMetadata();
updateBrightnessOverrides();
updateSdrGamutWideness();
connect(handle, &Output::geometryChanged,
this, &OutputDeviceV2Interface::updateGlobalPosition);
@ -238,6 +251,8 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, Output *handl
connect(handle, &Output::wideColorGamutChanged, this, &OutputDeviceV2Interface::updateWideColorGamut);
connect(handle, &Output::autoRotationPolicyChanged, this, &OutputDeviceV2Interface::updateAutoRotate);
connect(handle, &Output::iccProfileChanged, this, &OutputDeviceV2Interface::updateIccProfilePath);
connect(handle, &Output::brightnessMetadataChanged, this, &OutputDeviceV2Interface::updateBrightnessMetadata);
connect(handle, &Output::sdrGamutWidenessChanged, this, &OutputDeviceV2Interface::updateSdrGamutWideness);
}
OutputDeviceV2Interface::~OutputDeviceV2Interface()
@ -293,6 +308,8 @@ void OutputDeviceV2InterfacePrivate::kde_output_device_v2_bind_resource(Resource
sendWideColorGamut(resource);
sendAutoRotationPolicy(resource);
sendIccProfilePath(resource);
sendBrightnessMetadata(resource);
sendSdrGamutWideness(resource);
sendDone(resource);
}
@ -425,6 +442,27 @@ void OutputDeviceV2InterfacePrivate::sendIccProfilePath(Resource *resource)
}
}
void OutputDeviceV2InterfacePrivate::sendBrightnessMetadata(Resource *resource)
{
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_METADATA_SINCE_VERSION) {
send_brightness_metadata(resource->handle, std::round(m_maxPeakBrightness), std::round(m_maxAverageBrightness), std::round(m_minBrightness * 10'000));
}
}
void OutputDeviceV2InterfacePrivate::sendBrightnessOverrides(Resource *resource)
{
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_OVERRIDES_SINCE_VERSION) {
send_brightness_overrides(resource->handle, std::round(m_maxPeakBrightnessOverride.value_or(-1)), std::round(m_maxAverageBrightnessOverride.value_or(-1)), std::round(m_minBrightnessOverride.value_or(-0.000'1) * 10'000));
}
}
void OutputDeviceV2InterfacePrivate::sendSdrGamutWideness(Resource *resource)
{
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_SDR_GAMUT_WIDENESS_SINCE_VERSION) {
send_sdr_gamut_wideness(resource->handle, std::clamp<uint32_t>(m_sdrGamutWideness * 10'000, 0, 10'000));
}
}
void OutputDeviceV2Interface::updateGeometry()
{
const auto clientResources = d->resourceMap();
@ -706,6 +744,46 @@ void OutputDeviceV2Interface::updateIccProfilePath()
}
}
void OutputDeviceV2Interface::updateBrightnessMetadata()
{
if (d->m_maxPeakBrightness != d->m_handle->maxPeakBrightness() || d->m_maxAverageBrightness != d->m_handle->maxAverageBrightness() || d->m_minBrightness != d->m_handle->minBrightness()) {
d->m_maxPeakBrightness = d->m_handle->maxPeakBrightness();
d->m_maxAverageBrightness = d->m_handle->maxAverageBrightness();
d->m_minBrightness = d->m_handle->minBrightness();
const auto clientResources = d->resourceMap();
for (const auto &resource : clientResources) {
d->sendBrightnessMetadata(resource);
d->sendDone(resource);
}
}
}
void OutputDeviceV2Interface::updateBrightnessOverrides()
{
if (d->m_maxPeakBrightnessOverride != d->m_handle->maxPeakBrightnessOverride() || d->m_maxAverageBrightnessOverride != d->m_handle->maxAverageBrightnessOverride() || d->m_minBrightnessOverride != d->m_handle->minBrightnessOverride()) {
d->m_maxPeakBrightnessOverride = d->m_handle->maxPeakBrightnessOverride();
d->m_maxAverageBrightnessOverride = d->m_handle->maxAverageBrightnessOverride();
d->m_minBrightnessOverride = d->m_handle->minBrightnessOverride();
const auto clientResources = d->resourceMap();
for (const auto &resource : clientResources) {
d->sendBrightnessOverrides(resource);
d->sendDone(resource);
}
}
}
void OutputDeviceV2Interface::updateSdrGamutWideness()
{
if (d->m_sdrGamutWideness != d->m_handle->sdrGamutWideness()) {
d->m_sdrGamutWideness = d->m_handle->sdrGamutWideness();
const auto clientResources = d->resourceMap();
for (const auto &resource : clientResources) {
d->sendSdrGamutWideness(resource);
d->sendDone(resource);
}
}
}
OutputDeviceV2Interface *OutputDeviceV2Interface::get(wl_resource *native)
{
if (auto devicePrivate = resource_cast<OutputDeviceV2InterfacePrivate *>(native); devicePrivate && !devicePrivate->isGlobalRemoved()) {

View file

@ -74,6 +74,9 @@ private:
void updateWideColorGamut();
void updateAutoRotate();
void updateIccProfilePath();
void updateBrightnessMetadata();
void updateBrightnessOverrides();
void updateSdrGamutWideness();
std::unique_ptr<OutputDeviceV2InterfacePrivate> d;
};

View file

@ -23,7 +23,7 @@
namespace KWin
{
static const quint32 s_version = 6;
static const quint32 s_version = 7;
class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
{
@ -64,6 +64,8 @@ protected:
void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override;
void kde_output_configuration_v2_set_auto_rotate_policy(Resource *resource, wl_resource *outputdevice, uint32_t auto_rotation_policy) override;
void kde_output_configuration_v2_set_icc_profile_path(Resource *resource, wl_resource *outputdevice, const QString &profile_path) override;
void kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness) override;
void kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness) override;
};
OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
@ -295,6 +297,28 @@ void OutputConfigurationV2Interface::kde_output_configuration_v2_set_icc_profile
}
}
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_brightness_overrides(Resource *resource, wl_resource *outputdevice, int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness)
{
if (invalid) {
return;
}
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
config.changeSet(output->handle())->maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional<double>(max_peak_brightness);
config.changeSet(output->handle())->maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional<double>(max_average_brightness);
config.changeSet(output->handle())->minBrightnessOverride = min_brightness == -1 ? std::nullopt : std::optional<double>(min_brightness / 10'000.0);
}
}
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_gamut_wideness(Resource *resource, wl_resource *outputdevice, uint32_t gamut_wideness)
{
if (invalid) {
return;
}
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
config.changeSet(output->handle())->sdrGamutWideness = gamut_wideness / 10'000.0;
}
}
void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);