vo_opengl: adjust the rules for linearization

Two changes, compounded into one since they affect the same logic:

1. Never use linearization for HDR downscaling
2. Always use linearization for interpolation

Instead of fixing p->use_linear at the beginning of pass_render_frame,
we flip it on "dynamically" as needed. I plan on killing this
p->use_linear frame (along with other per-pass metadata) and moving them
into their own struct for tracking the "current" state of the video, but
that's a separate/upcoming refactor.

As a small bonus, reduce some code duplication in the interpolation
logic.

Fixes #4631
This commit is contained in:
Niklas Haas 2017-07-24 23:22:30 +02:00
parent 13ef6bcf6f
commit 241d5ebc46
No known key found for this signature in database
GPG Key ID: 9A09076581B27402
2 changed files with 45 additions and 22 deletions

View File

@ -4110,7 +4110,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
``--linear-scaling``
Scale in linear light. It should only be used with a
``--opengl-fbo-format`` that has at least 16 bit precision.
``--opengl-fbo-format`` that has at least 16 bit precision. This option
has no effect on HDR content.
``--correct-downscaling``
When using convolution based filters, extend the filter size when
@ -4133,7 +4134,8 @@ The following video options are currently all specific to ``--vo=opengl`` and
the ``--tscale`` setting.
Note that this relies on vsync to work, see ``--opengl-swapinterval`` for
more information.
more information. It should also only be used with an ``--opengl-fbo-format``
that has at least 16 bit precision.
``--interpolation-threshold=<0..1,-1>``
Threshold below which frame ratio interpolation gets disabled (default:

View File

@ -2326,12 +2326,21 @@ static void pass_scale_main(struct gl_video *p)
// Pre-conversion, like linear light/sigmoidization
GLSLF("// scaler pre-conversion\n");
if (p->use_linear) {
bool use_linear = p->opts.linear_scaling || p->opts.sigmoid_upscaling;
// Linear light downscaling results in nasty artifacts for HDR curves due
// to the potentially extreme brightness differences severely compounding
// any ringing. So just scale in gamma light instead.
if (mp_trc_is_hdr(p->image_params.color.gamma) && downscaling)
use_linear = false;
if (use_linear) {
p->use_linear = true;
pass_linearize(p->sc, p->image_params.color.gamma);
pass_opt_hook_point(p, "LINEAR", NULL);
}
bool use_sigmoid = p->use_linear && p->opts.sigmoid_upscaling && upscaling;
bool use_sigmoid = use_linear && p->opts.sigmoid_upscaling && upscaling;
float sig_center, sig_slope, sig_offset, sig_scale;
if (use_sigmoid) {
// Coefficients for the sigmoidal transform are taken from the
@ -2692,7 +2701,6 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, uint64_t
if (p->dumb_mode)
return true;
p->use_linear = p->opts.linear_scaling || p->opts.sigmoid_upscaling;
pass_read_video(p);
pass_opt_hook_point(p, "NATIVE", &p->texture_offset);
pass_convert_yuv(p);
@ -2800,13 +2808,34 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo)
finish_pass_direct(p, fbo, p->vp_w, p->vp_h, &p->dst_rect);
}
// Draws an interpolate frame to fbo, based on the frame timing in t
static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
int fbo)
static bool update_fbosurface(struct gl_video *p, struct mp_image *mpi,
uint64_t id, struct fbosurface *surf)
{
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0;
pass_info_reset(p, false);
if (!pass_render_frame(p, mpi, id))
return false;
// Frame blending should always be done in linear light to preserve the
// overall brightness, otherwise this will result in flashing dark frames
// because mixing in compressed light artificially darkens the results
if (!p->use_linear) {
p->use_linear = true;
pass_linearize(p->sc, p->image_params.color.gamma);
}
finish_pass_fbo(p, &surf->fbotex, vp_w, vp_h, FBOTEX_FUZZY);
surf->id = id;
surf->pts = mpi->pts;
return true;
}
// Draws an interpolate frame to fbo, based on the frame timing in t
static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
int fbo)
{
bool is_new = false;
// Reset the queue completely if this is a still image, to avoid any
@ -2818,15 +2847,11 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
// First of all, figure out if we have a frame available at all, and draw
// it manually + reset the queue if not
if (p->surfaces[p->surface_now].id == 0) {
is_new = true;
pass_info_reset(p, false);
if (!pass_render_frame(p, t->current, t->frame_id))
struct fbosurface *now = &p->surfaces[p->surface_now];
if (!update_fbosurface(p, t->current, t->frame_id, now))
return;
finish_pass_fbo(p, &p->surfaces[p->surface_now].fbotex,
vp_w, vp_h, FBOTEX_FUZZY);
p->surfaces[p->surface_now].id = p->image.id;
p->surfaces[p->surface_now].pts = p->image.mpi->pts;
p->surface_idx = p->surface_now;
is_new = true;
}
// Find the right frame for this instant
@ -2881,16 +2906,12 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
continue;
if (f_id > p->surfaces[p->surface_idx].id) {
is_new = true;
pass_info_reset(p, false);
if (!pass_render_frame(p, f, f_id))
struct fbosurface *dst = &p->surfaces[surface_dst];
if (!update_fbosurface(p, f, f_id, dst))
return;
finish_pass_fbo(p, &p->surfaces[surface_dst].fbotex,
vp_w, vp_h, FBOTEX_FUZZY);
p->surfaces[surface_dst].id = f_id;
p->surfaces[surface_dst].pts = f->pts;
p->surface_idx = surface_dst;
surface_dst = fbosurface_wrap(surface_dst + 1);
is_new = true;
}
}