summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/video_shaders.c
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2016-06-29 09:28:17 +0200
committerwm4 <wm4@nowhere>2016-07-03 19:42:52 +0200
commit923e3c7b20f0a238062b0ac538a751c6c363a8cb (patch)
treed86988c4fe3a603df877e7cf91216ccc20b09a27 /video/out/opengl/video_shaders.c
parentd81fb97f4587f73f62a760b99f686139f9b8d966 (diff)
downloadmpv-923e3c7b20f0a238062b0ac538a751c6c363a8cb.tar.bz2
mpv-923e3c7b20f0a238062b0ac538a751c6c363a8cb.tar.xz
vo_opengl: generalize HDR tone mapping mechanism
This involves multiple changes: 1. Brightness metadata is split into nominal peak and signal peak. For a quick and dirty explanation: nominal peak is the brightest value that your color space can represent (i.e. the brightness of an encoded 1.0), and signal peak is the brightest value that actually occurs in the video (i.e. the brightest thing that's displayed). 2. vo_opengl uses a new decision logic to figure out the right nom_peak and sig_peak for all situations. It also does a better job of picking the right target gamut/colorspace to use for the OSD. (Which still is and still should be treated as sRGB). This change in logic also fixes #3293 en passant. 3. Since it was growing rapidly, the logic for auto-guessing / inferring the right colorimetry configuration (in pass_colormanage) was split from the logic for actually performing the adaptation (now pass_color_map). Right now, the new logic doesn't do a whole lot since HDR metadata is still ignored (but not for long).
Diffstat (limited to 'video/out/opengl/video_shaders.c')
-rw-r--r--video/out/opengl/video_shaders.c57
1 files changed, 51 insertions, 6 deletions
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index 7b736f1d5d..eded7d59c2 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -361,9 +361,11 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
}
// Tone map from a known peak brightness to the range [0,1]
-void pass_tone_map(struct gl_shader_cache *sc, float peak,
- enum tone_mapping algo, float param)
+static void pass_tone_map(struct gl_shader_cache *sc, float ref_peak,
+ enum tone_mapping algo, float param)
{
+ GLSLF("// HDR tone mapping\n");
+
switch (algo) {
case TONE_MAPPING_CLIP:
GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
@@ -373,7 +375,7 @@ void pass_tone_map(struct gl_shader_cache *sc, float peak,
float contrast = isnan(param) ? 0.5 : param,
offset = (1.0 - contrast) / contrast;
GLSLF("color.rgb = color.rgb / (color.rgb + vec3(%f));\n", offset);
- GLSLF("color.rgb *= vec3(%f);\n", (peak + offset) / peak);
+ GLSLF("color.rgb *= vec3(%f);\n", (ref_peak + offset) / ref_peak);
break;
}
@@ -384,20 +386,20 @@ void pass_tone_map(struct gl_shader_cache *sc, float peak,
A, C*B, D*E, A, B, D*F, E/F);
GLSLHF("}\n");
- GLSLF("color.rgb = hable(color.rgb) / hable(vec3(%f));\n", peak);
+ GLSLF("color.rgb = hable(color.rgb) / hable(vec3(%f));\n", ref_peak);
break;
}
case TONE_MAPPING_GAMMA: {
float gamma = isnan(param) ? 1.8 : param;
GLSLF("color.rgb = pow(color.rgb / vec3(%f), vec3(%f));\n",
- peak, 1.0/gamma);
+ ref_peak, 1.0/gamma);
break;
}
case TONE_MAPPING_LINEAR: {
float coeff = isnan(param) ? 1.0 : param;
- GLSLF("color.rgb = vec3(%f) * color.rgb;\n", coeff / peak);
+ GLSLF("color.rgb = vec3(%f) * color.rgb;\n", coeff / ref_peak);
break;
}
@@ -406,6 +408,49 @@ void pass_tone_map(struct gl_shader_cache *sc, float peak,
}
}
+// Map colors from one source space to another. These source spaces
+// must be known (i.e. not MP_CSP_*_AUTO), as this function won't perform
+// any auto-guessing.
+void pass_color_map(struct gl_shader_cache *sc,
+ struct mp_colorspace src, struct mp_colorspace dst,
+ enum tone_mapping algo, float tone_mapping_param)
+{
+ GLSLF("// color mapping\n");
+
+ // All operations from here on require linear light as a starting point,
+ // so we linearize even if src.gamma == dst.gamma when one of the other
+ // operations needs it
+ bool need_gamma = src.gamma != dst.gamma ||
+ src.primaries != dst.primaries ||
+ src.nom_peak != dst.nom_peak ||
+ src.sig_peak > dst.nom_peak;
+
+ if (need_gamma)
+ pass_linearize(sc, src.gamma);
+
+ // Stretch the signal value to renormalize to the dst nominal peak
+ if (src.nom_peak != dst.nom_peak)
+ GLSLF("color.rgb *= vec3(%f);\n", src.nom_peak / dst.nom_peak);
+
+ // Tone map to prevent clipping when the source signal peak exceeds the
+ // encodable range.
+ if (src.sig_peak > dst.nom_peak)
+ pass_tone_map(sc, src.sig_peak / dst.nom_peak, algo, tone_mapping_param);
+
+ // Adapt to the right colorspace if necessary
+ if (src.primaries != dst.primaries) {
+ struct mp_csp_primaries csp_src = mp_get_csp_primaries(src.primaries),
+ csp_dst = mp_get_csp_primaries(dst.primaries);
+ float m[3][3] = {{0}};
+ mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m);
+ gl_sc_uniform_mat3(sc, "cms_matrix", true, &m[0][0]);
+ GLSL(color.rgb = cms_matrix * color.rgb;)
+ }
+
+ if (need_gamma)
+ pass_delinearize(sc, dst.gamma);
+}
+
// Wide usage friendly PRNG, shamelessly stolen from a GLSL tricks forum post.
// Obtain random numbers by calling rand(h), followed by h = permute(h) to
// update the state. Assumes the texture was hooked.