backends/drm: handle missing brightness values in the EDID better

Most importantly, fall back to an assumption of 1000 nits peak brightness when the
max brightness is missing, instead of the sdr brightness, which causes more immediately
visible issues.
Ideally the user should configure this value in the display settings, but it's too
late to still do that for Plasma 6.0.

BUG: 478860
wilder/Plasma/6.2
Xaver Hugl 2 years ago
parent 647790b44c
commit 4425bcd4e0
  1. 13
      src/backends/drm/drm_output.cpp
  2. 26
      src/backends/drm/drm_pipeline.cpp
  3. 8
      src/core/output.cpp
  4. 14
      src/core/output.h
  5. 29
      src/utils/edid.cpp
  6. 23
      src/utils/edid.h
  7. 6
      src/wayland/outputdevice_v2.cpp

@ -59,11 +59,10 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
capabilities |= Capability::RgbRange; capabilities |= Capability::RgbRange;
initialState.rgbRange = DrmConnector::broadcastRgbToRgbRange(conn->broadcastRGB.enumValue()); initialState.rgbRange = DrmConnector::broadcastRgbToRgbRange(conn->broadcastRGB.enumValue());
} }
if (m_connector->hdrMetadata.isValid() && m_connector->edid() && m_connector->edid()->hdrMetadata() && m_connector->edid()->hdrMetadata()->supportsPQ) { if (m_connector->hdrMetadata.isValid() && m_connector->edid()->supportsPQ()) {
capabilities |= Capability::HighDynamicRange; capabilities |= Capability::HighDynamicRange;
} }
if (m_connector->colorspace.isValid() && m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB) if (m_connector->colorspace.isValid() && m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB) && m_connector->edid()->supportsBT2020()) {
&& m_connector->edid() && m_connector->edid()->hdrMetadata() && m_connector->edid()->hdrMetadata()->supportsBT2020) {
capabilities |= Capability::WideColorGamut; capabilities |= Capability::WideColorGamut;
} }
if (conn->isInternal()) { if (conn->isInternal()) {
@ -72,8 +71,6 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
} }
const Edid *edid = conn->edid(); const Edid *edid = conn->edid();
const auto hdrData = edid->hdrMetadata();
setInformation(Information{ setInformation(Information{
.name = conn->connectorName(), .name = conn->connectorName(),
.manufacturer = edid->manufacturerString(), .manufacturer = edid->manufacturerString(),
@ -88,9 +85,9 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
.internal = conn->isInternal(), .internal = conn->isInternal(),
.nonDesktop = conn->isNonDesktop(), .nonDesktop = conn->isNonDesktop(),
.mstPath = conn->mstPath(), .mstPath = conn->mstPath(),
.maxPeakBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredContentMaxLuminance : 0, .maxPeakBrightness = edid->desiredMaxLuminance(),
.maxAverageBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredMaxFrameAverageLuminance : 0, .maxAverageBrightness = edid->desiredMaxFrameAverageLuminance(),
.minBrightness = hdrData && hdrData->hasValidBrightnessValues ? hdrData->desiredContentMinLuminance : 0, .minBrightness = edid->desiredMinLuminance(),
}); });
initialState.modes = getModes(); initialState.modes = getModes();

