diff options
Diffstat (limited to 'video/out/gpu')
-rw-r--r-- | video/out/gpu/video.c | 6 | ||||
-rw-r--r-- | video/out/gpu/video_shaders.c | 65 | ||||
-rw-r--r-- | video/out/gpu/video_shaders.h | 2 |
3 files changed, 40 insertions, 33 deletions
diff --git a/video/out/gpu/video.c b/video/out/gpu/video.c index 2d4e711210..fdefc0044f 100644 --- a/video/out/gpu/video.c +++ b/video/out/gpu/video.c @@ -2411,6 +2411,7 @@ static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool if (gl_video_get_lut3d(p, prim_orig, trc_orig)) { dst.primaries = prim_orig; dst.gamma = trc_orig; + assert(dst.primaries && dst.gamma); } } @@ -2445,6 +2446,11 @@ 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); + bool detect_peak = p->opts.compute_hdr_peak >= 0 && mp_trc_is_hdr(src.gamma); if (detect_peak && !p->hdr_peak_ssbo) { struct { diff --git a/video/out/gpu/video_shaders.c b/video/out/gpu/video_shaders.c index 23824cfdb5..cd63b37c11 100644 --- a/video/out/gpu/video_shaders.c +++ b/video/out/gpu/video_shaders.c @@ -334,6 +334,10 @@ static const float SLOG_A = 0.432699, // Linearize (expand), given a TRC as input. In essence, this is the ITU-R // EOTF, calculated on an idealized (reference) monitor with a white point of // MP_REF_WHITE and infinite contrast. +// +// These functions always output to a normalized scale of [0,1], for +// convenience of the video.c code that calls it. To get the values in an +// absolute scale, multiply the result by `mp_trc_nom_peak(trc)` void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) { if (trc == MP_CSP_TRC_LINEAR) @@ -417,6 +421,8 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) // Delinearize (compress), given a TRC as output. This corresponds to the // inverse EOTF (not the OETF) in ITU-R terminology, again assuming a // reference monitor. +// +// Like pass_linearize, this functions ingests values on an normalized scale void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) { if (trc == MP_CSP_TRC_LINEAR) @@ -488,15 +494,13 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc) } // Apply the OOTF mapping from a given light type to display-referred light. -// The extra peak parameter is used to scale the values before and after -// the OOTF, and can be inferred using mp_trc_nom_peak -void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak) +// Assumes absolute scale values. +static void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light) { if (light == MP_CSP_LIGHT_DISPLAY) return; GLSLF("// apply ootf\n"); - GLSLF("color.rgb *= vec3(%f);\n", peak); switch (light) { @@ -521,18 +525,15 @@ void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak) default: abort(); } - - GLSLF("color.rgb *= vec3(1.0/%f);\n", peak); } // Inverse of the function pass_ootf, for completeness' sake. -void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak) +static void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light light) { if (light == MP_CSP_LIGHT_DISPLAY) return; GLSLF("// apply inverse ootf\n"); - GLSLF("color.rgb *= vec3(%f);\n", peak); switch (light) { @@ -635,7 +636,7 @@ static void hdr_update_peak(struct gl_shader_cache *sc) // Tone map from a known peak brightness to the range [0,1]. If ref_peak // is 0, we will use peak detection instead static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, - float src_peak, float dst_range, + float src_peak, float dst_peak, enum tone_mapping algo, float param, float desat) { GLSLF("// HDR tone mapping\n"); @@ -647,18 +648,13 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, GLSLF("float sig_peak = %f;\n", src_peak); GLSLF("float sig_avg = %f;\n", sdr_avg); - // Rescale the variables in order to bring it into a representation where - // 1.0 represents the dst_peak. This is because all of the tone mapping - // algorithms are defined in such a way that they map to the range [0.0, 1.0]. - if (dst_range > 1.0) { - GLSLF("sig *= %f;\n", 1.0 / dst_range); - GLSLF("sig_peak *= %f;\n", 1.0 / dst_range); - } - // Desaturate the color using a coefficient dependent on the signal + // Do this before peak detection in order to try and reclaim as much + // dynamic range as possible. if (desat > 0) { + float base = 0.18 * dst_peak; GLSL(float luma = dot(dst_luma, color.rgb);) - GLSL(float coeff = max(sig - 0.18, 1e-6) / max(sig, 1e-6);); + GLSLF("float coeff = max(sig - %f, 1e-6) / max(sig, 1e-6);\n", base); GLSLF("coeff = pow(coeff, %f);\n", 10.0 / desat); GLSL(color.rgb = mix(color.rgb, vec3(luma), coeff);) GLSL(sig = mix(sig, luma, coeff);) // also make sure to update `sig` @@ -667,6 +663,14 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, if (detect_peak) hdr_update_peak(sc); + // Rescale the variables in order to bring it into a representation where + // 1.0 represents the dst_peak. This is because all of the tone mapping + // algorithms are defined in such a way that they map to the range [0.0, 1.0]. + if (dst_peak > 1.0) { + GLSLF("sig *= %f;\n", 1.0 / dst_peak); + GLSLF("sig_peak *= %f;\n", 1.0 / dst_peak); + } + GLSL(float sig_orig = sig;) GLSLF("float slope = min(1.0, %f / sig_avg);\n", sdr_avg); GLSL(sig *= slope;) @@ -728,7 +732,7 @@ static void pass_tone_map(struct gl_shader_cache *sc, bool detect_peak, // Apply the computed scale factor to the color, linearly to prevent // discoloration GLSL(sig = min(sig, 1.0);) - GLSL(color.rgb *= sig / sig_orig;) + GLSL(color.rgb *= vec3(sig / sig_orig);) } // Map colors from one source space to another. These source spaces must be @@ -746,10 +750,6 @@ void pass_color_map(struct gl_shader_cache *sc, { GLSLF("// color mapping\n"); - // Compute the highest encodable level - float src_range = mp_trc_nom_peak(src.gamma), - dst_range = mp_trc_nom_peak(dst.gamma); - // Some operations need access to the video's luma coefficients, so make // them available float rgb2xyz[3][3]; @@ -763,8 +763,7 @@ void pass_color_map(struct gl_shader_cache *sc, // operations needs it bool need_gamma = src.gamma != dst.gamma || src.primaries != dst.primaries || - src_range != dst_range || - src.sig_peak > dst_range || + src.sig_peak > dst.sig_peak || src.light != dst.light; if (need_gamma && !is_linear) { @@ -773,8 +772,11 @@ void pass_color_map(struct gl_shader_cache *sc, is_linear = true; } + // Pre-scale the incoming values into an absolute scale + GLSLF("color.rgb *= vec3(%f);\n", mp_trc_nom_peak(src.gamma)); + if (src.light != dst.light) - pass_ootf(sc, src.light, src_range); + pass_ootf(sc, src.light); // Adapt to the right colorspace if necessary if (src.primaries != dst.primaries) { @@ -791,15 +793,16 @@ void pass_color_map(struct gl_shader_cache *sc, // Tone map to prevent clipping when the source signal peak exceeds the // encodable range or we've reduced the gamut - if (src.sig_peak > dst_range) { - GLSLF("color.rgb *= vec3(%f);\n", src_range); - pass_tone_map(sc, detect_peak, src.sig_peak, dst_range, algo, + if (src.sig_peak > dst.sig_peak) { + pass_tone_map(sc, detect_peak, src.sig_peak, dst.sig_peak, algo, tone_mapping_param, tone_mapping_desat); - GLSLF("color.rgb *= vec3(%f);\n", 1.0 / dst_range); } if (src.light != dst.light) - pass_inverse_ootf(sc, dst.light, dst_range); + pass_inverse_ootf(sc, dst.light); + + // Post-scale the outgoing values from absolute scale to normalized + GLSLF("color.rgb *= vec3(%f);\n", 1.0 / mp_trc_nom_peak(dst.gamma)); // Warn for remaining out-of-gamut colors is enabled if (gamut_warning) { diff --git a/video/out/gpu/video_shaders.h b/video/out/gpu/video_shaders.h index 2ae2ac3fa9..cd395d6377 100644 --- a/video/out/gpu/video_shaders.h +++ b/video/out/gpu/video_shaders.h @@ -39,8 +39,6 @@ void pass_sample_oversample(struct gl_shader_cache *sc, struct scaler *scaler, void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc); -void pass_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak); -void pass_inverse_ootf(struct gl_shader_cache *sc, enum mp_csp_light light, float peak); void pass_color_map(struct gl_shader_cache *sc, struct mp_colorspace src, struct mp_colorspace dst, |