backends/drm: add support for an HDR brightness setting

This commit is contained in:
Xaver Hugl 2024-05-08 20:04:39 +02:00
parent 589479c24a
commit 22d0631b1b
12 changed files with 78 additions and 9 deletions

View file

@ -54,7 +54,7 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::doBeginFrame()
// as the hardware cursor is more important than an incorrectly blended cursor edge
m_scanoutBuffer.reset();
return m_surface.startRendering(targetRect().size(), m_pipeline->output()->transform().combine(OutputTransform::FlipY), m_pipeline->formats(m_type), m_pipeline->colorDescription(), m_pipeline->output()->channelFactors(), m_pipeline->iccProfile(), m_pipeline->output()->needsColormanagement());
return m_surface.startRendering(targetRect().size(), m_pipeline->output()->transform().combine(OutputTransform::FlipY), m_pipeline->formats(m_type), m_pipeline->colorDescription(), m_pipeline->output()->channelFactors(), m_pipeline->iccProfile(), m_pipeline->output()->needsColormanagement(), m_pipeline->output()->brightness());
}
bool EglGbmLayer::doEndFrame(const QRegion &renderedRegion, const QRegion &damagedRegion, OutputFrame *frame)

View file

@ -74,7 +74,7 @@ void EglGbmLayerSurface::destroyResources()
m_oldSurface = {};
}
std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, OutputTransform transformation, const QHash<uint32_t, QList<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr<IccProfile> &iccProfile, bool enableColormanagement)
std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, OutputTransform transformation, const QHash<uint32_t, QList<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr<IccProfile> &iccProfile, bool enableColormanagement, double brightness)
{
if (!checkSurface(bufferSize, formats)) {
return std::nullopt;
@ -96,12 +96,14 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(cons
m_surface->currentSlot = slot;
if (m_surface->targetColorDescription != colorDescription || m_surface->channelFactors != channelFactors
|| m_surface->colormanagementEnabled != enableColormanagement || m_surface->iccProfile != iccProfile) {
|| m_surface->colormanagementEnabled != enableColormanagement || m_surface->iccProfile != iccProfile
|| m_surface->brightness != brightness) {
m_surface->damageJournal.clear();
m_surface->colormanagementEnabled = enableColormanagement;
m_surface->targetColorDescription = colorDescription;
m_surface->channelFactors = channelFactors;
m_surface->iccProfile = iccProfile;
m_surface->brightness = brightness;
if (iccProfile) {
if (!m_surface->iccShader) {
m_surface->iccShader = std::make_unique<IccShader>();
@ -185,10 +187,14 @@ bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion, OutputFrame
if (m_surface->iccShader) {
m_surface->iccShader->setUniforms(m_surface->iccProfile, m_surface->intermediaryColorDescription.sdrBrightness(), m_surface->channelFactors);
} else {
// enforce a 25 nits minimum sdr brightness
constexpr double minBrightness = 25;
const double sdrBrightness = m_surface->intermediaryColorDescription.sdrBrightness();
const double brightnessFactor = (m_surface->brightness * (1 - (minBrightness / sdrBrightness))) + (minBrightness / sdrBrightness);
QMatrix4x4 ctm;
ctm(0, 0) = m_surface->channelFactors.x();
ctm(1, 1) = m_surface->channelFactors.y();
ctm(2, 2) = m_surface->channelFactors.z();
ctm(0, 0) = m_surface->channelFactors.x() * brightnessFactor;
ctm(1, 1) = m_surface->channelFactors.y() * brightnessFactor;
ctm(2, 2) = m_surface->channelFactors.z() * brightnessFactor;
binder.shader()->setUniform(GLShader::Mat4Uniform::ColorimetryTransformation, ctm);
binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(m_surface->intermediaryColorDescription.transferFunction()));
binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(m_surface->targetColorDescription.transferFunction()));

View file

@ -56,7 +56,7 @@ public:
EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target = BufferTarget::Normal, FormatOption formatOption = FormatOption::PreferAlpha);
~EglGbmLayerSurface();
std::optional<OutputLayerBeginFrameInfo> startRendering(const QSize &bufferSize, OutputTransform transformation, const QHash<uint32_t, QList<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr<IccProfile> &iccProfile, bool enableColormanagement);
std::optional<OutputLayerBeginFrameInfo> startRendering(const QSize &bufferSize, OutputTransform transformation, const QHash<uint32_t, QList<uint64_t>> &formats, const ColorDescription &colorDescription, const QVector3D &channelFactors, const std::shared_ptr<IccProfile> &iccProfile, bool enableColormanagement, double brightness);
bool endRendering(const QRegion &damagedRegion, OutputFrame *frame);
bool doesSurfaceFit(const QSize &size, const QHash<uint32_t, QList<uint64_t>> &formats) const;
@ -101,6 +101,7 @@ private:
ColorDescription targetColorDescription = ColorDescription::sRGB;
ColorDescription intermediaryColorDescription = ColorDescription::sRGB;
QVector3D channelFactors = {1, 1, 1};
double brightness = 1.0;
std::unique_ptr<IccShader> iccShader;
std::shared_ptr<IccProfile> iccProfile;

View file

@ -402,6 +402,7 @@ void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props
next.colorDescription = m_pipeline->colorDescription();
next.vrrPolicy = props->vrrPolicy.value_or(m_state.vrrPolicy);
next.colorProfileSource = props->colorProfileSource.value_or(m_state.colorProfileSource);
next.brightness = props->brightness.value_or(m_state.brightness);
setState(next);
if (!isEnabled() && m_pipeline->needsModeset()) {

View file

@ -610,6 +610,9 @@ void Output::setState(const State &state)
if (oldState.colorProfileSource != state.colorProfileSource) {
Q_EMIT colorProfileSourceChanged();
}
if (oldState.brightness != state.brightness) {
Q_EMIT brightnessChanged();
}
if (oldState.enabled != state.enabled) {
Q_EMIT enabledChanged();
}
@ -761,6 +764,11 @@ Output::ColorProfileSource Output::colorProfileSource() const
{
return m_state.colorProfileSource;
}
double Output::brightness() const
{
return m_state.brightness;
}
} // namespace KWin
#include "moc_output.cpp"

View file

@ -358,6 +358,8 @@ public:
double sdrGamutWideness() const;
ColorProfileSource colorProfileSource() const;
double brightness() const;
const ColorDescription &colorDescription() const;
Q_SIGNALS:
@ -423,6 +425,7 @@ Q_SIGNALS:
void sdrGamutWidenessChanged();
void colorDescriptionChanged();
void colorProfileSourceChanged();
void brightnessChanged();
protected:
struct Information
@ -472,6 +475,7 @@ protected:
std::optional<double> minBrightnessOverride;
double sdrGamutWideness = 0;
VrrPolicy vrrPolicy = VrrPolicy::Automatic;
double brightness = 1.0;
};
void setInformation(const Information &information);

View file

@ -43,6 +43,7 @@ public:
std::optional<std::optional<double>> minBrightnessOverride;
std::optional<double> sdrGamutWideness;
std::optional<Output::ColorProfileSource> colorProfileSource;
std::optional<double> brightness;
};
class KWIN_EXPORT OutputConfiguration

View file

@ -229,6 +229,7 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.maxAverageBrightnessOverride = changeSet->maxAverageBrightnessOverride.value_or(output->maxAverageBrightnessOverride()),
.minBrightnessOverride = changeSet->minBrightnessOverride.value_or(output->minBrightnessOverride()),
.sdrGamutWideness = changeSet->sdrGamutWideness.value_or(output->sdrGamutWideness()),
.brightness = changeSet->brightness.value_or(output->brightness()),
};
*outputIt = SetupState{
.outputIndex = *outputIndex,
@ -263,6 +264,7 @@ void OutputConfigurationStore::storeConfig(const QList<Output *> &allOutputs, bo
.maxAverageBrightnessOverride = output->maxAverageBrightnessOverride(),
.minBrightnessOverride = output->minBrightnessOverride(),
.sdrGamutWideness = output->sdrGamutWideness(),
.brightness = output->brightness(),
};
*outputIt = SetupState{
.outputIndex = *outputIndex,
@ -311,6 +313,7 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::setupT
.minBrightnessOverride = state.minBrightnessOverride,
.sdrGamutWideness = state.sdrGamutWideness,
.colorProfileSource = state.colorProfileSource,
.brightness = state.brightness,
};
if (setupState.enabled) {
priorities.push_back(std::make_pair(output, setupState.priority));
@ -432,6 +435,7 @@ std::pair<OutputConfiguration, QList<Output *>> OutputConfigurationStore::genera
.wideColorGamut = existingData.wideColorGamut.value_or(false),
.autoRotationPolicy = existingData.autoRotation.value_or(Output::AutoRotationPolicy::InTabletMode),
.colorProfileSource = existingData.colorProfileSource.value_or(Output::ColorProfileSource::sRGB),
.brightness = existingData.brightness.value_or(1.0),
};
if (enable) {
const auto modeSize = changeset->transform->map(mode->size());
@ -737,6 +741,9 @@ void OutputConfigurationStore::load()
state.colorProfileSource = Output::ColorProfileSource::sRGB;
}
}
if (const auto it = data.find("brightness"); it != data.end() && it->isDouble()) {
state.brightness = std::clamp(it->toDouble(), 0.0, 1.0);
}
outputDatas.push_back(state);
}
@ -966,6 +973,9 @@ void OutputConfigurationStore::save()
break;
}
}
if (output.brightness) {
o["brightness"] = *output.brightness;
}
outputsData.append(o);
}
outputs["data"] = outputsData;