@ -769,13 +769,11 @@ ColorDescription DrmPipeline::createColorDescription() const
{ {
if (m_pending.transferFunction == NamedTransferFunction::PerceptualQuantizer && m_connector->edid()) { if (m_pending.transferFunction == NamedTransferFunction::PerceptualQuantizer && m_connector->edid()) {
const auto colorimetry = m_pending.BT2020 ? NamedColorimetry::BT2020 : NamedColorimetry::BT709; 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,
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, hdr->desiredContentMinLuminance, hdr->desiredMaxFrameAverageLuminance, hdr->desiredContentMaxLuminance, m_pending.sdrGamutWideness); m_pending.minBrightnessOverride.value_or(m_connector->edid()->desiredMinLuminance()),
} else if (m_pending.peakBrightnessOverride && m_pending.averageBrightnessOverride) { m_pending.averageBrightnessOverride.value_or(m_connector->edid()->desiredMaxFrameAverageLuminance().value_or(m_pending.sdrBrightness)),
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, m_pending.minBrightnessOverride.value_or(0), *m_pending.averageBrightnessOverride, *m_pending.peakBrightnessOverride, m_pending.sdrGamutWideness); m_pending.peakBrightnessOverride.value_or(m_connector->edid()->desiredMaxLuminance().value_or(1000)),
} else { m_pending.sdrGamutWideness);
return ColorDescription(colorimetry, m_pending.transferFunction, m_pending.sdrBrightness, 0, m_pending.sdrBrightness, m_pending.sdrBrightness, m_pending.sdrGamutWideness);
}
} else if (m_pending.iccProfile) { } else if (m_pending.iccProfile) {
return ColorDescription(m_pending.iccProfile->colorimetry(), NamedTransferFunction::sRGB, 200, 0, 200, 200, 0); return ColorDescription(m_pending.iccProfile->colorimetry(), NamedTransferFunction::sRGB, 200, 0, 200, 200, 0);
} else { } else {
@ -789,11 +787,7 @@ std::shared_ptr<DrmBlob> DrmPipeline::createHdrMetadata(NamedTransferFunction tr
// for sRGB / gamma 2.2, don't send any metadata, to ensure the non-HDR experience stays the same // for sRGB / gamma 2.2, don't send any metadata, to ensure the non-HDR experience stays the same
return nullptr; return nullptr;
} }
if (!m_connector->edid() || !m_connector->edid()->hdrMetadata()) { if (!m_connector->edid()->supportsPQ()) {
return nullptr;
}
const auto metadata = *m_connector->edid()->hdrMetadata();
if (!metadata.supportsPQ) {
return nullptr; return nullptr;
} }
const auto colorimetry = m_connector->edid()->colorimetry(); const auto colorimetry = m_connector->edid()->colorimetry();
@ -820,12 +814,12 @@ std::shared_ptr<DrmBlob> DrmPipeline::createHdrMetadata(NamedTransferFunction tr
}, },
.white_point = {to16Bit(colorimetry.white.x()), to16Bit(colorimetry.white.y())}, .white_point = {to16Bit(colorimetry.white.x()), to16Bit(colorimetry.white.y())},
// in nits // in nits
.max_display_mastering_luminance = uint16_t(std::round(metadata.desiredMaxFrameAverageLuminance)), .max_display_mastering_luminance = uint16_t(std::round(m_connector->edid()->desiredMaxFrameAverageLuminance().value_or(0))),
// in 0.0001 nits // in 0.0001 nits
.min_display_mastering_luminance = uint16_t(std::round(metadata.desiredContentMinLuminance * 10000)), .min_display_mastering_luminance = uint16_t(std::round(m_connector->edid()->desiredMaxLuminance().value_or(0) * 10000)),
// in nits // in nits
.max_cll = uint16_t(std::round(metadata.desiredMaxFrameAverageLuminance)), .max_cll = uint16_t(std::round(m_connector->edid()->desiredMaxFrameAverageLuminance().value_or(0))),
.max_fall = uint16_t(std::round(metadata.desiredMaxFrameAverageLuminance)), .max_fall = uint16_t(std::round(m_connector->edid()->desiredMaxFrameAverageLuminance().value_or(0))),
}, },
}; };
return DrmBlob::create(gpu(), &data, sizeof(data)); return DrmBlob::create(gpu(), &data, sizeof(data));

