diff --git a/src/protocols/types/ColorManagement.hpp b/src/protocols/types/ColorManagement.hpp index 435e50df0..8bb30e8e0 100644 --- a/src/protocols/types/ColorManagement.hpp +++ b/src/protocols/types/ColorManagement.hpp @@ -6,8 +6,10 @@ #define SDR_MIN_LUMINANCE 0.2 #define SDR_MAX_LUMINANCE 80.0 +#define SDR_REF_LUMINANCE 80.0 #define HDR_MIN_LUMINANCE 0.005 #define HDR_MAX_LUMINANCE 10000.0 +#define HDR_REF_LUMINANCE 203.0 #define HLG_MAX_LUMINANCE 1000.0 namespace NColorManagement { @@ -229,6 +231,25 @@ namespace NColorManagement { } }; + float getTFRefLuminance(int sdrRefLuminance = -1) const { + switch (transferFunction) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: + case CM_TRANSFER_FUNCTION_ST2084_PQ: + case CM_TRANSFER_FUNCTION_HLG: return HDR_REF_LUMINANCE; + case CM_TRANSFER_FUNCTION_GAMMA22: + case CM_TRANSFER_FUNCTION_GAMMA28: + case CM_TRANSFER_FUNCTION_BT1886: + case CM_TRANSFER_FUNCTION_ST240: + case CM_TRANSFER_FUNCTION_LOG_100: + case CM_TRANSFER_FUNCTION_LOG_316: + case CM_TRANSFER_FUNCTION_XVYCC: + case CM_TRANSFER_FUNCTION_EXT_SRGB: + case CM_TRANSFER_FUNCTION_ST428: + case CM_TRANSFER_FUNCTION_SRGB: + default: return sdrRefLuminance >= 0 ? sdrRefLuminance : SDR_REF_LUMINANCE; + } + }; + uint32_t findId() const; uint32_t getId() const; uint32_t updateId(); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 19c07923c..2f1bccb2b 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -971,6 +971,7 @@ static void getCMShaderUniforms(SShader& shader) { shader.uniformLocations[SHADER_DST_TF_RANGE] = glGetUniformLocation(shader.program, "dstTFRange"); shader.uniformLocations[SHADER_TARGET_PRIMARIES] = glGetUniformLocation(shader.program, "targetPrimaries"); shader.uniformLocations[SHADER_MAX_LUMINANCE] = glGetUniformLocation(shader.program, "maxLuminance"); + shader.uniformLocations[SHADER_SRC_REF_LUMINANCE] = glGetUniformLocation(shader.program, "srcRefLuminance"); shader.uniformLocations[SHADER_DST_MAX_LUMINANCE] = glGetUniformLocation(shader.program, "dstMaxLuminance"); shader.uniformLocations[SHADER_DST_REF_LUMINANCE] = glGetUniformLocation(shader.program, "dstRefLuminance"); shader.uniformLocations[SHADER_SDR_SATURATION] = glGetUniformLocation(shader.program, "sdrSaturation"); @@ -1560,6 +1561,14 @@ static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescriptio targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG); } +static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) { + // might be too strict + return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ || + imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) && + (targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB || + targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22); +} + void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); @@ -1585,16 +1594,22 @@ void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SI shader.setUniformMatrix4x2fv(SHADER_TARGET_PRIMARIES, 1, false, glTargetPrimaries); const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription, targetImageDescription); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription, targetImageDescription); shader.setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), imageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), targetImageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); - const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference; - shader.setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription.luminances.reference / imageDescription.luminances.reference); + shader.setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription.getTFRefLuminance(-1)); + shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.getTFRefLuminance(-1)); + + const float maxLuminance = + needsHDRmod ? imageDescription.getTFMaxLuminance(-1) : (imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference); + shader.setUniformFloat(SHADER_MAX_LUMINANCE, + maxLuminance * targetImageDescription.luminances.reference / + (needsHDRmod ? imageDescription.getTFRefLuminance(-1) : imageDescription.luminances.reference)); shader.setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription.luminances.max > 0 ? targetImageDescription.luminances.max : 10000); - shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.luminances.reference); shader.setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f); shader.setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f); const auto cacheKey = std::make_pair(imageDescription.getId(), targetImageDescription.getId()); @@ -1710,11 +1725,8 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c if (shader == &m_shaders->m_shCM) { shader->setUniformInt(SHADER_TEX_TYPE, texType); if (data.cmBackToSRGB) { - // revert luma changes to avoid black screenshots. - // this will likely not be 1:1, and might cause screenshots to be too bright, but it's better than pitch black. - imageDescription.luminances = {}; - static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); - auto chosenSdrEotf = *PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; + static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); + auto chosenSdrEotf = *PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; passCMUniforms(*shader, imageDescription, NColorManagement::SImageDescription{.transferFunction = chosenSdrEotf}, true, -1, -1); } else passCMUniforms(*shader, imageDescription); diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index 4f5456426..50ff58898 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -16,6 +16,7 @@ enum eShaderUniform : uint8_t { SHADER_DST_TF_RANGE, SHADER_TARGET_PRIMARIES, SHADER_MAX_LUMINANCE, + SHADER_SRC_REF_LUMINANCE, SHADER_DST_MAX_LUMINANCE, SHADER_DST_REF_LUMINANCE, SHADER_SDR_SATURATION, diff --git a/src/render/shaders/glsl/CM.glsl b/src/render/shaders/glsl/CM.glsl index 0e79aab01..362d7cfb4 100644 --- a/src/render/shaders/glsl/CM.glsl +++ b/src/render/shaders/glsl/CM.glsl @@ -2,6 +2,7 @@ uniform vec2 srcTFRange; uniform vec2 dstTFRange; uniform float maxLuminance; +uniform float srcRefLuminance; uniform float dstMaxLuminance; uniform float dstRefLuminance; uniform float sdrSaturation; @@ -387,24 +388,17 @@ vec4 tonemap(vec4 color, mat3 dstXYZ) { PQ_INV_M1 ) * HDR_MAX_LUMINANCE; - float srcScale = maxLuminance / dstRefLuminance; - float dstScale = dstMaxLuminance / dstRefLuminance; + float linearPart = min(luminance, dstRefLuminance); + float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); + float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0); + float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); + float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); + float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); - float minScale = min(srcScale, 1.5); - float dimming = 1.0 / clamp(minScale / dstScale, 1.0, minScale); - float refLuminance = dstRefLuminance * dimming; + // scale src to dst reference + float refScale = dstRefLuminance / srcRefLuminance; - float low = min(luminance * dimming, refLuminance); - float highlight = clamp((luminance / dstRefLuminance - 1.0) / (srcScale - 1.0), 0.0, 1.0); - float high = log(highlight * (M_E - 1.0) + 1.0) * (dstMaxLuminance - refLuminance); - luminance = low + high; - - E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_M1); - ICtCp[0] = pow( - (PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E), - PQ_M2 - ) / HDR_MAX_LUMINANCE; - return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]); + return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); } vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat4x2 dstPrimaries) { diff --git a/src/render/shaders/glsl/blurprepare.frag b/src/render/shaders/glsl/blurprepare.frag index 548289c30..6b9809f8d 100644 --- a/src/render/shaders/glsl/blurprepare.frag +++ b/src/render/shaders/glsl/blurprepare.frag @@ -28,7 +28,7 @@ void main() { pixColor.rgb /= sdrBrightnessMultiplier; } pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); - pixColor = toNit(pixColor, srcTFRange); + pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); }