@@ -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