View file

@ -82,6 +82,7 @@ private:
std::optional<double> maxAverageBrightnessOverride;
std::optional<double> minBrightnessOverride;
std::optional<double> sdrGamutWideness;
std::optional<double> brightness;
};
struct SetupState
{

View file

@ -22,7 +22,7 @@
namespace KWin
{
static const quint32 s_version = 7;
static const quint32 s_version = 8;
static QtWaylandServer::kde_output_device_v2::transform kwinTransformToOutputDeviceTransform(OutputTransform transform)
{
@ -106,6 +106,7 @@ public:
void sendBrightnessOverrides(Resource *resource);
void sendSdrGamutWideness(Resource *resource);
void sendColorProfileSource(Resource *resource);
void sendBrightness(Resource *resource);
OutputDeviceV2Interface *q;
QPointer<Display> m_display;
@ -142,6 +143,7 @@ public:
std::optional<double> m_maxAverageBrightnessOverride;
std::optional<double> m_minBrightnessOverride;
color_profile_source m_colorProfile = color_profile_source::color_profile_source_sRGB;
double m_brightness = 1.0;
protected:
void kde_output_device_v2_bind_resource(Resource *resource) override;
@ -228,6 +230,7 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, Output *handl
updateBrightnessOverrides();
updateSdrGamutWideness();
updateColorProfileSource();
updateBrightness();
connect(handle, &Output::geometryChanged,
this, &OutputDeviceV2Interface::updateGlobalPosition);
@ -258,6 +261,7 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, Output *handl
connect(handle, &Output::brightnessMetadataChanged, this, &OutputDeviceV2Interface::updateBrightnessOverrides);
connect(handle, &Output::sdrGamutWidenessChanged, this, &OutputDeviceV2Interface::updateSdrGamutWideness);
connect(handle, &Output::colorProfileSourceChanged, this, &OutputDeviceV2Interface::updateColorProfileSource);
connect(handle, &Output::brightnessChanged, this, &OutputDeviceV2Interface::updateBrightness);
}
OutputDeviceV2Interface::~OutputDeviceV2Interface()
@ -317,6 +321,7 @@ void OutputDeviceV2InterfacePrivate::kde_output_device_v2_bind_resource(Resource
sendBrightnessOverrides(resource);
sendSdrGamutWideness(resource);
sendColorProfileSource(resource);
sendBrightness(resource);
sendDone(resource);
}
@ -477,6 +482,13 @@ void OutputDeviceV2InterfacePrivate::sendColorProfileSource(Resource *resource)
}
}
void OutputDeviceV2InterfacePrivate::sendBrightness(Resource *resource)
{
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_BRIGHTNESS_SINCE_VERSION) {
send_brightness(resource->handle, m_brightness);
}
}
void OutputDeviceV2Interface::updateGeometry()
{
const auto clientResources = d->resourceMap();
@ -821,6 +833,19 @@ void OutputDeviceV2Interface::updateColorProfileSource()
}
}
void OutputDeviceV2Interface::updateBrightness()
{
const uint32_t newBrightness = std::round(d->m_handle->brightness() * 10'000);
if (d->m_brightness != newBrightness) {
d->m_brightness = newBrightness;
const auto clientResources = d->resourceMap();
for (const auto &resource : clientResources) {
d->sendBrightness(resource);
d->sendDone(resource);
}
}
}
OutputDeviceV2Interface *OutputDeviceV2Interface::get(wl_resource *native)
{
if (auto devicePrivate = resource_cast<OutputDeviceV2InterfacePrivate *>(native); devicePrivate && !devicePrivate->isGlobalRemoved()) {

View file

@ -78,6 +78,7 @@ private:
void updateBrightnessOverrides();
void updateSdrGamutWideness();
void updateColorProfileSource();
void updateBrightness();
std::unique_ptr<OutputDeviceV2InterfacePrivate> d;
};

View file

@ -23,7 +23,7 @@
namespace KWin
{
static const quint32 s_version = 8;
static const quint32 s_version = 9;
class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
{
@ -67,6 +67,7 @@ protected:
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;
void kde_output_configuration_v2_set_color_profile_source(Resource *resource, wl_resource *outputdevice, uint32_t source) override;
void kde_output_configuration_v2_set_brightness(Resource *resource, wl_resource *outputdevice, uint32_t brightness) override;
};
OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
@ -342,6 +343,16 @@ void OutputConfigurationV2Interface::kde_output_configuration_v2_set_color_profi
}
}
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_brightness(Resource *resource, wl_resource *outputdevice, uint32_t brightness)
{
if (invalid) {
return;
}
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
config.changeSet(output->handle())->brightness = brightness / 10'000.0;
}
}
void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);