@ -624,14 +624,14 @@ const ColorDescription &Output::colorDescription() const
return m_state.colorDescription; return m_state.colorDescription;
} }
double Output::maxPeakBrightness() const std::optional<double> Output::maxPeakBrightness() const
{ {
return m_state.maxPeakBrightnessOverride.value_or(m_information.maxPeakBrightness); return m_state.maxPeakBrightnessOverride ? m_state.maxPeakBrightnessOverride : m_information.maxPeakBrightness;
} }
double Output::maxAverageBrightness() const std::optional<double> Output::maxAverageBrightness() const
{ {
return m_state.maxAverageBrightnessOverride.value_or(m_information.maxAverageBrightness); return m_state.maxAverageBrightnessOverride ? *m_state.maxAverageBrightnessOverride : m_information.maxAverageBrightness;
} }
double Output::minBrightness() const double Output::minBrightness() const

@ -341,8 +341,8 @@ public:
virtual bool updateCursorLayer(); virtual bool updateCursorLayer();
double maxPeakBrightness() const; std::optional<double> maxPeakBrightness() const;
double maxAverageBrightness() const; std::optional<double> maxAverageBrightness() const;
double minBrightness() const; double minBrightness() const;
std::optional<double> maxPeakBrightnessOverride() const; std::optional<double> maxPeakBrightnessOverride() const;
std::optional<double> maxAverageBrightnessOverride() const; std::optional<double> maxAverageBrightnessOverride() const;
@ -431,8 +431,8 @@ protected:
bool placeholder = false; bool placeholder = false;
bool nonDesktop = false; bool nonDesktop = false;
QByteArray mstPath; QByteArray mstPath;
double maxPeakBrightness = 0; std::optional<double> maxPeakBrightness;
double maxAverageBrightness = 0; std::optional<double> maxAverageBrightness;
double minBrightness = 0; double minBrightness = 0;
}; };
@ -456,9 +456,9 @@ protected:
QString iccProfilePath; QString iccProfilePath;
std::shared_ptr<IccProfile> iccProfile; std::shared_ptr<IccProfile> iccProfile;
ColorDescription colorDescription = ColorDescription::sRGB; ColorDescription colorDescription = ColorDescription::sRGB;
std::optional<float> maxPeakBrightnessOverride; std::optional<double> maxPeakBrightnessOverride;
std::optional<float> maxAverageBrightnessOverride; std::optional<double> maxAverageBrightnessOverride;
std::optional<float> minBrightnessOverride; std::optional<double> minBrightnessOverride;
double sdrGamutWideness = 0; double sdrGamutWideness = 0;
}; };

@ -166,10 +166,9 @@ Edid::Edid(const void *data, uint32_t size)
} }
if (hdr_static_metadata) { if (hdr_static_metadata) {
m_hdrMetadata = HDRMetadata{ m_hdrMetadata = HDRMetadata{
.hasValidBrightnessValues = hdr_static_metadata->desired_content_min_luminance > 0 && hdr_static_metadata->desired_content_max_luminance > 0 && hdr_static_metadata->desired_content_max_frame_avg_luminance,
.desiredContentMinLuminance = hdr_static_metadata->desired_content_min_luminance, .desiredContentMinLuminance = hdr_static_metadata->desired_content_min_luminance,
.desiredContentMaxLuminance = hdr_static_metadata->desired_content_max_luminance, .desiredContentMaxLuminance = hdr_static_metadata->desired_content_max_luminance > 0 ? std::make_optional(hdr_static_metadata->desired_content_max_luminance) : std::nullopt,
.desiredMaxFrameAverageLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance, .desiredMaxFrameAverageLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance > 0 ? std::make_optional(hdr_static_metadata->desired_content_max_frame_avg_luminance) : std::nullopt,
.supportsPQ = hdr_static_metadata->eotfs->pq, .supportsPQ = hdr_static_metadata->eotfs->pq,
.supportsBT2020 = colorimetry && colorimetry->bt2020_rgb, .supportsBT2020 = colorimetry && colorimetry->bt2020_rgb,
}; };
@ -252,9 +251,29 @@ Colorimetry Edid::colorimetry() const
return m_colorimetry; return m_colorimetry;
} }
std::optional<Edid::HDRMetadata> Edid::hdrMetadata() const double Edid::desiredMinLuminance() const
{ {
return m_hdrMetadata; return m_hdrMetadata ? m_hdrMetadata->desiredContentMinLuminance : 0;
}
std::optional<double> Edid::desiredMaxFrameAverageLuminance() const
{
return m_hdrMetadata ? m_hdrMetadata->desiredMaxFrameAverageLuminance : std::nullopt;
}
std::optional<double> Edid::desiredMaxLuminance() const
{
return m_hdrMetadata ? m_hdrMetadata->desiredContentMaxLuminance : std::nullopt;
}
bool Edid::supportsPQ() const
{
return m_hdrMetadata && m_hdrMetadata->supportsPQ;
}
bool Edid::supportsBT2020() const
{
return m_hdrMetadata && m_hdrMetadata->supportsBT2020;
} }
QByteArray Edid::identifier() const QByteArray Edid::identifier() const

