From cf7e3aa448f8c9e0d9e8f407e6ed730da55acc69 Mon Sep 17 00:00:00 2001 From: UjinT34 <41110182+UjinT34@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:33:09 +0300 Subject: [PATCH] renderer/cm: Add automatic hdr (#9785) --- src/config/ConfigDescriptions.hpp | 6 ++ src/config/ConfigManager.cpp | 1 + src/helpers/Monitor.cpp | 107 ++++++++++++++++-------------- src/helpers/Monitor.hpp | 1 + src/render/Renderer.cpp | 47 ++++++++----- 5 files changed, 94 insertions(+), 68 deletions(-) diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 5ff88ea0..1af5a3ca 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1482,6 +1482,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, + SConfigOptionDescription{ + .value = "render:cm_auto_hdr", + .description = "Auto-switch to hdr mode when fullscreen app is in hdr, 0 - off, 1 - hdr, 2 - hdredid (cm_fs_passthrough can switch to hdr even when this setting is off)", + .type = CONFIG_OPTION_INT, + .data = SConfigOptionDescription::SRangeData{.value = 1, .min = 0, .max = 2}, + }, /* * cursor: diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 739fdd79..5ebb7a26 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -748,6 +748,7 @@ CConfigManager::CConfigManager() { registerConfigVar("render:cm_fs_passthrough", Hyprlang::INT{2}); registerConfigVar("render:cm_enabled", Hyprlang::INT{1}); registerConfigVar("render:send_content_type", Hyprlang::INT{1}); + registerConfigVar("render:cm_auto_hdr", Hyprlang::INT{1}); registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index df9c4a8e..db06a49d 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -419,6 +419,58 @@ void CMonitor::onDisconnect(bool destroy) { std::erase_if(g_pCompositor->m_monitors, [&](PHLMONITOR& el) { return el.get() == this; }); } +void CMonitor::applyCMType(eCMType cmType) { + switch (cmType) { + case CM_SRGB: m_imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB + case CM_WIDE: + m_imageDescription = {.primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)}; + break; + case CM_EDID: + m_imageDescription = {.primariesNameSet = false, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = { + .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, + .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y}, + .blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y}, + .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y}, + }}; + break; + case CM_HDR: + m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = 0, .max = 10000, .reference = 203}}; + break; + case CM_HDR_EDID: + m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = false, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = m_output->parsedEDID.chromaticityCoords.has_value() ? + NColorManagement::SPCPRimaries{ + .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, + .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y}, + .blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y}, + .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y}, + } : + NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance, + .max = m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance, + .reference = m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}}; + + break; + default: UNREACHABLE(); + } + if (m_minLuminance >= 0) + m_imageDescription.luminances.min = m_minLuminance; + if (m_maxLuminance >= 0) + m_imageDescription.luminances.max = m_maxLuminance; + if (m_maxAvgLuminance >= 0) + m_imageDescription.luminances.reference = m_maxAvgLuminance; +} + bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { static auto PDISABLESCALECHECKS = CConfigValue("debug:disable_scale_checks"); @@ -748,62 +800,15 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { case CM_HDR_EDID: m_cmType = supportsHDR() ? m_cmType : CM_SRGB; break; default: break; } - switch (m_cmType) { - case CM_SRGB: m_imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB - case CM_WIDE: - m_imageDescription = {.primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)}; - break; - case CM_EDID: - m_imageDescription = {.primariesNameSet = false, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = { - .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, - .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y}, - .blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y}, - .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y}, - }}; - break; - case CM_HDR: - m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .luminances = {.min = 0, .max = 10000, .reference = 203}}; - break; - case CM_HDR_EDID: - m_imageDescription = {.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = false, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = m_output->parsedEDID.chromaticityCoords.has_value() ? - NColorManagement::SPCPRimaries{ - .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, - .green = {.x = m_output->parsedEDID.chromaticityCoords->green.x, .y = m_output->parsedEDID.chromaticityCoords->green.y}, - .blue = {.x = m_output->parsedEDID.chromaticityCoords->blue.x, .y = m_output->parsedEDID.chromaticityCoords->blue.y}, - .white = {.x = m_output->parsedEDID.chromaticityCoords->white.x, .y = m_output->parsedEDID.chromaticityCoords->white.y}, - } : - NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .luminances = {.min = m_output->parsedEDID.hdrMetadata->desiredContentMinLuminance, - .max = m_output->parsedEDID.hdrMetadata->desiredContentMaxLuminance, - .reference = m_output->parsedEDID.hdrMetadata->desiredMaxFrameAverageLuminance}}; - - break; - default: UNREACHABLE(); - } m_sdrMinLuminance = RULE->sdrMinLuminance; m_sdrMaxLuminance = RULE->sdrMaxLuminance; - m_minLuminance = RULE->minLuminance; - if (m_minLuminance >= 0) - m_imageDescription.luminances.min = m_minLuminance; - m_maxLuminance = RULE->maxLuminance; - if (m_maxLuminance >= 0) - m_imageDescription.luminances.max = m_maxLuminance; + m_minLuminance = RULE->minLuminance; + m_maxLuminance = RULE->maxLuminance; m_maxAvgLuminance = RULE->maxAvgLuminance; - if (m_maxAvgLuminance >= 0) - m_imageDescription.luminances.reference = m_maxAvgLuminance; + + applyCMType(m_cmType); if (oldImageDescription != m_imageDescription) PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self); diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 0f35a423..ad7ecf31 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -200,6 +200,7 @@ class CMonitor { // methods void onConnect(bool noRule); void onDisconnect(bool destroy = false); + void applyCMType(eCMType cmType); bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); void addDamage(const pixman_region32_t* rg); void addDamage(const CRegion& rg); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index 419ebd74..34347277 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -26,6 +26,7 @@ #include "../hyprerror/HyprError.hpp" #include "../debug/HyprDebugOverlay.hpp" #include "../debug/HyprNotificationOverlay.hpp" +#include "helpers/Monitor.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" @@ -1489,9 +1490,13 @@ static hdr_output_metadata createHDRMetadata(SImageDescription settings, A } bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { - static auto PCT = CConfigValue("render:send_content_type"); - static auto PPASS = CConfigValue("render:cm_fs_passthrough"); - const bool PHDR = pMonitor->m_imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ; + static auto PCT = CConfigValue("render:send_content_type"); + static auto PPASS = CConfigValue("render:cm_fs_passthrough"); + static auto PAUTOHDR = CConfigValue("render:cm_auto_hdr"); + + const bool configuredHDR = (pMonitor->m_cmType == CM_HDR_EDID || pMonitor->m_cmType == CM_HDR); + const bool hdsIsActive = pMonitor->m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2; + bool wantHDR = configuredHDR; if (pMonitor->supportsHDR()) { // HDR metadata determined by @@ -1504,32 +1509,40 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { // fullscreen SDR surface: monitor settings // fullscreen HDR surface: surface settings - bool wantHDR = PHDR; bool hdrIsHandled = false; - if (*PPASS && pMonitor->m_activeWorkspace && pMonitor->m_activeWorkspace->m_hasFullscreenWindow && pMonitor->m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) { + if (pMonitor->m_activeWorkspace && pMonitor->m_activeWorkspace->m_hasFullscreenWindow && pMonitor->m_activeWorkspace->m_fullscreenMode == FSMODE_FULLSCREEN) { const auto WINDOW = pMonitor->m_activeWorkspace->getFullscreenWindow(); const auto ROOT_SURF = WINDOW->m_wlSurface->resource(); const auto SURF = ROOT_SURF->findFirstPreorder([ROOT_SURF](SP surf) { return surf->m_colorManagement.valid() && surf->extends() == ROOT_SURF->extends(); }); - wantHDR = PHDR && *PPASS == 2; - - // we have a surface with image description and it's allowed by wantHDR - if (SURF && SURF->m_colorManagement.valid() && SURF->m_colorManagement->hasImageDescription() && - (!wantHDR || SURF->m_colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ)) { - bool needsHdrMetadataUpdate = SURF->m_colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW; - if (SURF->m_colorManagement->needsHdrMetadataUpdate()) - SURF->m_colorManagement->setHDRMetadata(createHDRMetadata(SURF->m_colorManagement->imageDescription(), pMonitor->m_output->parsedEDID)); - if (needsHdrMetadataUpdate) - pMonitor->m_output->state->setHDRMetadata(SURF->m_colorManagement->hdrMetadata()); - hdrIsHandled = true; + // we have a surface with image description + if (SURF && SURF->m_colorManagement.valid() && SURF->m_colorManagement->hasImageDescription()) { + const bool surfaceIsHDR = SURF->m_colorManagement->imageDescription().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ; + if (*PPASS == 1 || (*PPASS == 2 && surfaceIsHDR)) { + // passthrough + bool needsHdrMetadataUpdate = SURF->m_colorManagement->needsHdrMetadataUpdate() || pMonitor->m_previousFSWindow != WINDOW; + if (SURF->m_colorManagement->needsHdrMetadataUpdate()) + SURF->m_colorManagement->setHDRMetadata(createHDRMetadata(SURF->m_colorManagement->imageDescription(), pMonitor->m_output->parsedEDID)); + if (needsHdrMetadataUpdate) + pMonitor->m_output->state->setHDRMetadata(SURF->m_colorManagement->hdrMetadata()); + hdrIsHandled = true; + } else if (*PAUTOHDR && surfaceIsHDR) + wantHDR = true; // auto-hdr: hdr on } pMonitor->m_previousFSWindow = WINDOW; } + if (!hdrIsHandled) { - if ((pMonitor->m_output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != wantHDR) + if (hdsIsActive != wantHDR) { + if (*PAUTOHDR && !(hdsIsActive && configuredHDR)) { + // modify or restore monitor image description for auto-hdr + // FIXME ok for now, will need some other logic if monitor image description can be modified some other way + pMonitor->applyCMType(wantHDR ? (*PAUTOHDR == 2 ? CM_HDR_EDID : CM_HDR) : pMonitor->m_cmType); + } pMonitor->m_output->state->setHDRMetadata(wantHDR ? createHDRMetadata(pMonitor->m_imageDescription, pMonitor->m_output->parsedEDID) : NO_HDR_METADATA); + } pMonitor->m_previousFSWindow.reset(); } }