Skip to content

Commit 27aa71a

Browse files
committed
renderer: blend frames in linear light
This avoids flickering on high-contrast transitions. Also this matches reference frame blending implementation in mpv. Fixes: mpv-player/mpv#13108
1 parent 7cac5aa commit 27aa71a

1 file changed

Lines changed: 33 additions & 12 deletions

File tree

src/renderer.c

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct cached_frame {
2929
uint64_t signature;
3030
uint64_t params_hash; // for detecting `pl_render_params` changes
3131
struct pl_color_space color;
32+
struct pl_color_repr repr;
3233
struct pl_icc_profile profile;
3334
pl_rect2df crop;
3435
pl_tex tex;
@@ -3641,6 +3642,11 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
36413642
if (!out_w || !out_h)
36423643
goto fallback;
36433644

3645+
// Frames are cached and blended in linear light to avoid flickering
3646+
// on high-contrast transitions
3647+
struct pl_color_space mix_csp = target->color;
3648+
mix_csp.transfer = PL_COLOR_TRC_LINEAR;
3649+
36443650
int fidx = 0;
36453651
struct cached_frame frames[MAX_MIX_FRAMES];
36463652
float weights[MAX_MIX_FRAMES];
@@ -3778,7 +3784,7 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
37783784
f->tex->params.h == out_h &&
37793785
pl_rect2d_eq(f->crop, img->crop) &&
37803786
f->params_hash == par_info.hash &&
3781-
pl_color_space_equal(&f->color, &target->color) &&
3787+
pl_color_space_equal(&f->color, &mix_csp) &&
37823788
pl_icc_profile_equal(&f->profile, &target->profile);
37833789
}
37843790

@@ -3826,6 +3832,11 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
38263832
if (!pass_init(&inter_pass, true))
38273833
goto fail;
38283834

3835+
// Override transfer after pass_init, we want consistent mix_csp,
3836+
// and avoid any inference that happens in pass_fix_frames. This is
3837+
// later applied on main output pass.
3838+
inter_pass.target.color = mix_csp;
3839+
38293840
pass_begin_frame(&inter_pass);
38303841
if (!(ok = pass_read_image(&inter_pass)))
38313842
goto inter_pass_error;
@@ -3834,6 +3845,8 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
38343845
pass_convert_colors(&inter_pass);
38353846

38363847
pl_assert(inter_pass.img.sh); // guaranteed by `pass_convert_colors`
3848+
pl_assert(inter_pass.img.color.transfer == PL_COLOR_TRC_LINEAR);
3849+
38373850
pl_shader_set_alpha(inter_pass.img.sh, &inter_pass.img.repr,
38383851
PL_ALPHA_PREMULTIPLIED); // for frame mixing
38393852

@@ -3873,6 +3886,7 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
38733886
f->params_hash = par_info.hash;
38743887
f->crop = img->crop;
38753888
f->color = inter_pass.img.color;
3889+
f->repr = inter_pass.img.repr;
38763890
f->comps = inter_pass.img.comps;
38773891
f->profile = target->profile;
38783892
// fall through
@@ -3944,18 +3958,19 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
39443958

39453959
GLSL("color = textureLod("$", "$", 0.0); \n", tex, pos);
39463960

3947-
// Note: This ignores differences in ICC profile, which we decide to
3948-
// just simply not care about. Doing that properly would require
3949-
// converting between different image profiles, and the headache of
3950-
// finagling that state is just not worth it because this is an
3951-
// exceptionally unlikely hypothetical.
3952-
//
3953-
// This also ignores differences in HDR metadata, which we deliberately
3954-
// ignore because it causes aggressive shader recompilation.
3961+
// Usually a no-op. Handles mixed-colorspace frames when
3962+
// preserve_mixing_cache spans target changes. ICC diffs ignored
3963+
struct pl_color_repr frame_repr = frames[i].repr;
39553964
struct pl_color_space frame_csp = frames[i].color;
3956-
struct pl_color_space mix_csp = target->color;
3957-
frame_csp.hdr = mix_csp.hdr = (struct pl_hdr_metadata) {0};
3958-
pl_shader_color_map_ex(sh, NULL, pl_color_map_args(frame_csp, mix_csp));
3965+
// Ignore differences in HDR metadata, which may cause shader or lut
3966+
// recompilation. Note that when preserve_mixing_cache is false, frames
3967+
// will be always re-rendered with the target's HDR metadata.
3968+
frame_csp.hdr = mix_csp.hdr;
3969+
if (!pl_color_space_equal(&frame_csp, &mix_csp)) {
3970+
pl_shader_set_alpha(sh, &frame_repr, PL_ALPHA_INDEPENDENT);
3971+
pl_shader_color_map_ex(sh, NULL, pl_color_map_args(frame_csp, mix_csp));
3972+
}
3973+
pl_shader_set_alpha(sh, &frame_repr, PL_ALPHA_PREMULTIPLIED);
39593974

39603975
float weight = weights[i] / wsum;
39613976
GLSL("mix_color += vec4("$") * color; \n", SH_FLOAT_DYN(weight));
@@ -3980,6 +3995,12 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
39803995
},
39813996
};
39823997

3998+
// Re-encode to target transfer, this will in practice delinearize only.
3999+
if (!pl_color_space_equal(&mix_csp, &pass.img.color)) {
4000+
pl_shader_set_alpha(sh, &pass.img.repr, PL_ALPHA_INDEPENDENT);
4001+
pl_shader_color_map_ex(sh, NULL, pl_color_map_args(mix_csp, pass.img.color));
4002+
}
4003+
39834004
if (!pass_output_target(&pass))
39844005
goto fallback;
39854006

0 commit comments

Comments
 (0)