mirror of
https://github.com/mpv-player/mpv
synced 2025-03-29 15:00:27 +00:00
filter_kernels: add radius cutoff functionality
This allows filter functions to be prematurely cut off once their contributions start becoming insignificant. This effectively prevents wasted GPU time sampling from parts of the function that are essentially reduced to zero by the window function, providing anywhere from a 10% to 20% speedup. (5700μs -> 4700μs for me)
This commit is contained in:
parent
41b3b11669
commit
8854a2bef6
@ -4004,6 +4004,14 @@ The following video options are currently all specific to ``--vo=opengl`` and
|
||||
fringes of black, mostly around moving edges) in exchange for potentially
|
||||
adding more blur.
|
||||
|
||||
``--scale-cutoff=<value>``, ``--cscale-cutoff=<value>``, ``--dscale-cutoff=<value>``
|
||||
Cut off the filter kernel prematurely once the value range drops below
|
||||
this threshold. Doing so allows more aggressive pruning of skippable
|
||||
coefficients by disregarding parts of the LUT which are effectively zeroed
|
||||
out by the window function. Only affects polar (EWA) filters. The default
|
||||
is 0.001 for each, which is perceptually transparent but provides a 10%-20%
|
||||
speedup, depending on the exact radius and filter kernel chosen.
|
||||
|
||||
``--scale-taper=<value>``, ``--scale-wtaper=<value>``, ``--dscale-taper=<value>``, ``--dscale-wtaper=<value>``, ``--cscale-taper=<value>``, ``--cscale-wtaper=<value>``, ``--tscale-taper=<value>``, ``--tscale-wtaper=<value>``
|
||||
Kernel/window taper factor. Increasing this flattens the filter function.
|
||||
Value range is 0 to 1. A value of 0 (the default) means no flattening, a
|
||||
|
@ -152,10 +152,14 @@ static void mp_compute_weights(struct filter_kernel *filter, double f,
|
||||
void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
|
||||
{
|
||||
if (filter->polar) {
|
||||
filter->radius_cutoff = 0.0;
|
||||
// Compute a 1D array indexed by radius
|
||||
for (int x = 0; x < count; x++) {
|
||||
double r = x * filter->f.radius / (count - 1);
|
||||
out_array[x] = sample_filter(filter, r);
|
||||
|
||||
if (fabs(out_array[x]) > filter->value_cutoff)
|
||||
filter->radius_cutoff = r;
|
||||
}
|
||||
} else {
|
||||
// Compute a 2D array indexed by subpixel position
|
||||
|
@ -29,6 +29,7 @@ struct filter_kernel {
|
||||
struct filter_window f; // the kernel itself
|
||||
struct filter_window w; // window storage
|
||||
bool clamp; // clamp to the range [0-1]
|
||||
double value_cutoff; // discard all contributions below this value (polar)
|
||||
// Constant values
|
||||
const char *window; // default window
|
||||
bool polar; // whether or not the filter uses polar coordinates
|
||||
@ -38,6 +39,7 @@ struct filter_kernel {
|
||||
// function radius to the possibly wider
|
||||
// (in the case of downsampling) filter sample
|
||||
// radius.
|
||||
double radius_cutoff; // the true radius at which we can cut off the filter
|
||||
};
|
||||
|
||||
extern const struct filter_window mp_filter_windows[];
|
||||
|
@ -294,10 +294,13 @@ static const struct gl_video_opts gl_video_opts_def = {
|
||||
.sigmoid_center = 0.75,
|
||||
.sigmoid_slope = 6.5,
|
||||
.scaler = {
|
||||
{{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // scale
|
||||
{{NULL, .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // dscale
|
||||
{{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // cscale
|
||||
{{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}},
|
||||
{{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}},
|
||||
.cutoff = 0.001}, // scale
|
||||
{{NULL, .params={NAN, NAN}}, {.params = {NAN, NAN}},
|
||||
.cutoff = 0.001}, // dscale
|
||||
{{"bilinear", .params={NAN, NAN}}, {.params = {NAN, NAN}},
|
||||
.cutoff = 0.001}, // cscale
|
||||
{{"mitchell", .params={NAN, NAN}}, {.params = {NAN, NAN}},
|
||||
.clamp = 1, }, // tscale
|
||||
},
|
||||
.scaler_resizes_only = 1,
|
||||
@ -324,6 +327,7 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
|
||||
OPT_FLOAT(n"-param1", scaler[i].kernel.params[0], 0), \
|
||||
OPT_FLOAT(n"-param2", scaler[i].kernel.params[1], 0), \
|
||||
OPT_FLOAT(n"-blur", scaler[i].kernel.blur, 0), \
|
||||
OPT_FLOATRANGE(n"-cutoff", scaler[i].cutoff, 0, 0.0, 1.0), \
|
||||
OPT_FLOATRANGE(n"-taper", scaler[i].kernel.taper, 0, 0.0, 1.0), \
|
||||
OPT_FLOAT(n"-wparam", scaler[i].window.params[0], 0), \
|
||||
OPT_FLOAT(n"-wblur", scaler[i].window.blur, 0), \
|
||||
@ -1437,6 +1441,7 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
|
||||
scaler->kernel->f.radius = conf->radius;
|
||||
|
||||
scaler->kernel->clamp = conf->clamp;
|
||||
scaler->kernel->value_cutoff = conf->cutoff;
|
||||
|
||||
scaler->insufficient = !mp_init_filter(scaler->kernel, sizes, scale_factor);
|
||||
|
||||
|
@ -43,6 +43,7 @@ struct scaler_config {
|
||||
struct scaler_fun window;
|
||||
float radius;
|
||||
float antiring;
|
||||
float cutoff;
|
||||
int clamp;
|
||||
};
|
||||
|
||||
|
@ -108,8 +108,10 @@ void pass_sample_separated_gen(struct gl_shader_cache *sc, struct scaler *scaler
|
||||
void pass_sample_polar(struct gl_shader_cache *sc, struct scaler *scaler)
|
||||
{
|
||||
double radius = scaler->kernel->f.radius * scaler->kernel->filter_scale;
|
||||
int bound = ceil(radius);
|
||||
double radius_cutoff = scaler->kernel->radius_cutoff;
|
||||
int bound = ceil(radius_cutoff);
|
||||
bool use_ar = scaler->conf.antiring > 0;
|
||||
|
||||
GLSL(color = vec4(0.0);)
|
||||
GLSLF("{\n");
|
||||
GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));)
|
||||
@ -130,12 +132,13 @@ void pass_sample_polar(struct gl_shader_cache *sc, struct scaler *scaler)
|
||||
int xx = x > 0 ? x-1 : x;
|
||||
double dmax = sqrt(xx*xx + yy*yy);
|
||||
// Skip samples definitely outside the radius
|
||||
if (dmax >= radius)
|
||||
if (dmax >= radius_cutoff)
|
||||
continue;
|
||||
GLSLF("d = length(vec2(%d.0, %d.0) - fcoord)/%f;\n", x, y, radius);
|
||||
// Check for samples that might be skippable
|
||||
if (dmax >= radius - M_SQRT2)
|
||||
GLSLF("if (d < 1.0) {\n");
|
||||
bool maybe_skippable = dmax >= radius_cutoff - M_SQRT2;
|
||||
if (maybe_skippable)
|
||||
GLSLF("if (d < %f) {\n", radius_cutoff / radius);
|
||||
if (scaler->gl_target == GL_TEXTURE_1D) {
|
||||
GLSLF("w = texture1D(lut, LUT_POS(d, %d.0)).r;\n",
|
||||
scaler->lut_size);
|
||||
@ -150,7 +153,7 @@ void pass_sample_polar(struct gl_shader_cache *sc, struct scaler *scaler)
|
||||
GLSL(lo = min(lo, c);)
|
||||
GLSL(hi = max(hi, c);)
|
||||
}
|
||||
if (dmax >= radius - M_SQRT2)
|
||||
if (maybe_skippable)
|
||||
GLSLF("}\n");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user