diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 60a21c8be2c..9c503f0f220 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1542,6 +1542,13 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_CHOICE, .data = SConfigOptionDescription::SChoiceData{0, "disable,always,ondemand,ignore"}, }, + SConfigOptionDescription{ + .value = "render:cm_sdr_eotf", + .description = "Default transfer function for displaying SDR apps. 0 - Treat unspecified as sRGB, 1 - Treat unspecified as Gamma 2.2, 2 - Treat " + "unspecified and sRGB as Gamma 2.2", + .type = CONFIG_OPTION_CHOICE, + .data = SConfigOptionDescription::SChoiceData{0, "srgb,gamma22,gamma22force"}, + }, /* * cursor: diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 0e88fca316e..59fc7117ab3 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -781,6 +781,7 @@ CConfigManager::CConfigManager() { registerConfigVar("render:cm_auto_hdr", Hyprlang::INT{1}); registerConfigVar("render:new_render_scheduling", Hyprlang::INT{0}); registerConfigVar("render:non_shader_cm", Hyprlang::INT{2}); + registerConfigVar("render:cm_sdr_eotf", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); @@ -842,6 +843,7 @@ CConfigManager::CConfigManager() { m_config->addSpecialConfigValue("monitorv2", "mirror", {STRVAL_EMPTY}); m_config->addSpecialConfigValue("monitorv2", "bitdepth", {STRVAL_EMPTY}); // TODO use correct type m_config->addSpecialConfigValue("monitorv2", "cm", {"auto"}); + m_config->addSpecialConfigValue("monitorv2", "sdr_eotf", Hyprlang::INT{0}); m_config->addSpecialConfigValue("monitorv2", "sdrbrightness", Hyprlang::FLOAT{1.0}); m_config->addSpecialConfigValue("monitorv2", "sdrsaturation", Hyprlang::FLOAT{1.0}); m_config->addSpecialConfigValue("monitorv2", "vrr", Hyprlang::INT{0}); @@ -1115,6 +1117,9 @@ std::optional CConfigManager::handleMonitorv2(const std::string& ou VAL = m_config->getSpecialConfigValuePtr("monitorv2", "cm", output.c_str()); if (VAL && VAL->m_bSetByUser) parser.parseCM(std::any_cast(VAL->getValue())); + VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdr_eotf", output.c_str()); + if (VAL && VAL->m_bSetByUser) + parser.rule().sdrEotf = std::any_cast(VAL->getValue()); VAL = m_config->getSpecialConfigValuePtr("monitorv2", "sdrbrightness", output.c_str()); if (VAL && VAL->m_bSetByUser) parser.rule().sdrBrightness = std::any_cast(VAL->getValue()); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 1ec035fd8be..d91ae176a91 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -465,32 +465,41 @@ void CMonitor::onDisconnect(bool destroy) { std::erase_if(g_pCompositor->m_monitors, [&](PHLMONITOR& el) { return el.get() == this; }); } -void CMonitor::applyCMType(NCMType::eCMType cmType) { - auto oldImageDescription = m_imageDescription; +void CMonitor::applyCMType(NCMType::eCMType cmType, int cmSdrEotf) { + auto oldImageDescription = m_imageDescription; + static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); + auto chosenSdrEotf = cmSdrEotf == 0 ? (*PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB) : + (cmSdrEotf == 1 ? NColorManagement::CM_TRANSFER_FUNCTION_SRGB : NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); + switch (cmType) { - case NCMType::CM_SRGB: m_imageDescription = {}; break; // assumes SImageDescirption defaults to sRGB + case NCMType::CM_SRGB: m_imageDescription = {.transferFunction = chosenSdrEotf}; break; // assumes SImageDescription defaults to sRGB case NCMType::CM_WIDE: - m_imageDescription = {.primariesNameSet = true, + m_imageDescription = {.transferFunction = chosenSdrEotf, + .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020)}; break; case NCMType::CM_DCIP3: - m_imageDescription = {.primariesNameSet = true, + m_imageDescription = {.transferFunction = chosenSdrEotf, + .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_DCI_P3, .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DCI_P3)}; break; case NCMType::CM_DP3: - m_imageDescription = {.primariesNameSet = true, + m_imageDescription = {.transferFunction = chosenSdrEotf, + .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_DISPLAY_P3, .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_DISPLAY_P3)}; break; case NCMType::CM_ADOBE: - m_imageDescription = {.primariesNameSet = true, + m_imageDescription = {.transferFunction = chosenSdrEotf, + .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_ADOBE_RGB, .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_ADOBE_RGB)}; break; case NCMType::CM_EDID: - m_imageDescription = {.primariesNameSet = false, + m_imageDescription = {.transferFunction = chosenSdrEotf, + .primariesNameSet = true, .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, .primaries = { .red = {.x = m_output->parsedEDID.chromaticityCoords->red.x, .y = m_output->parsedEDID.chromaticityCoords->red.y}, @@ -868,6 +877,8 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { default: break; } + m_sdrEotf = RULE->sdrEotf; + m_sdrMinLuminance = RULE->sdrMinLuminance; m_sdrMaxLuminance = RULE->sdrMaxLuminance; @@ -875,7 +886,7 @@ bool CMonitor::applyMonitorRule(SMonitorRule* pMonitorRule, bool force) { m_maxLuminance = RULE->maxLuminance; m_maxAvgLuminance = RULE->maxAvgLuminance; - applyCMType(m_cmType); + applyCMType(m_cmType, m_sdrEotf); m_sdrSaturation = RULE->sdrSaturation; m_sdrBrightness = RULE->sdrBrightness; diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index 687fc049f08..84de34b730e 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -48,6 +48,7 @@ struct SMonitorRule { std::string mirrorOf = ""; bool enable10bit = false; NCMType::eCMType cmType = NCMType::CM_SRGB; + int sdrEotf = 0; float sdrSaturation = 1.0f; // SDR -> HDR float sdrBrightness = 1.0f; // SDR -> HDR @@ -131,6 +132,7 @@ class CMonitor { bool m_vrrActive = false; // this can be TRUE even if VRR is not active in the case that this display does not support it. bool m_enabled10bit = false; // as above, this can be TRUE even if 10 bit failed. NCMType::eCMType m_cmType = NCMType::CM_SRGB; + int m_sdrEotf = 0; float m_sdrSaturation = 1.0f; float m_sdrBrightness = 1.0f; float m_sdrMinLuminance = 0.2f; @@ -272,7 +274,7 @@ class CMonitor { // methods void onConnect(bool noRule); void onDisconnect(bool destroy = false); - void applyCMType(NCMType::eCMType cmType); + void applyCMType(NCMType::eCMType cmType, int cmSdrEotf); bool applyMonitorRule(SMonitorRule* pMonitorRule, bool force = false); void addDamage(const pixman_region32_t* rg); void addDamage(const CRegion& rg); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 436ba26c495..f1d5ae9855b 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1552,14 +1552,24 @@ static std::map, std::array> primaries static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { // might be too strict - return imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB && + return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || + imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) && (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); } void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - shader.setUniformInt(SHADER_SOURCE_TF, imageDescription.transferFunction); + static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); + + if (m_renderData.surface.valid() && + ((!m_renderData.surface->m_colorManagement.valid() && *PSDREOTF >= 1) || + (*PSDREOTF == 2 && m_renderData.surface->m_colorManagement.valid() && + imageDescription.transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB))) { + shader.setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); + } else + shader.setUniformInt(SHADER_SOURCE_TF, imageDescription.transferFunction); + shader.setUniformInt(SHADER_TARGET_TF, targetImageDescription.transferFunction); const auto targetPrimaries = targetImageDescription.primariesNameSet || targetImageDescription.primaries == SPCPRimaries{} ? diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index edf0f7d1d01..14ed146996e 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -1555,9 +1555,10 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) { if (*PAUTOHDR && !(pMonitor->inHDR() && 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 - const auto targetCM = wantHDR ? (*PAUTOHDR == 2 ? NCMType::CM_HDR_EDID : NCMType::CM_HDR) : pMonitor->m_cmType; + const auto targetCM = wantHDR ? (*PAUTOHDR == 2 ? NCMType::CM_HDR_EDID : NCMType::CM_HDR) : pMonitor->m_cmType; + const auto targetSDREOTF = pMonitor->m_sdrEotf; Debug::log(INFO, "[CM] Auto HDR: changing monitor cm to {}", sc(targetCM)); - pMonitor->applyCMType(targetCM); + pMonitor->applyCMType(targetCM, targetSDREOTF); pMonitor->m_previousFSWindow.reset(); // trigger CTM update } Debug::log(INFO, wantHDR ? "[CM] Updating HDR metadata from monitor" : "[CM] Restoring SDR mode"); diff --git a/src/render/shaders/glsl/CM.glsl b/src/render/shaders/glsl/CM.glsl index 9074fa4f895..0e79aab0120 100644 --- a/src/render/shaders/glsl/CM.glsl +++ b/src/render/shaders/glsl/CM.glsl @@ -416,7 +416,7 @@ vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat4x2 dstPrimaries) mat3 dstxyz = primaries2xyz(dstPrimaries); pixColor = tonemap(pixColor, dstxyz); pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); - if (srcTF == CM_TRANSFER_FUNCTION_SRGB && dstTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { + if ((srcTF == CM_TRANSFER_FUNCTION_SRGB || srcTF == CM_TRANSFER_FUNCTION_GAMMA22) && dstTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { pixColor = saturate(pixColor, dstxyz, sdrSaturation); pixColor.rgb *= sdrBrightnessMultiplier; }