summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--video/out/opengl/lcms.c123
-rw-r--r--video/out/opengl/lcms.h6
-rw-r--r--video/out/opengl/video.c72
-rw-r--r--video/out/opengl/video.h6
-rw-r--r--video/out/opengl/video_shaders.c5
-rw-r--r--video/out/vo_opengl.c24
-rw-r--r--video/out/vo_opengl_cb.c2
7 files changed, 162 insertions, 76 deletions
diff --git a/video/out/opengl/lcms.c b/video/out/opengl/lcms.c
index c956127a75..8f6b40830a 100644
--- a/video/out/opengl/lcms.c
+++ b/video/out/opengl/lcms.c
@@ -44,6 +44,8 @@ struct gl_lcms {
size_t icc_size;
char *icc_path;
bool changed;
+ enum mp_csp_prim prev_prim;
+ enum mp_csp_trc prev_trc;
struct mp_log *log;
struct mpv_global *global;
@@ -166,16 +168,88 @@ void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile)
p->icc_size = profile->len;
}
-// Return and _reset_ whether the lookul table has changed since the last call.
-// If it has changed, gl_lcms_get_lut3d() should be called.
-bool gl_lcms_has_changed(struct gl_lcms *p)
+// Return and _reset_ whether the profile or config has changed since the last
+// call. If it has changed, gl_lcms_get_lut3d() should be called.
+bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
+ enum mp_csp_trc trc)
{
- bool change = p->changed;
+ bool change = p->changed || p->prev_prim != prim || p->prev_trc != trc;
p->changed = false;
+ p->prev_prim = prim;
+ p->prev_trc = trc;
return change;
}
-bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d)
+static cmsHPROFILE get_vid_profile(cmsContext cms, cmsHPROFILE disp_profile,
+ enum mp_csp_prim prim, enum mp_csp_trc trc)
+{
+ // The input profile for the transformation is dependent on the video
+ // primaries and transfer characteristics
+ struct mp_csp_primaries csp = mp_get_csp_primaries(prim);
+ cmsCIExyY wp_xyY = {csp.white.x, csp.white.y, 1.0};
+ cmsCIExyYTRIPLE prim_xyY = {
+ .Red = {csp.red.x, csp.red.y, 1.0},
+ .Green = {csp.green.x, csp.green.y, 1.0},
+ .Blue = {csp.blue.x, csp.blue.y, 1.0},
+ };
+
+ cmsToneCurve *tonecurve = NULL;
+ switch (trc) {
+ case MP_CSP_TRC_LINEAR: tonecurve = cmsBuildGamma(cms, 1.0); break;
+ case MP_CSP_TRC_GAMMA18: tonecurve = cmsBuildGamma(cms, 1.8); break;
+ case MP_CSP_TRC_GAMMA22: tonecurve = cmsBuildGamma(cms, 2.2); break;
+ case MP_CSP_TRC_GAMMA28: tonecurve = cmsBuildGamma(cms, 2.8); break;
+
+ case MP_CSP_TRC_SRGB:
+ // Values copied from Little-CMS
+ tonecurve = cmsBuildParametricToneCurve(cms, 4,
+ (double[5]){2.40, 1/1.055, 0.055/1.055, 1/12.92, 0.04045});
+ break;
+
+ case MP_CSP_TRC_PRO_PHOTO:
+ tonecurve = cmsBuildParametricToneCurve(cms, 4,
+ (double[5]){1.8, 1.0, 0.0, 1/16.0, 0.03125});
+ break;
+
+ case MP_CSP_TRC_BT_1886: {
+ // To build an appropriate BT.1886 transformation we need access to
+ // the display's black point, so we use the reverse mappings
+ cmsHPROFILE xyz_profile = cmsCreateXYZProfileTHR(cms);
+ cmsHTRANSFORM rgb2xyz = cmsCreateTransformTHR(cms,
+ disp_profile, TYPE_RGB_16, xyz_profile, TYPE_XYZ_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(xyz_profile);
+ if (!rgb2xyz)
+ return false;
+
+ uint64_t black[3] = {0};
+ cmsCIEXYZ disp_black;
+ cmsDoTransform(rgb2xyz, black, &disp_black, 1);
+
+ // Build the parametric BT.1886 transfer curve
+ const double gamma = 2.40;
+ double binv = pow(disp_black.Y, 1.0/gamma);
+ tonecurve = cmsBuildParametricToneCurve(cms, 6,
+ (double[4]){gamma, 1.0 - binv, binv, 0.0});
+ break;
+ }
+
+ default:
+ abort();
+ }
+
+ if (!tonecurve)
+ return false;
+
+ cmsHPROFILE *vid_profile = cmsCreateRGBProfileTHR(cms, &wp_xyY, &prim_xyY,
+ (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
+ cmsFreeToneCurve(tonecurve);
+
+ return vid_profile;
+}
+
+bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
+ enum mp_csp_prim prim, enum mp_csp_trc trc)
{
int s_r, s_g, s_b;
bool result = false;
@@ -197,8 +271,8 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d)
// because we may change the parameter in the future or make it
// customizable, same for the primaries.
char *cache_info = talloc_asprintf(tmp,
- "ver=1.1, intent=%d, size=%dx%dx%d, gamma=2.4, prim=bt2020\n",
- p->opts.intent, s_r, s_g, s_b);
+ "ver=1.2, intent=%d, size=%dx%dx%d, prim=%d, trc=%d\n",
+ p->opts.intent, s_r, s_g, s_b, prim, trc);
uint8_t hash[32];
struct AVSHA *sha = av_sha_alloc();
@@ -242,23 +316,12 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d)
if (!profile)
goto error_exit;
- // We always generate the 3DLUT against BT.2020, and transform into this
- // space inside the shader if the source differs.
- struct mp_csp_primaries csp = mp_get_csp_primaries(MP_CSP_PRIM_BT_2020);
-
- cmsCIExyY wp = {csp.white.x, csp.white.y, 1.0};
- cmsCIExyYTRIPLE prim = {
- .Red = {csp.red.x, csp.red.y, 1.0},
- .Green = {csp.green.x, csp.green.y, 1.0},
- .Blue = {csp.blue.x, csp.blue.y, 1.0},
- };
+ cmsHPROFILE vid_profile = get_vid_profile(cms, profile, prim, trc);
+ if (!vid_profile) {
+ cmsCloseProfile(profile);
+ goto error_exit;
+ }
- // 2.4 is arbitrarily used as a gamma compression factor for the 3DLUT,
- // reducing artifacts due to rounding errors on wide gamut profiles
- cmsToneCurve *tonecurve = cmsBuildGamma(cms, 2.4);
- cmsHPROFILE vid_profile = cmsCreateRGBProfileTHR(cms, &wp, &prim,
- (cmsToneCurve*[3]){tonecurve, tonecurve, tonecurve});
- cmsFreeToneCurve(tonecurve);
cmsHTRANSFORM trafo = cmsCreateTransformTHR(cms, vid_profile, TYPE_RGB_16,
profile, TYPE_RGB_16,
p->opts.intent,
@@ -333,7 +396,17 @@ struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log,
void gl_lcms_set_options(struct gl_lcms *p, struct mp_icc_opts *opts) { }
void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile) { }
-bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **x) { return false; }
-bool gl_lcms_has_changed(struct gl_lcms *p) { return false; }
+
+bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
+ enum mp_csp_trc trc)
+{
+ return false;
+}
+
+bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d,
+ enum mp_csp_prim prim, enum mp_csp_trc trc)
+{
+ return false;
+}
#endif
diff --git a/video/out/opengl/lcms.h b/video/out/opengl/lcms.h
index 5ad08b7d64..ee2a48b59c 100644
--- a/video/out/opengl/lcms.h
+++ b/video/out/opengl/lcms.h
@@ -24,7 +24,9 @@ struct gl_lcms *gl_lcms_init(void *talloc_ctx, struct mp_log *log,
struct mpv_global *global);
void gl_lcms_set_options(struct gl_lcms *p, struct mp_icc_opts *opts);
void gl_lcms_set_memory_profile(struct gl_lcms *p, bstr *profile);
-bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **);
-bool gl_lcms_has_changed(struct gl_lcms *p);
+bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **,
+ enum mp_csp_prim prim, enum mp_csp_trc trc);
+bool gl_lcms_has_changed(struct gl_lcms *p, enum mp_csp_prim prim,
+ enum mp_csp_trc trc);
#endif
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c
index e9bafc0d3f..c2d5fc211d 100644
--- a/video/out/opengl/video.c
+++ b/video/out/opengl/video.c
@@ -148,6 +148,7 @@ struct gl_video {
struct mpv_global *global;
struct mp_log *log;
struct gl_video_opts opts;
+ struct gl_lcms *cms;
bool gl_debug;
int texture_16bit_depth; // actual bits available in 16 bit textures
@@ -693,21 +694,31 @@ static void uninit_rendering(struct gl_video *p)
gl_video_reset_surfaces(p);
}
-void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d)
+void gl_video_update_profile(struct gl_video *p)
+{
+ if (p->use_lut_3d)
+ return;
+
+ p->use_lut_3d = true;
+ check_gl_features(p);
+
+ reinit_rendering(p);
+}
+
+static bool gl_video_get_lut3d(struct gl_video *p, enum mp_csp_prim prim,
+ enum mp_csp_trc trc)
{
GL *gl = p->gl;
- if (!lut3d) {
- if (p->use_lut_3d) {
- p->use_lut_3d = false;
- reinit_rendering(p);
- }
- return;
- }
+ if (!p->cms || !p->use_lut_3d)
+ return false;
- if (!(gl->mpgl_caps & MPGL_CAP_3D_TEX) || gl->es) {
- MP_ERR(p, "16 bit fixed point 3D textures not available.\n");
- return;
+ if (!gl_lcms_has_changed(p->cms, prim, trc))
+ return true;
+
+ struct lut3d *lut3d = NULL;
+ if (!gl_lcms_get_lut3d(p->cms, &lut3d, prim, trc) || !lut3d) {
+ return false;
}
if (!p->lut_3d_texture)
@@ -724,12 +735,9 @@ void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d)
gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
gl->ActiveTexture(GL_TEXTURE0);
- p->use_lut_3d = true;
- check_gl_features(p);
-
debug_check_gl(p, "after 3d lut creation");
- reinit_rendering(p);
+ return true;
}
// Fill an img_tex struct from an FBO + some metadata
@@ -1868,10 +1876,16 @@ static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src,
enum mp_csp_prim prim_dst = p->opts.target_prim;
if (p->use_lut_3d) {
- // The 3DLUT is hard-coded against BT.2020's gamut during creation, and
- // we never want to adjust its output (so treat it as linear)
- prim_dst = MP_CSP_PRIM_BT_2020;
- trc_dst = MP_CSP_TRC_LINEAR;
+ // The 3DLUT is always generated against the original source space
+ enum mp_csp_prim prim_orig = p->image_params.primaries;
+ enum mp_csp_trc trc_orig = p->image_params.gamma;
+
+ if (gl_video_get_lut3d(p, prim_orig, trc_orig)) {
+ prim_dst = prim_orig;
+ trc_dst = trc_orig;
+ } else {
+ p->use_lut_3d = false;
+ }
}
if (prim_dst == MP_CSP_PRIM_AUTO)
@@ -1885,10 +1899,10 @@ static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src,
trc_dst = MP_CSP_TRC_GAMMA22;
}
- bool need_cms = prim_src != prim_dst || p->use_lut_3d;
- bool need_gamma = trc_src != trc_dst || need_cms;
+ bool need_gamma = trc_src != trc_dst || prim_src != prim_dst;
if (need_gamma)
pass_linearize(p->sc, trc_src);
+
// Adapt to the right colorspace if necessary
if (prim_src != prim_dst) {
struct mp_csp_primaries csp_src = mp_get_csp_primaries(prim_src),
@@ -1898,16 +1912,14 @@ static void pass_colormanage(struct gl_video *p, enum mp_csp_prim prim_src,
gl_sc_uniform_mat3(p->sc, "cms_matrix", true, &m[0][0]);
GLSL(color.rgb = cms_matrix * color.rgb;)
}
+
+ if (need_gamma)
+ pass_delinearize(p->sc, trc_dst);
+
if (p->use_lut_3d) {
gl_sc_uniform_sampler(p->sc, "lut_3d", GL_TEXTURE_3D, TEXUNIT_3DLUT);
- // For the 3DLUT we are arbitrarily using 2.4 as input gamma to reduce
- // the severity of quantization errors.
- GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);)
- GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
GLSL(color.rgb = texture3D(lut_3d, color.rgb).rgb;)
}
- if (need_gamma)
- pass_delinearize(p->sc, trc_dst);
}
static void pass_dither(struct gl_video *p)
@@ -2681,7 +2693,7 @@ static void check_gl_features(struct gl_video *p)
// GLES3 doesn't provide filtered 16 bit integer textures
// GLES2 doesn't even provide 3D textures
- if (p->use_lut_3d && !(have_3d_tex && have_float_tex)) {
+ if (p->use_lut_3d && (!have_3d_tex || gl->es)) {
p->use_lut_3d = false;
MP_WARN(p, "Disabling color management (GLES unsupported).\n");
}
@@ -3001,7 +3013,8 @@ void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd)
recreate_osd(p);
}
-struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g)
+struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g,
+ struct gl_lcms *cms)
{
if (gl->version < 210 && gl->es < 200) {
mp_err(log, "At least OpenGL 2.1 or OpenGL ES 2.0 required.\n");
@@ -3013,6 +3026,7 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g)
.gl = gl,
.global = g,
.log = log,
+ .cms = cms,
.opts = gl_video_opts_def,
.gl_target = GL_TEXTURE_2D,
.texture_16bit_depth = 16,
diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h
index 23b6c86cb1..4f9d497997 100644
--- a/video/out/opengl/video.h
+++ b/video/out/opengl/video.h
@@ -24,6 +24,7 @@
#include "sub/osd.h"
#include "common.h"
#include "utils.h"
+#include "lcms.h"
#include "video/out/filter_kernels.h"
// Texture units 0-5 are used by the video, and for free use by the passes
@@ -125,14 +126,15 @@ extern const struct gl_video_opts gl_video_opts_def;
struct gl_video;
struct vo_frame;
-struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g);
+struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g,
+ struct gl_lcms *cms);
void gl_video_uninit(struct gl_video *p);
void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd);
void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts);
bool gl_video_check_format(struct gl_video *p, int mp_format);
void gl_video_config(struct gl_video *p, struct mp_image_params *params);
void gl_video_set_output_depth(struct gl_video *p, int r, int g, int b);
-void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d);
+void gl_video_update_profile(struct gl_video *p);
void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo);
void gl_video_resize(struct gl_video *p, int vp_w, int vp_h,
struct mp_rect *src, struct mp_rect *dst,
diff --git a/video/out/opengl/video_shaders.c b/video/out/opengl/video_shaders.c
index 62feb47738..bea1bbf325 100644
--- a/video/out/opengl/video_shaders.c
+++ b/video/out/opengl/video_shaders.c
@@ -250,7 +250,8 @@ void pass_linearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
lessThan(vec3(0.04045), color.rgb));)
break;
case MP_CSP_TRC_BT_1886:
- GLSL(color.rgb = pow(color.rgb, vec3(1.961));)
+ // We don't have an actual black point, so we assume a perfect display
+ GLSL(color.rgb = pow(color.rgb, vec3(2.4));)
break;
case MP_CSP_TRC_GAMMA18:
GLSL(color.rgb = pow(color.rgb, vec3(1.8));)
@@ -284,7 +285,7 @@ void pass_delinearize(struct gl_shader_cache *sc, enum mp_csp_trc trc)
lessThanEqual(vec3(0.0031308), color.rgb));)
break;
case MP_CSP_TRC_BT_1886:
- GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.961));)
+ GLSL(color.rgb = pow(color.rgb, vec3(1.0/2.4));)
break;
case MP_CSP_TRC_GAMMA18:
GLSL(color.rgb = pow(color.rgb, vec3(1.0/1.8));)
diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c
index 7f4f13f882..dfef6ec500 100644
--- a/video/out/vo_opengl.c
+++ b/video/out/vo_opengl.c
@@ -217,7 +217,7 @@ static void call_request_hwdec_api(struct mp_hwdec_info *info,
vo_control(vo, VOCTRL_LOAD_HWDEC_API, (void *)api_name);
}
-static bool get_and_update_icc_profile(struct gl_priv *p, int *events)
+static void get_and_update_icc_profile(struct gl_priv *p, int *events)
{
bool has_profile = p->icc_opts->profile && p->icc_opts->profile[0];
if (p->icc_opts->profile_auto && !has_profile) {
@@ -233,17 +233,12 @@ static bool get_and_update_icc_profile(struct gl_priv *p, int *events)
}
gl_lcms_set_memory_profile(p->cms, &icc);
+ has_profile = true;
}
}
- struct lut3d *lut3d = NULL;
- if (!gl_lcms_has_changed(p->cms))
- return true;
- if (gl_lcms_get_lut3d(p->cms, &lut3d) && !lut3d)
- return false;
- gl_video_set_lut3d(p->renderer, lut3d);
- talloc_free(lut3d);
- return true;
+ if (has_profile)
+ gl_video_update_profile(p->renderer);
}
static void get_and_update_ambient_lighting(struct gl_priv *p, int *events)
@@ -416,19 +411,18 @@ static int preinit(struct vo *vo)
MP_VERBOSE(vo, "swap_control extension missing.\n");
}
- p->renderer = gl_video_init(p->gl, vo->log, vo->global);
+ p->cms = gl_lcms_init(p, vo->log, vo->global);
+ if (!p->cms)
+ goto err_out;
+ p->renderer = gl_video_init(p->gl, vo->log, vo->global, p->cms);
if (!p->renderer)
goto err_out;
gl_video_set_osd_source(p->renderer, vo->osd);
gl_video_set_options(p->renderer, p->renderer_opts);
gl_video_configure_queue(p->renderer, vo);
- p->cms = gl_lcms_init(p, vo->log, vo->global);
- if (!p->cms)
- goto err_out;
gl_lcms_set_options(p->cms, p->icc_opts);
- if (!get_and_update_icc_profile(p, &(int){0}))
- goto err_out;
+ get_and_update_icc_profile(p, &(int){0});
p->hwdec_info.load_api = call_request_hwdec_api;
p->hwdec_info.load_api_ctx = vo;
diff --git a/video/out/vo_opengl_cb.c b/video/out/vo_opengl_cb.c
index 7accfc1a92..40930fbcae 100644
--- a/video/out/vo_opengl_cb.c
+++ b/video/out/vo_opengl_cb.c
@@ -176,7 +176,7 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
mpgl_load_functions2(ctx->gl, get_proc_address, get_proc_address_ctx,
exts, ctx->log);
- ctx->renderer = gl_video_init(ctx->gl, ctx->log, ctx->global);
+ ctx->renderer = gl_video_init(ctx->gl, ctx->log, ctx->global, NULL);
if (!ctx->renderer)
return MPV_ERROR_UNSUPPORTED;