From 441e384390f1cd1b1b1c159cb797429432a09493 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Wed, 14 Feb 2018 16:10:51 +0100 Subject: vo_gpu: introduce --target-peak This solves a number of problems simultaneously: 1. When outputting HLG, this allows tuning the OOTF based on the display characteristics. 2. When outputting PQ or other HDR curves, this allows soft-limiting the output brightness using the tone mapping algorithm. 3. When outputting SDR, this allows HDR-in-SDR style output, by controlling the output brightness directly. Closes #5521 --- DOCS/man/options.rst | 33 +++++++++++++++++++++++++++++++++ video/out/gpu/video.c | 11 +++++++---- video/out/gpu/video.h | 2 +- video/out/gpu/video_shaders.c | 10 ++++++++-- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index e51f564490..1a5af8121a 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -5054,6 +5054,39 @@ The following video options are currently all specific to ``--vo=gpu`` and The user should independently guarantee this before using these signal formats for display. +``--target-peak=`` + Specifies the measured peak brightness of the output display, in cd/m^2 + (AKA nits). The interpretation of this brightness depends on the configured + ``--target-trc``. In all cases, it imposes a limit on the signal values + that will be sent to the display. If the source exceeds this brightness + level, a tone mapping filter will be inserted. For HLG, it has the + additional effect of parametrizing the inverse OOTF, in order to get + colorimetrically consistent results with the mastering display. For SDR, or + when using an ICC (profile (``--icc-profile``), setting this to a value + above 100 essentially causes the display to be treated as if it were an HDR + display in disguise. (See the note below) + + By default, the chosen peak defaults to an appropriate value based on the + TRC in use. For SDR curves, it defaults to 100. For HDR curves, it + defaults to 100 * the transfer function's nominal peak. + + .. note:: + + When using an SDR transfer function, this is normally not needed, and + setting it may lead to very unexpected results. The one time it *is* + useful is if you want to calibrate a HDR display using traditional + transfer functions and calibration equipment. In such cases, you can + set your HDR display to a high brightness such as 800 cd/m^2, and then + calibrate it to a standard curve like gamma2.8. Setting this value to + 800 would then instruct mpv to essentially treat it as an HDR display + with the given peak. This may be a good alternative in environments + where PQ or HLG input to the display is not possible, and makes it + possible to use HDR displays with mpv regardless of operating system + support for HDMI HDR metadata. + + In such a configuration, we highly recommend setting ``--tone-mapping`` + to ``mobius`` or even ``clip``. + ``--tone-mapping=`` Specifies the algorithm used for tone-mapping images onto the target display. This is relevant for both HDR->SDR conversion as well as gamut diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index fdefc0044f..4e9f6d5e27 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -351,6 +351,7 @@ const struct m_sub_options gl_video_conf = { OPT_FLAG("gamma-auto", gamma_auto, 0), OPT_CHOICE_C("target-prim", target_prim, 0, mp_csp_prim_names), OPT_CHOICE_C("target-trc", target_trc, 0, mp_csp_trc_names), + OPT_INTRANGE("target-peak", target_peak, 0, 10, 10000), OPT_CHOICE("tone-mapping", tone_mapping, 0, ({"clip", TONE_MAPPING_CLIP}, {"mobius", TONE_MAPPING_MOBIUS}, @@ -548,6 +549,7 @@ struct mp_colorspace gl_video_get_output_colorspace(struct gl_video *p) return (struct mp_colorspace) { .primaries = p->opts.target_prim, .gamma = p->opts.target_trc, + .sig_peak = p->opts.target_peak / MP_REF_WHITE, }; } @@ -2392,6 +2394,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool .gamma = p->opts.target_trc, .primaries = p->opts.target_prim, .light = MP_CSP_LIGHT_DISPLAY, + .sig_peak = p->opts.target_peak / MP_REF_WHITE, }; if (p->use_lut_3d) { @@ -2446,10 +2449,10 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool dst.gamma = MP_CSP_TRC_GAMMA22; } - // For now, just infer the dst sig peak from the gamma function always. - // In theory, we could allow users to configure this or detect it from the - // ICC profile, but avoid the complexity for now. - dst.sig_peak = mp_trc_nom_peak(dst.gamma); + // If there's no specific signal peak known for the output display, infer + // it from the chosen transfer function + if (!dst.sig_peak) + dst.sig_peak = mp_trc_nom_peak(dst.gamma); bool detect_peak = p->opts.compute_hdr_peak >= 0 && mp_trc_is_hdr(src.gamma); if (detect_peak && !p->hdr_peak_ssbo) { diff --git a/video/out/gpu/video.h b/video/out/gpu/video.h index 80f31c2934..f385661e47 100644 --- a/video/out/gpu/video.h +++ b/video/out/gpu/video.h @@ -106,7 +106,7 @@ struct gl_video_opts { int gamma_auto; int target_prim; int target_trc; - int target_brightness; + int target_peak; int tone_mapping; int compute_hdr_peak; float tone_mapping_param; diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c index d8a26542b0..b588b8e500 100644 --- a/video/out/gpu/video_shaders.c +++ b/video/out/gpu/video_shaders.c @@ -812,8 +812,14 @@ void pass_color_map(struct gl_shader_cache *sc, if (need_ootf) pass_inverse_ootf(sc, dst.light, dst.sig_peak); - // Post-scale the outgoing values from absolute scale to normalized - GLSLF("color.rgb *= vec3(%f);\n", 1.0 / mp_trc_nom_peak(dst.gamma)); + // Post-scale the outgoing values from absolute scale to normalized. + // For SDR, we normalize to the chosen signal peak. For HDR, we normalize + // to the encoding range of the transfer function. + float dst_range = dst.sig_peak; + if (mp_trc_is_hdr(dst.gamma)) + dst_range = mp_trc_nom_peak(dst.gamma); + + GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range); // Warn for remaining out-of-gamut colors is enabled if (gamut_warning) { -- cgit v1.2.3