Skip to content

Commit 3f2aefa

Browse files
committed
cm: higher-quality tonemapping
1 parent 8e9add2 commit 3f2aefa

File tree

4 files changed

+43
-19
lines changed

4 files changed

+43
-19
lines changed

src/protocols/types/ColorManagement.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
#define SDR_MIN_LUMINANCE 0.2
88
#define SDR_MAX_LUMINANCE 80.0
9+
#define SDR_REF_LUMINANCE 80.0
910
#define HDR_MIN_LUMINANCE 0.005
1011
#define HDR_MAX_LUMINANCE 10000.0
12+
#define HDR_REF_LUMINANCE 203.0
1113
#define HLG_MAX_LUMINANCE 1000.0
1214

1315
namespace NColorManagement {
@@ -228,6 +230,25 @@ namespace NColorManagement {
228230
}
229231
};
230232

233+
float getTFRefLuminance(int sdrRefLuminance = -1) const {
234+
switch (transferFunction) {
235+
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
236+
case CM_TRANSFER_FUNCTION_ST2084_PQ:
237+
case CM_TRANSFER_FUNCTION_HLG: return HDR_REF_LUMINANCE;
238+
case CM_TRANSFER_FUNCTION_GAMMA22:
239+
case CM_TRANSFER_FUNCTION_GAMMA28:
240+
case CM_TRANSFER_FUNCTION_BT1886:
241+
case CM_TRANSFER_FUNCTION_ST240:
242+
case CM_TRANSFER_FUNCTION_LOG_100:
243+
case CM_TRANSFER_FUNCTION_LOG_316:
244+
case CM_TRANSFER_FUNCTION_XVYCC:
245+
case CM_TRANSFER_FUNCTION_EXT_SRGB:
246+
case CM_TRANSFER_FUNCTION_ST428:
247+
case CM_TRANSFER_FUNCTION_SRGB:
248+
default: return sdrRefLuminance >= 0 ? sdrRefLuminance : SDR_REF_LUMINANCE;
249+
}
250+
};
251+
231252
uint32_t findId() const;
232253
uint32_t getId() const;
233254
uint32_t updateId();

src/render/OpenGL.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ static void getCMShaderUniforms(SShader& shader) {
969969
shader.uniformLocations[SHADER_DST_TF_RANGE] = glGetUniformLocation(shader.program, "dstTFRange");
970970
shader.uniformLocations[SHADER_TARGET_PRIMARIES] = glGetUniformLocation(shader.program, "targetPrimaries");
971971
shader.uniformLocations[SHADER_MAX_LUMINANCE] = glGetUniformLocation(shader.program, "maxLuminance");
972+
shader.uniformLocations[SHADER_SRC_REF_LUMINANCE] = glGetUniformLocation(shader.program, "srcRefLuminance");
972973
shader.uniformLocations[SHADER_DST_MAX_LUMINANCE] = glGetUniformLocation(shader.program, "dstMaxLuminance");
973974
shader.uniformLocations[SHADER_DST_REF_LUMINANCE] = glGetUniformLocation(shader.program, "dstRefLuminance");
974975
shader.uniformLocations[SHADER_SDR_SATURATION] = glGetUniformLocation(shader.program, "sdrSaturation");
@@ -1589,10 +1590,12 @@ void CHyprOpenGLImpl::passCMUniforms(SShader& shader, const NColorManagement::SI
15891590
shader.setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription.getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1),
15901591
targetImageDescription.getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1));
15911592

1593+
shader.setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription.getTFRefLuminance(-1));
1594+
shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.getTFRefLuminance(-1));
1595+
15921596
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
15931597
shader.setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription.luminances.reference / imageDescription.luminances.reference);
15941598
shader.setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription.luminances.max > 0 ? targetImageDescription.luminances.max : 10000);
1595-
shader.setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription.luminances.reference);
15961599
shader.setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f);
15971600
shader.setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f);
15981601
const auto cacheKey = std::make_pair(imageDescription.getId(), targetImageDescription.getId());
@@ -1709,9 +1712,11 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
17091712
shader->setUniformInt(SHADER_TEX_TYPE, texType);
17101713
if (data.cmBackToSRGB) {
17111714
// revert luma changes to avoid black screenshots.
1712-
// this will likely not be 1:1, and might cause screenshots to be too bright, but it's better than pitch black.
1713-
imageDescription.luminances = {};
1714-
passCMUniforms(*shader, imageDescription, NColorManagement::SImageDescription{}, true, -1, -1);
1715+
imageDescription.luminances = {
1716+
.min = imageDescription.getTFMinLuminance(-1), .max = imageDescription.getTFMaxLuminance(-1), .reference = imageDescription.getTFRefLuminance(-1)};
1717+
static auto PSDREOTF = CConfigValue<Hyprlang::INT>("render:cm_sdr_eotf");
1718+
auto chosenSdrEotf = *PSDREOTF > 0 ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB;
1719+
passCMUniforms(*shader, imageDescription, NColorManagement::SImageDescription{.transferFunction = chosenSdrEotf}, true, -1, -1);
17151720
} else
17161721
passCMUniforms(*shader, imageDescription);
17171722
}

src/render/Shader.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ enum eShaderUniform : uint8_t {
1616
SHADER_DST_TF_RANGE,
1717
SHADER_TARGET_PRIMARIES,
1818
SHADER_MAX_LUMINANCE,
19+
SHADER_SRC_REF_LUMINANCE,
1920
SHADER_DST_MAX_LUMINANCE,
2021
SHADER_DST_REF_LUMINANCE,
2122
SHADER_SDR_SATURATION,

src/render/shaders/glsl/CM.glsl

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ uniform vec2 srcTFRange;
22
uniform vec2 dstTFRange;
33

44
uniform float maxLuminance;
5+
uniform float srcRefLuminance;
56
uniform float dstMaxLuminance;
67
uniform float dstRefLuminance;
78
uniform float sdrSaturation;
@@ -387,24 +388,20 @@ vec4 tonemap(vec4 color, mat3 dstXYZ) {
387388
PQ_INV_M1
388389
) * HDR_MAX_LUMINANCE;
389390

390-
float srcScale = maxLuminance / dstRefLuminance;
391-
float dstScale = dstMaxLuminance / dstRefLuminance;
391+
float linearPart = min(luminance, dstRefLuminance);
392+
float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0);
393+
float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0);
394+
float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0));
395+
float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance);
396+
float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance);
392397

393-
float minScale = min(srcScale, 1.5);
394-
float dimming = 1.0 / clamp(minScale / dstScale, 1.0, minScale);
395-
float refLuminance = dstRefLuminance * dimming;
398+
float newIpq = tfPQ(vec3(newLum / HDR_MAX_LUMINANCE)).r;
399+
ICtCp[0] = clamp(newIpq, 0.0, 1.0);
396400

397-
float low = min(luminance * dimming, refLuminance);
398-
float highlight = clamp((luminance / dstRefLuminance - 1.0) / (srcScale - 1.0), 0.0, 1.0);
399-
float high = log(highlight * (M_E - 1.0) + 1.0) * (dstMaxLuminance - refLuminance);
400-
luminance = low + high;
401+
// scale src to dst reference
402+
float refScale = dstRefLuminance / srcRefLuminance;
401403

402-
E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_M1);
403-
ICtCp[0] = pow(
404-
(PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E),
405-
PQ_M2
406-
) / HDR_MAX_LUMINANCE;
407-
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]);
404+
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]);
408405
}
409406

410407
vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat4x2 dstPrimaries) {

0 commit comments

Comments
 (0)