@ -81,16 +81,11 @@ public:
Colorimetry colorimetry() const; Colorimetry colorimetry() const;
struct HDRMetadata double desiredMinLuminance() const;
{ std::optional<double> desiredMaxFrameAverageLuminance() const;
bool hasValidBrightnessValues; std::optional<double> desiredMaxLuminance() const;
float desiredContentMinLuminance; bool supportsPQ() const;
float desiredContentMaxLuminance; bool supportsBT2020() const;
float desiredMaxFrameAverageLuminance;
bool supportsPQ;
bool supportsBT2020;
};
std::optional<HDRMetadata> hdrMetadata() const;
/** /**
* @returns a string that is intended to identify the monitor uniquely. * @returns a string that is intended to identify the monitor uniquely.
@ -106,6 +101,14 @@ private:
QByteArray m_serialNumber; QByteArray m_serialNumber;
QString m_hash; QString m_hash;
Colorimetry m_colorimetry; Colorimetry m_colorimetry;
struct HDRMetadata
{
double desiredContentMinLuminance;
std::optional<double> desiredContentMaxLuminance;
std::optional<double> desiredMaxFrameAverageLuminance;
bool supportsPQ;
bool supportsBT2020;
};
std::optional<HDRMetadata> m_hdrMetadata; std::optional<HDRMetadata> m_hdrMetadata;
QByteArray m_identifier; QByteArray m_identifier;

@ -133,8 +133,8 @@ public:
bool m_wideColorGamut = false; bool m_wideColorGamut = false;
auto_rotate_policy m_autoRotation = auto_rotate_policy::auto_rotate_policy_in_tablet_mode; auto_rotate_policy m_autoRotation = auto_rotate_policy::auto_rotate_policy_in_tablet_mode;
QString m_iccProfilePath; QString m_iccProfilePath;
double m_maxPeakBrightness = 0; std::optional<double> m_maxPeakBrightness;
double m_maxAverageBrightness = 0; std::optional<double> m_maxAverageBrightness;
double m_minBrightness = 0; double m_minBrightness = 0;
double m_sdrGamutWideness = 0; double m_sdrGamutWideness = 0;
std::optional<double> m_maxPeakBrightnessOverride; std::optional<double> m_maxPeakBrightnessOverride;
@ -445,7 +445,7 @@ void OutputDeviceV2InterfacePrivate::sendIccProfilePath(Resource *resource)
void OutputDeviceV2InterfacePrivate::sendBrightnessMetadata(Resource *resource) void OutputDeviceV2InterfacePrivate::sendBrightnessMetadata(Resource *resource)
{ {
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_METADATA_SINCE_VERSION) { 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)); send_brightness_metadata(resource->handle, std::round(m_maxPeakBrightness.value_or(0)), std::round(m_maxAverageBrightness.value_or(0)), std::round(m_minBrightness * 10'000));
} }
} }

Loading…
Cancel
Save