From 31a5f08f135535e5c284d3994d2fad17ce8e2e53 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Sun, 15 Mar 2015 07:11:51 +0100 Subject: vo_opengl: add oversample support for tscale This is interesting mainly because it's essentially equivalent to the old smoothmotion algorithm. As such, it is now the default for tscale. --- DOCS/man/vo.rst | 10 +++++++- video/out/gl_video.c | 69 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 54 insertions(+), 25 deletions(-) diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst index b59f97b6a9..f15ae9a3fb 100644 --- a/DOCS/man/vo.rst +++ b/DOCS/man/vo.rst @@ -327,6 +327,14 @@ Available video output drivers are: ``scale-param1`` and ``scale-param2``. This filter is very good at downscaling (see ``scale-down``). + ``oversample`` + A version of nearest neighbour that (naively) oversamples pixels, + so that pixels overlapping edges get linearly interpolated instead + of rounded. This essentially removes the small imperfections and + judder artifacts caused by nearest-neighbour interpolation, in + exchange for adding some blur. This filter is good at temporal + interpolation, and also known as "smoothmotion" (see ``tscale``). + There are some more filters, but most are not as useful. For a complete list, pass ``help`` as value, e.g.:: @@ -474,7 +482,7 @@ Available video output drivers are: only used if ``interpolation`` is enabled. The only valid choices for ``tscale`` are separable convolution filters (use ``tscale=help`` to get a list). The other options (``tscale-param1`` etc.) are - analogous to their ``scale`` counterparts. The default is ``mitchell``. + analogous to their ``scale`` counterparts. The default is ``oversample``. Note that the maximum supported filter radius is currently 3, and that using filters with larger radius may introduce isues when pausing or diff --git a/video/out/gl_video.c b/video/out/gl_video.c index 1b75b680c3..17dd121709 100644 --- a/video/out/gl_video.c +++ b/video/out/gl_video.c @@ -62,6 +62,10 @@ static const char *const fixed_scale_filters[] = { "oversample", NULL }; +static const char *const fixed_tscale_filters[] = { + "oversample", + NULL +}; // must be sorted, and terminated with 0 int filter_sizes[] = @@ -321,7 +325,7 @@ const struct gl_video_opts gl_video_opts_def = { .fbo_format = GL_RGBA, .sigmoid_center = 0.75, .sigmoid_slope = 6.5, - .scalers = { "bilinear", "bilinear", "mitchell" }, + .scalers = { "bilinear", "bilinear", "oversample" }, .dscaler = "bilinear", .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}}, .scaler_radius = {3, 3, 3}, @@ -339,7 +343,7 @@ const struct gl_video_opts gl_video_opts_hq_def = { .sigmoid_center = 0.75, .sigmoid_slope = 6.5, .sigmoid_upscaling = 1, - .scalers = { "spline36", "bilinear", "mitchell" }, + .scalers = { "spline36", "bilinear", "oversample" }, .dscaler = "mitchell", .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}}, .scaler_radius = {3, 3, 3}, @@ -427,7 +431,6 @@ const struct m_sub_options gl_video_conf = { OPT_REMOVED("cscale-down", "chroma is never downscaled"), OPT_REMOVED("scale-sep", "this is set automatically whenever sane"), OPT_REMOVED("indirect", "this is set automatically whenever sane"), - OPT_REMOVED("smoothmotion-threshold", "to be readded as a proper scaler"), OPT_REPLACED("lscale", "scale"), OPT_REPLACED("lscale-down", "scale-down"), @@ -441,6 +444,7 @@ const struct m_sub_options gl_video_conf = { OPT_REPLACED("cantiring", "cscale-antiring"), OPT_REPLACED("srgb", "target-prim=srgb:target-trc=srgb"), OPT_REPLACED("smoothmotion", "interpolation"), + OPT_REPLACED("smoothmotion-threshold", "tscale-param1"), {0} }, @@ -1734,10 +1738,15 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo, // surface_end. struct scaler *tscale = &p->scalers[2]; reinit_scaler(p, 2, p->opts.scalers[2], 1, tscale_sizes); - assert(tscale->kernel && !tscale->kernel->polar); - - int size = ceil(tscale->kernel->size); - assert(size <= TEXUNIT_VIDEO_NUM); + bool oversample = strcmp(tscale->name, "oversample") == 0; + int size; + if (oversample) { + size = 2; + } else { + assert(tscale->kernel && !tscale->kernel->polar); + size = ceil(tscale->kernel->size); + assert(size <= TEXUNIT_VIDEO_NUM); + } int radius = size/2; int surface_now = p->surface_now; @@ -1781,22 +1790,31 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo, } else { int64_t pts_now = p->surfaces[surface_now].pts, pts_nxt = p->surfaces[surface_nxt].pts; - double fscale = pts_nxt - pts_now; - double fcoord = (t->next_vsync - pts_now) / fscale; - gl_sc_uniform_f(p->sc, "fcoord", fcoord); - - MP_STATS(p, "frame-mix"); - MP_DBG(p, "inter frame ppts: %lld, pts: %lld, vsync: %lld, mix: %f\n", - (long long)pts_now, (long long)pts_nxt, - (long long)t->next_vsync, fcoord); - + double fscale = pts_nxt - pts_now, mix; + if (oversample) { + double vsync_interval = t->next_vsync - t->prev_vsync, + threshold = isnan(tscale->params[0]) ? 0 : tscale->params[0]; + mix = (pts_nxt - t->next_vsync) / vsync_interval; + mix = mix <= 0 + threshold ? 0 : mix; + mix = mix >= 1 - threshold ? 1 : mix; + gl_sc_uniform_f(p->sc, "inter_coeff", mix); + GLSL(vec4 color = mix(texture(texture1, texcoord1), + texture(texture0, texcoord0), + inter_coeff);) + } else { + mix = (t->next_vsync - pts_now) / fscale; + gl_sc_uniform_f(p->sc, "fcoord", mix); + pass_sample_separated_gen(p, tscale, 0, 0); + } for (int i = 0; i < size; i++) { pass_load_fbotex(p, &p->surfaces[fbosurface_wrap(surface_bse+i)].fbotex, i, vp_w, vp_h); } - pass_sample_separated_gen(p, tscale, 0, 0); + MP_STATS(p, "frame-mix"); + MP_DBG(p, "inter frame ppts: %lld, pts: %lld, vsync: %lld, mix: %f\n", + (long long)pts_now, (long long)pts_nxt, + (long long)t->next_vsync, mix); p->is_interpolated = true; - } pass_draw_to_screen(p, fbo); @@ -2368,7 +2386,7 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct osd_state *osd .scalers = { { .index = 0, .name = "bilinear" }, { .index = 1, .name = "bilinear" }, - { .index = 2, .name = "mitchell" }, + { .index = 2, .name = "oversample" }, }, .sc = gl_sc_create(gl, log), }; @@ -2387,8 +2405,10 @@ static const char *handle_scaler_opt(const char *name, bool tscale) if (kernel && (!tscale || !kernel->polar)) return kernel->name; - for (const char *const *filter = fixed_scale_filters; *filter; filter++) { - if (strcmp(*filter, name) == 0 && !tscale) + for (const char *const *filter = tscale ? fixed_tscale_filters + : fixed_scale_filters; + *filter; filter++) { + if (strcmp(*filter, name) == 0) return *filter; } } @@ -2452,9 +2472,10 @@ static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt, } if (r < 1) { mp_info(log, "Available scalers:\n"); - if (!tscale) { - for (const char *const *filter = fixed_scale_filters; *filter; filter++) - mp_info(log, " %s\n", *filter); + for (const char *const *filter = tscale ? fixed_tscale_filters + : fixed_scale_filters; + *filter; filter++) { + mp_info(log, " %s\n", *filter); } for (int n = 0; mp_filter_kernels[n].name; n++) { if (!tscale || !mp_filter_kernels[n].polar) -- cgit v1.2.3