1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-28 01:52:19 +00:00

vo_gpu{,_next}: convert scale options to type choice

This allows Tab completing them in the console and zsh, and using cycle
scale.

jinc is also added to --tscale=help's output, while before it was
missing because validate_scaler_opt() skipped filter windows with the
same name as a filter kernel, but the jinc kernel was skipped with
--tscale because it is polar.
This commit is contained in:
Guido Cella 2024-05-24 18:59:35 +02:00 committed by Kacper Michajłow
parent d84a7c5112
commit dd77d0a7da
5 changed files with 274 additions and 244 deletions

View File

@ -32,23 +32,19 @@
// NOTE: all filters are designed for discrete convolution
const struct filter_window *mp_find_filter_window(const char *name)
const struct filter_window *mp_find_filter_window(enum scaler_filter function)
{
if (!name)
return NULL;
for (const struct filter_window *w = mp_filter_windows; w->name; w++) {
if (strcmp(w->name, name) == 0)
for (const struct filter_window *w = mp_filter_windows; w->function; w++) {
if (w->function == function)
return w;
}
return NULL;
}
const struct filter_kernel *mp_find_filter_kernel(const char *name)
const struct filter_kernel *mp_find_filter_kernel(enum scaler_filter function)
{
if (!name)
return NULL;
for (const struct filter_kernel *k = mp_filter_kernels; k->f.name; k++) {
if (strcmp(k->f.name, name) == 0)
for (const struct filter_kernel *k = mp_filter_kernels; k->f.function; k++) {
if (k->f.function == function)
return k;
}
return NULL;
@ -335,21 +331,21 @@ static double sphinx(params *p, double x)
}
const struct filter_window mp_filter_windows[] = {
{"box", 1, box},
{"triangle", 1, triangle},
{"bartlett", 1, triangle},
{"cosine", M_PI_2, cosine},
{"hanning", 1, hanning},
{"tukey", 1, hanning, .taper = 0.5},
{"hamming", 1, hamming},
{"quadric", 1.5, quadric},
{"welch", 1, welch},
{"kaiser", 1, kaiser, .params = {6.33, NAN} },
{"blackman", 1, blackman, .params = {0.16, NAN} },
{"gaussian", 2, gaussian, .params = {1.00, NAN} },
{"sinc", 1, sinc},
{"jinc", 1.2196698912665045, jinc},
{"sphinx", 1.4302966531242027, sphinx},
{WINDOW_BOX, 1, box},
{WINDOW_TRIANGLE, 1, triangle},
{WINDOW_BARTLETT, 1, triangle},
{WINDOW_COSINE, M_PI_2, cosine},
{WINDOW_HANNING, 1, hanning},
{WINDOW_TUKEY, 1, hanning, .taper = 0.5},
{WINDOW_HAMMING, 1, hamming},
{WINDOW_QUADRIC, 1.5, quadric},
{WINDOW_WELCH, 1, welch},
{WINDOW_KAISER, 1, kaiser, .params = {6.33, NAN} },
{WINDOW_BLACKMAN, 1, blackman, .params = {0.16, NAN} },
{WINDOW_GAUSSIAN, 2, gaussian, .params = {1.00, NAN} },
{WINDOW_SINC, 1, sinc},
{WINDOW_JINC, 1.2196698912665045, jinc},
{WINDOW_SPHINX, 1.4302966531242027, sphinx},
{0}
};
@ -358,54 +354,54 @@ const struct filter_window mp_filter_windows[] = {
const struct filter_kernel mp_filter_kernels[] = {
// Spline filters
{{"spline16", 2, spline16}},
{{"spline36", 3, spline36}},
{{"spline64", 4, spline64}},
{{SCALER_SPLINE16, 2, spline16}},
{{SCALER_SPLINE36, 3, spline36}},
{{SCALER_SPLINE64, 4, spline64}},
// Sinc filters
{{"sinc", 2, sinc, .resizable = true}},
{{"lanczos", 3, sinc, .resizable = true}, .window = "sinc"},
{{"ginseng", 3, sinc, .resizable = true}, .window = "jinc"},
{{SCALER_SINC, 2, sinc, .resizable = true}},
{{SCALER_LANCZOS, 3, sinc, .resizable = true}, .window = WINDOW_SINC},
{{SCALER_GINSENG, 3, sinc, .resizable = true}, .window = WINDOW_JINC},
// Jinc filters
{{"jinc", JINC_R3, jinc, .resizable = true}, .polar = true},
{{"ewa_lanczos", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "jinc"},
{{"ewa_hanning", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "hanning" },
{{"ewa_ginseng", JINC_R3, jinc, .resizable = true}, .polar = true, .window = "sinc"},
{{SCALER_JINC, JINC_R3, jinc, .resizable = true}, .polar = true},
{{SCALER_EWA_LANCZOS, JINC_R3, jinc, .resizable = true}, .polar = true, .window = WINDOW_JINC},
{{SCALER_EWA_HANNING, JINC_R3, jinc, .resizable = true}, .polar = true, .window = WINDOW_HANNING},
{{SCALER_EWA_GINSENG, JINC_R3, jinc, .resizable = true}, .polar = true, .window = WINDOW_SINC},
// Slightly sharpened to minimize the 1D step response error (to better
// preserve horizontal/vertical lines)
{{"ewa_lanczossharp", JINC_R3, jinc, .blur = 0.9812505837223707, .resizable = true},
.polar = true, .window = "jinc"},
{{SCALER_EWA_LANCZOSSHARP, JINC_R3, jinc, .blur = 0.9812505837223707, .resizable = true},
.polar = true, .window = WINDOW_JINC},
// Similar to the above, but sharpened substantially to the point of
// minimizing the total impulse response error on an integer grid. Tends
// to preserve hash patterns well. Very sharp but rings a lot.
{{"ewa_lanczos4sharpest", JINC_R4, jinc, .blur = 0.8845120932605005, .resizable = true},
.polar = true, .window = "jinc"},
{{SCALER_EWA_LANCZOS4SHARPEST, JINC_R4, jinc, .blur = 0.8845120932605005, .resizable = true},
.polar = true, .window = WINDOW_JINC},
// Similar to the above, but softened instead, to make even/odd integer
// contributions exactly symmetrical. Designed to smooth out hash patterns.
{{"ewa_lanczossoft", JINC_R3, jinc, .blur = 1.0164667662867047, .resizable = true},
.polar = true, .window = "jinc"},
{{SCALER_EWA_LANCZOSSOFT, JINC_R3, jinc, .blur = 1.0164667662867047, .resizable = true},
.polar = true, .window = WINDOW_JINC},
// Very soft (blurred) hanning-windowed jinc; removes almost all aliasing.
// Blur parameter picked to match orthogonal and diagonal contributions
{{"haasnsoft", JINC_R3, jinc, .blur = 1.11, .resizable = true},
.polar = true, .window = "hanning"},
{{SCALER_HAASNSOFT, JINC_R3, jinc, .blur = 1.11, .resizable = true},
.polar = true, .window = WINDOW_HANNING},
// Cubic filters
{{"bicubic", 2, cubic_bc, .params = {1.0, 0.0} }},
{{"hermite", 1, cubic_bc, .params = {0.0, 0.0} }},
{{"catmull_rom", 2, cubic_bc, .params = {0.0, 0.5} }},
{{"mitchell", 2, cubic_bc, .params = {1.0/3.0, 1.0/3.0} }},
{{"robidoux", 2, cubic_bc, .params = {12 / (19 + 9 * M_SQRT2),
113 / (58 + 216 * M_SQRT2)} }},
{{"robidouxsharp", 2, cubic_bc, .params = {6 / (13 + 7 * M_SQRT2),
7 / (2 + 12 * M_SQRT2)} }},
{{"ewa_robidoux", 2, cubic_bc, .params = {12 / (19 + 9 * M_SQRT2),
113 / (58 + 216 * M_SQRT2)}},
.polar = true},
{{"ewa_robidouxsharp", 2,cubic_bc, .params = {6 / (13 + 7 * M_SQRT2),
7 / (2 + 12 * M_SQRT2)}},
.polar = true},
{{SCALER_BICUBIC, 2, cubic_bc, .params = {1.0, 0.0} }},
{{SCALER_HERMITE, 1, cubic_bc, .params = {0.0, 0.0} }},
{{SCALER_CATMULL_ROM, 2, cubic_bc, .params = {0.0, 0.5} }},
{{SCALER_MITCHELL, 2, cubic_bc, .params = {1.0/3.0, 1.0/3.0} }},
{{SCALER_ROBIDOUX, 2, cubic_bc,
.params = {12 / (19 + 9 * M_SQRT2), 113 / (58 + 216 * M_SQRT2)} }},
{{SCALER_ROBIDOUXSHARP, 2, cubic_bc,
.params = {6 / (13 + 7 * M_SQRT2), 7 / (2 + 12 * M_SQRT2)} }},
{{SCALER_EWA_ROBIDOUX, 2, cubic_bc,
.params = {12 / (19 + 9 * M_SQRT2), 113 / (58 + 216 * M_SQRT2)}},
.polar = true},
{{SCALER_EWA_ROBIDOUXSHARP, 2,cubic_bc,
.params = {6 / (13 + 7 * M_SQRT2), 7 / (2 + 12 * M_SQRT2)}},
.polar = true},
// Miscellaneous filters
{{"box", 1, box, .resizable = true}},
{{"nearest", 0.5, box}},
{{"triangle", 1, triangle, .resizable = true}},
{{"gaussian", 2, gaussian, .params = {1.0, NAN}, .resizable = true}},
{{SCALER_BOX, 1, box, .resizable = true}},
{{SCALER_NEAREST, 0.5, box}},
{{SCALER_TRIANGLE, 1, triangle, .resizable = true}},
{{SCALER_GAUSSIAN, 2, gaussian, .params = {1.0, NAN}, .resizable = true}},
{{0}}
};

View File

@ -14,8 +14,58 @@
#include <stdbool.h>
enum scaler_filter {
SCALER_INHERIT,
SCALER_BILINEAR,
SCALER_BICUBIC_FAST,
SCALER_OVERSAMPLE,
SCALER_LINEAR,
SCALER_SPLINE16,
SCALER_SPLINE36,
SCALER_SPLINE64,
SCALER_SINC,
SCALER_LANCZOS,
SCALER_GINSENG,
SCALER_JINC,
SCALER_EWA_LANCZOS,
SCALER_EWA_HANNING,
SCALER_EWA_GINSENG,
SCALER_EWA_LANCZOSSHARP,
SCALER_EWA_LANCZOS4SHARPEST,
SCALER_EWA_LANCZOSSOFT,
SCALER_HAASNSOFT,
SCALER_BICUBIC,
SCALER_HERMITE,
SCALER_CATMULL_ROM,
SCALER_MITCHELL,
SCALER_ROBIDOUX,
SCALER_ROBIDOUXSHARP,
SCALER_EWA_ROBIDOUX,
SCALER_EWA_ROBIDOUXSHARP,
SCALER_BOX,
SCALER_NEAREST,
SCALER_TRIANGLE,
SCALER_GAUSSIAN,
WINDOW_PREFERRED,
WINDOW_BOX,
WINDOW_TRIANGLE,
WINDOW_BARTLETT,
WINDOW_COSINE,
WINDOW_HANNING,
WINDOW_TUKEY,
WINDOW_HAMMING,
WINDOW_QUADRIC,
WINDOW_WELCH,
WINDOW_KAISER,
WINDOW_BLACKMAN,
WINDOW_GAUSSIAN,
WINDOW_SINC,
WINDOW_JINC,
WINDOW_SPHINX,
};
struct filter_window {
const char *name;
enum scaler_filter function;
double radius; // Preferred radius, should only be changed if resizable
double (*weight)(struct filter_window *k, double x);
bool resizable; // Filter supports any given radius
@ -30,7 +80,7 @@ struct filter_kernel {
struct filter_window w; // window storage
double clamp; // clamping factor, affects negative weights
// Constant values
const char *window; // default window
enum scaler_filter window; // default window
bool polar; // whether or not the filter uses polar coordinates
// The following values are set by mp_init_filter() at runtime.
int size; // number of coefficients (may depend on radius)
@ -45,8 +95,8 @@ struct filter_kernel {
extern const struct filter_window mp_filter_windows[];
extern const struct filter_kernel mp_filter_kernels[];
const struct filter_window *mp_find_filter_window(const char *name);
const struct filter_kernel *mp_find_filter_kernel(const char *name);
const struct filter_window *mp_find_filter_window(enum scaler_filter function);
const struct filter_kernel *mp_find_filter_kernel(enum scaler_filter function);
bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
double scale);

View File

@ -45,20 +45,6 @@
#include "video/out/dither.h"
#include "video/out/vo.h"
// scale/cscale arguments that map directly to shader filter routines.
// Note that the convolution filters are not included in this list.
static const char *const fixed_scale_filters[] = {
"bilinear",
"bicubic_fast",
"oversample",
NULL
};
static const char *const fixed_tscale_filters[] = {
"oversample",
"linear",
NULL
};
// must be sorted, and terminated with 0
int filter_sizes[] =
{2, 4, 6, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 0};
@ -295,6 +281,86 @@ struct gl_video {
bool correct_downscaling_warned;
};
#define FIXED_SCALE_KERNELS \
{"bilinear", SCALER_BILINEAR}, \
{"bicubic_fast", SCALER_BICUBIC_FAST}, \
{"oversample", SCALER_OVERSAMPLE}, \
#define NON_POLAR_FILTER_KERNELS \
{"spline16", SCALER_SPLINE16}, \
{"spline36", SCALER_SPLINE36}, \
{"spline64", SCALER_SPLINE64}, \
{"sinc", SCALER_SINC}, \
{"lanczos", SCALER_LANCZOS}, \
{"ginseng", SCALER_GINSENG}, \
{"bicubic", SCALER_BICUBIC}, \
{"hermite", SCALER_HERMITE}, \
{"catmull_rom", SCALER_CATMULL_ROM}, \
{"mitchell", SCALER_MITCHELL}, \
{"robidoux", SCALER_ROBIDOUX}, \
{"robidouxsharp", SCALER_ROBIDOUXSHARP}, \
{"box", SCALER_BOX}, \
{"nearest", SCALER_NEAREST}, \
{"triangle", SCALER_TRIANGLE}, \
{"gaussian", SCALER_GAUSSIAN}, \
#define POLAR_FILTER_KERNELS \
{"jinc", SCALER_JINC}, \
{"ewa_lanczos", SCALER_EWA_LANCZOS}, \
{"ewa_hanning", SCALER_EWA_HANNING}, \
{"ewa_ginseng", SCALER_EWA_GINSENG}, \
{"ewa_lanczossharp", SCALER_EWA_LANCZOSSHARP}, \
{"ewa_lanczos4sharpest", SCALER_EWA_LANCZOS4SHARPEST}, \
{"ewa_lanczossoft", SCALER_EWA_LANCZOSSOFT}, \
{"haasnsoft", SCALER_HAASNSOFT}, \
{"ewa_robidoux", SCALER_EWA_ROBIDOUX}, \
{"ewa_robidouxsharp", SCALER_EWA_ROBIDOUXSHARP}, \
#define FILTER_WINDOWS \
{"bartlett", WINDOW_BARTLETT}, \
{"cosine", WINDOW_COSINE}, \
{"hanning", WINDOW_HANNING}, \
{"tukey", WINDOW_TUKEY}, \
{"hamming", WINDOW_HAMMING}, \
{"quadric", WINDOW_QUADRIC}, \
{"welch", WINDOW_WELCH}, \
{"kaiser", WINDOW_KAISER}, \
{"blackman", WINDOW_BLACKMAN}, \
{"sphinx", WINDOW_SPHINX}, \
static const struct m_opt_choice_alternatives scale_filters[] = {
FIXED_SCALE_KERNELS
NON_POLAR_FILTER_KERNELS
POLAR_FILTER_KERNELS
FILTER_WINDOWS
{0},
};
static const struct m_opt_choice_alternatives cdscale_filters[] = {
{"", SCALER_INHERIT},
FIXED_SCALE_KERNELS
NON_POLAR_FILTER_KERNELS
POLAR_FILTER_KERNELS
FILTER_WINDOWS
{0},
};
static const struct m_opt_choice_alternatives tscale_filters[] = {
{"oversample", SCALER_OVERSAMPLE},
{"linear", SCALER_LINEAR},
NON_POLAR_FILTER_KERNELS
FILTER_WINDOWS
{"jinc", WINDOW_JINC},
{0},
};
static const struct m_opt_choice_alternatives filter_windows[] = {
{"", WINDOW_PREFERRED},
FILTER_WINDOWS
{"jinc", WINDOW_JINC},
{0},
};
static const struct gl_video_opts gl_video_opts_def = {
.dither_algo = DITHER_FRUIT,
.dither_size = 6,
@ -304,10 +370,22 @@ static const struct gl_video_opts gl_video_opts_def = {
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
.scaler = {
{{"lanczos", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // scale
{{"hermite", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // dscale
{{NULL, .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // cscale
{{"oversample", .params={NAN, NAN}}, {.params = {NAN, NAN}}}, // tscale
[SCALER_SCALE] = {
{SCALER_LANCZOS, .params = {NAN, NAN}, .functions = scale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_DSCALE] = {
{SCALER_HERMITE, .params = {NAN, NAN}, .functions = cdscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_CSCALE] = {
{SCALER_INHERIT, .params = {NAN, NAN}, .functions = cdscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_TSCALE] = {
{SCALER_OVERSAMPLE, .params = {NAN, NAN}, .functions = tscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
},
.scaler_resizes_only = true,
.correct_downscaling = true,
@ -331,8 +409,6 @@ static const struct gl_video_opts gl_video_opts_def = {
.hwdec_interop = "auto",
};
static OPT_STRING_VALIDATE_FUNC(validate_scaler_opt);
static OPT_STRING_VALIDATE_FUNC(validate_window_opt);
static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
#define OPT_BASE_STRUCT struct gl_video_opts
@ -343,7 +419,6 @@ static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
.flags = M_OPT_DEFAULT_NAN
#define SCALER_OPTS(n, i) \
{n, OPT_STRING_VALIDATE(scaler[i].kernel.name, validate_scaler_opt)}, \
{n"-param1", OPT_FLOATDEF(scaler[i].kernel.params[0])}, \
{n"-param2", OPT_FLOATDEF(scaler[i].kernel.params[1])}, \
{n"-blur", OPT_FLOAT(scaler[i].kernel.blur)}, \
@ -353,7 +428,7 @@ static OPT_STRING_VALIDATE_FUNC(validate_error_diffusion_opt);
{n"-clamp", OPT_FLOAT(scaler[i].clamp), M_RANGE(0.0, 1.0)}, \
{n"-radius", OPT_FLOAT(scaler[i].radius), M_RANGE(0.5, 16.0)}, \
{n"-antiring", OPT_FLOAT(scaler[i].antiring), M_RANGE(0.0, 1.0)}, \
{n"-window", OPT_STRING_VALIDATE(scaler[i].window.name, validate_window_opt)}
{n"-window", OPT_CHOICE_C(scaler[i].window.function, filter_windows)}
const struct m_sub_options gl_video_conf = {
.opts = (const m_option_t[]) {
@ -416,9 +491,13 @@ const struct m_sub_options gl_video_conf = {
{"hdr-contrast-smoothness", OPT_FLOAT(tone_map.contrast_smoothness),
M_RANGE(1.0, 100.0)},
{"opengl-pbo", OPT_BOOL(pbo)},
{"scale", OPT_CHOICE_C(scaler[SCALER_SCALE].kernel.function, scale_filters)},
SCALER_OPTS("scale", SCALER_SCALE),
{"dscale", OPT_CHOICE_C(scaler[SCALER_DSCALE].kernel.function, cdscale_filters)},
SCALER_OPTS("dscale", SCALER_DSCALE),
{"cscale", OPT_CHOICE_C(scaler[SCALER_CSCALE].kernel.function, cdscale_filters)},
SCALER_OPTS("cscale", SCALER_CSCALE),
{"tscale", OPT_CHOICE_C(scaler[SCALER_TSCALE].kernel.function, tscale_filters)},
SCALER_OPTS("tscale", SCALER_TSCALE),
{"scaler-resizes-only", OPT_BOOL(scaler_resizes_only)},
{"correct-downscaling", OPT_BOOL(correct_downscaling)},
@ -481,7 +560,6 @@ static void uninit_rendering(struct gl_video *p);
static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t id);
static const char *handle_scaler_opt(const char *name, bool tscale);
static void reinit_from_options(struct gl_video *p);
static void get_scale_factors(struct gl_video *p, bool transpose_rot, double xy[2]);
static void gl_video_setup_hooks(struct gl_video *p);
@ -1663,10 +1741,7 @@ static bool double_seq(double a, double b)
static bool scaler_fun_eq(struct scaler_fun a, struct scaler_fun b)
{
if ((a.name && !b.name) || (b.name && !a.name))
return false;
return ((!a.name && !b.name) || strcmp(a.name, b.name) == 0) &&
return a.function == b.function &&
double_seq(a.params[0], b.params[0]) &&
double_seq(a.params[1], b.params[1]) &&
a.blur == b.blur &&
@ -1696,24 +1771,14 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
uninit_scaler(p, scaler);
if (scaler->index == SCALER_DSCALE && (!conf->kernel.name ||
!conf->kernel.name[0]))
{
if (conf->kernel.function == SCALER_INHERIT)
conf = &p->opts.scaler[SCALER_SCALE];
}
if (scaler->index == SCALER_CSCALE && (!conf->kernel.name ||
!conf->kernel.name[0]))
{
conf = &p->opts.scaler[SCALER_SCALE];
}
struct filter_kernel bare_window;
const struct filter_kernel *t_kernel = mp_find_filter_kernel(conf->kernel.name);
const struct filter_window *t_window = mp_find_filter_window(conf->window.name);
bool is_tscale = scaler->index == SCALER_TSCALE;
const struct filter_kernel *t_kernel = mp_find_filter_kernel(conf->kernel.function);
const struct filter_window *t_window = mp_find_filter_window(conf->window.function);
if (!t_kernel) {
const struct filter_window *window = mp_find_filter_window(conf->kernel.name);
const struct filter_window *window = mp_find_filter_window(conf->kernel.function);
if (window) {
bare_window = (struct filter_kernel) { .f = *window };
t_kernel = &bare_window;
@ -1721,8 +1786,6 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
}
scaler->conf = *conf;
scaler->conf.kernel.name = (char *)handle_scaler_opt(conf->kernel.name, is_tscale);
scaler->conf.window.name = t_window ? (char *)t_window->name : NULL;
scaler->scale_factor = scale_factor;
scaler->insufficient = false;
scaler->initialized = true;
@ -1819,7 +1882,9 @@ static void pass_sample_separated(struct gl_video *p, struct image src,
// Second pass (scale only in the x dir)
src = image_wrap(scaler->sep_fbo, src.type, src.components);
src.transform = t_x;
pass_describe(p, "%s second pass", scaler->conf.kernel.name);
pass_describe(p, "%s second pass",
m_opt_choice_str(scaler->conf.kernel.functions,
scaler->conf.kernel.function));
sampler_prelude(p->sc, pass_bind(p, src));
pass_sample_separated_gen(p->sc, scaler, 1, 0);
}
@ -1888,7 +1953,9 @@ static void pass_sample(struct gl_video *p, struct image img,
};
pass_describe(p, "%s=%s (%s)", scaler_opt[scaler->index],
scaler->conf.kernel.name, plane_names[img.type]);
m_opt_choice_str(scaler->conf.kernel.functions,
scaler->conf.kernel.function),
plane_names[img.type]);
bool is_separated = scaler->kernel && !scaler->kernel->polar;
@ -1898,12 +1965,11 @@ static void pass_sample(struct gl_video *p, struct image img,
sampler_prelude(p->sc, pass_bind(p, img));
// Dispatch the scaler. They're all wildly different.
const char *name = scaler->conf.kernel.name;
if (strcmp(name, "bilinear") == 0) {
if (scaler->conf.kernel.function == SCALER_BILINEAR) {
GLSL(color = texture(tex, pos);)
} else if (strcmp(name, "bicubic_fast") == 0) {
} else if (scaler->conf.kernel.function == SCALER_BICUBIC_FAST) {
pass_sample_bicubic_fast(p->sc);
} else if (strcmp(name, "oversample") == 0) {
} else if (scaler->conf.kernel.function == SCALER_OVERSAMPLE) {
pass_sample_oversample(p->sc, scaler, w, h);
} else if (scaler->kernel && scaler->kernel->polar) {
pass_dispatch_sample_polar(p, scaler, img, w, h);
@ -2290,16 +2356,13 @@ static void pass_read_video(struct gl_video *p)
const struct scaler_config *conf = &p->opts.scaler[scaler_id];
if (scaler_id == SCALER_CSCALE && (!conf->kernel.name ||
!conf->kernel.name[0]))
{
if (conf->kernel.function == SCALER_INHERIT)
conf = &p->opts.scaler[SCALER_SCALE];
}
struct scaler *scaler = &p->scaler[scaler_id];
// bilinear scaling is a free no-op thanks to GPU sampling
if (strcmp(conf->kernel.name, "bilinear") != 0) {
if (conf->kernel.function != SCALER_BILINEAR) {
GLSLF("// upscaling plane %d\n", n);
pass_sample(p, img[n], scaler, conf, 1.0, p->texture_w, p->texture_h);
finish_pass_tex(p, &p->scale_tex[n], p->texture_w, p->texture_h);
@ -2460,7 +2523,7 @@ static void pass_scale_main(struct gl_video *p)
struct scaler *scaler = &p->scaler[SCALER_SCALE];
struct scaler_config scaler_conf = p->opts.scaler[SCALER_SCALE];
if (p->opts.scaler_resizes_only && !downscaling && !upscaling) {
scaler_conf.kernel.name = "bilinear";
scaler_conf.kernel.function = SCALER_BILINEAR;
// For scaler-resizes-only, we round the texture offset to
// the nearest round value in order to prevent ugly blurriness
// (in exchange for slightly shifting the image by up to half a
@ -2468,7 +2531,8 @@ static void pass_scale_main(struct gl_video *p)
p->texture_offset.t[0] = roundf(p->texture_offset.t[0]);
p->texture_offset.t[1] = roundf(p->texture_offset.t[1]);
}
if (downscaling && p->opts.scaler[SCALER_DSCALE].kernel.name) {
if (downscaling &&
p->opts.scaler[SCALER_DSCALE].kernel.function != SCALER_INHERIT) {
scaler_conf = p->opts.scaler[SCALER_DSCALE];
scaler = &p->scaler[SCALER_DSCALE];
}
@ -3194,8 +3258,8 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t,
// surface_end.
struct scaler *tscale = &p->scaler[SCALER_TSCALE];
reinit_scaler(p, tscale, &p->opts.scaler[SCALER_TSCALE], 1, tscale_sizes);
bool oversample = strcmp(tscale->conf.kernel.name, "oversample") == 0;
bool linear = strcmp(tscale->conf.kernel.name, "linear") == 0;
bool oversample = tscale->conf.kernel.function == SCALER_OVERSAMPLE;
bool linear = tscale->conf.kernel.function == SCALER_LINEAR;
int size;
if (oversample || linear) {
@ -3753,7 +3817,7 @@ static bool test_fbo(struct gl_video *p, const struct ra_format *fmt)
}
// Return whether dumb-mode can be used without disabling any features.
// Essentially, vo_gpu with mostly default settings will return true.
// Essentially, vo_gpu with --profile=fast will return true.
static bool check_dumb_mode(struct gl_video *p)
{
struct gl_video_opts *o = &p->opts;
@ -3772,8 +3836,8 @@ static bool check_dumb_mode(struct gl_video *p)
// check remaining scalers (tscale is already implicitly excluded above)
for (int i = 0; i < SCALER_COUNT; i++) {
if (i != SCALER_TSCALE) {
const char *name = o->scaler[i].kernel.name;
if (name && strcmp(name, "bilinear") != 0)
if (o->scaler[i].kernel.function != SCALER_BILINEAR &&
o->scaler[i].kernel.function != SCALER_INHERIT)
return false;
}
}
@ -3872,17 +3936,25 @@ static void check_gl_features(struct gl_video *p)
"Most extended features will be disabled.\n");
}
p->dumb_mode = true;
static const struct scaler_config dumb_scaler_config = {
{"bilinear", .params = {NAN, NAN}},
{.params = {NAN, NAN}},
};
// Most things don't work, so whitelist all options that still work.
p->opts = (struct gl_video_opts){
.scaler = {
[SCALER_SCALE] = dumb_scaler_config,
[SCALER_DSCALE] = dumb_scaler_config,
[SCALER_CSCALE] = dumb_scaler_config,
[SCALER_TSCALE] = dumb_scaler_config,
[SCALER_SCALE] = {
{SCALER_BILINEAR, .params = {NAN, NAN}, .functions = scale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_DSCALE] = {
{SCALER_BILINEAR, .params = {NAN, NAN}, .functions = cdscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_CSCALE] = {
{SCALER_BILINEAR, .params = {NAN, NAN}, .functions = cdscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
[SCALER_TSCALE] = {
{SCALER_BILINEAR, .params = {NAN, NAN}, .functions = tscale_filters},
{WINDOW_PREFERRED, .params = {NAN, NAN}, .functions = filter_windows},
},
},
.gamma = p->opts.gamma,
.gamma_auto = p->opts.gamma_auto,
@ -3919,7 +3991,7 @@ static void check_gl_features(struct gl_video *p)
// I don't know if luminance alpha float textures exist, so disregard them.
for (int n = 0; n < SCALER_COUNT; n++) {
const struct filter_kernel *kernel =
mp_find_filter_kernel(p->opts.scaler[n].kernel.name);
mp_find_filter_kernel(p->opts.scaler[n].kernel.function);
if (kernel) {
char *reason = NULL;
if (!have_float_tex)
@ -3928,9 +4000,12 @@ static void check_gl_features(struct gl_video *p)
reason = "(GLSL version too old)";
if (reason) {
MP_WARN(p, "Disabling scaler #%d %s %s.\n", n,
p->opts.scaler[n].kernel.name, reason);
m_opt_choice_str(p->opts.scaler[n].kernel.functions,
p->opts.scaler[n].kernel.function),
reason);
// p->opts is a copy => we can just mess with it.
p->opts.scaler[n].kernel.name = "bilinear";
p->opts.scaler[n].kernel.function = SCALER_BILINEAR;
if (n == SCALER_TSCALE)
p->opts.interpolation = false;
}
@ -4097,29 +4172,6 @@ struct gl_video *gl_video_init(struct ra *ra, struct mp_log *log,
return p;
}
// Get static string for scaler shader. If "tscale" is set to true, the
// scaler must be a separable convolution filter.
static const char *handle_scaler_opt(const char *name, bool tscale)
{
if (name && name[0]) {
const struct filter_kernel *kernel = mp_find_filter_kernel(name);
if (kernel && (!tscale || !kernel->polar))
return kernel->f.name;
const struct filter_window *window = mp_find_filter_window(name);
if (window)
return window->name;
for (const char *const *filter = tscale ? fixed_tscale_filters
: fixed_scale_filters;
*filter; filter++) {
if (strcmp(*filter, name) == 0)
return *filter;
}
}
return NULL;
}
static void gl_video_update_options(struct gl_video *p)
{
if (m_config_cache_update(p->opts_cache)) {
@ -4160,10 +4212,9 @@ static void reinit_from_options(struct gl_video *p)
talloc_free(vo_opts);
if (p->opts.correct_downscaling && !p->correct_downscaling_warned) {
const char *name = p->opts.scaler[SCALER_DSCALE].kernel.name;
if (!name)
name = p->opts.scaler[SCALER_SCALE].kernel.name;
if (!name || !strcmp(name, "bilinear")) {
if (p->opts.scaler[SCALER_DSCALE].kernel.function == SCALER_BILINEAR ||
(p->opts.scaler[SCALER_DSCALE].kernel.function == SCALER_INHERIT &&
p->opts.scaler[SCALER_SCALE].kernel.function == SCALER_BILINEAR)) {
MP_WARN(p, "correct-downscaling requires non-bilinear scaler.\n");
p->correct_downscaling_warned = true;
}
@ -4180,7 +4231,7 @@ void gl_video_configure_queue(struct gl_video *p, struct vo *vo)
// the radius, the earlier we need to queue frames.
if (p->opts.interpolation) {
const struct filter_kernel *kernel =
mp_find_filter_kernel(p->opts.scaler[SCALER_TSCALE].kernel.name);
mp_find_filter_kernel(p->opts.scaler[SCALER_TSCALE].kernel.function);
if (kernel) {
// filter_scale wouldn't be correctly initialized were we to use it here.
// This is fine since we're always upsampling, but beware if downsampling
@ -4197,75 +4248,6 @@ void gl_video_configure_queue(struct gl_video *p, struct vo *vo)
vo_set_queue_params(vo, 0, queue_size);
}
static int validate_scaler_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
{
struct bstr param = bstr0(*value);
char s[32] = {0};
int r = 1;
bool tscale = bstr_equals0(name, "tscale");
if (bstr_equals0(param, "help")) {
r = M_OPT_EXIT;
} else if (bstr_equals0(name, "dscale") && !param.len) {
return r; // empty dscale means "use same as upscaler"
} else if (bstr_equals0(name, "cscale") && !param.len) {
return r; // empty cscale means "use same as upscaler"
} else {
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
if (!handle_scaler_opt(s, tscale))
r = M_OPT_INVALID;
}
if (r < 1) {
mp_info(log, "Available scalers:\n");
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].f.name; n++) {
if (!tscale || !mp_filter_kernels[n].polar)
mp_info(log, " %s\n", mp_filter_kernels[n].f.name);
}
for (int n = 0; mp_filter_windows[n].name; n++) {
for (int m = 0; mp_filter_kernels[m].f.name; m++) {
if (!strcmp(mp_filter_windows[n].name, mp_filter_kernels[m].f.name))
goto next_window; // don't log duplicates
}
mp_info(log, " %s\n", mp_filter_windows[n].name);
next_window: ;
}
if (s[0])
mp_fatal(log, "No scaler named '%s' found!\n", s);
}
return r;
}
static int validate_window_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
{
struct bstr param = bstr0(*value);
char s[32] = {0};
int r = 1;
if (bstr_equals0(param, "help")) {
r = M_OPT_EXIT;
} else if (!param.len) {
return r; // empty string means "use preferred window"
} else {
snprintf(s, sizeof(s), "%.*s", BSTR_P(param));
const struct filter_window *window = mp_find_filter_window(s);
if (!window)
r = M_OPT_INVALID;
}
if (r < 1) {
mp_info(log, "Available windows:\n");
for (int n = 0; mp_filter_windows[n].name; n++)
mp_info(log, " %s\n", mp_filter_windows[n].name);
if (s[0])
mp_fatal(log, "No window named '%s' found!\n", s);
}
return r;
}
static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *opt,
struct bstr name, const char **value)
{

View File

@ -29,10 +29,11 @@
#include "video/out/filter_kernels.h"
struct scaler_fun {
char *name;
int function;
float params[2];
float blur;
float taper;
const struct m_opt_choice_alternatives *functions;
};
struct scaler_config {

View File

@ -1907,13 +1907,13 @@ static const struct pl_filter_config *map_scaler(struct priv *p,
const struct gl_video_opts *opts = p->opts_cache->opts;
const struct scaler_config *cfg = &opts->scaler[unit];
if (unit == SCALER_DSCALE && (!cfg->kernel.name || !cfg->kernel.name[0]))
cfg = &opts->scaler[SCALER_SCALE];
if (unit == SCALER_CSCALE && (!cfg->kernel.name || !cfg->kernel.name[0]))
if (cfg->kernel.function == SCALER_INHERIT)
cfg = &opts->scaler[SCALER_SCALE];
const char *kernel_name = m_opt_choice_str(cfg->kernel.functions,
cfg->kernel.function);
for (int i = 0; fixed_presets[i].name; i++) {
if (strcmp(cfg->kernel.name, fixed_presets[i].name) == 0)
if (strcmp(kernel_name, fixed_presets[i].name) == 0)
return fixed_presets[i].filter;
}
@ -1921,9 +1921,9 @@ static const struct pl_filter_config *map_scaler(struct priv *p,
struct scaler_params *par = &p->scalers[unit];
const struct pl_filter_preset *preset;
const struct pl_filter_function_preset *fpreset;
if ((preset = pl_find_filter_preset(cfg->kernel.name))) {
if ((preset = pl_find_filter_preset(kernel_name))) {
par->config = *preset->filter;
} else if ((fpreset = pl_find_filter_function_preset(cfg->kernel.name))) {
} else if ((fpreset = pl_find_filter_function_preset(kernel_name))) {
par->config = (struct pl_filter_config) {
.kernel = fpreset->function,
.params[0] = fpreset->function->params[0],
@ -1931,12 +1931,13 @@ static const struct pl_filter_config *map_scaler(struct priv *p,
};
} else {
MP_ERR(p, "Failed mapping filter function '%s', no libplacebo analog?\n",
cfg->kernel.name);
kernel_name);
return &pl_filter_bilinear;
}
const struct pl_filter_function_preset *wpreset;
if ((wpreset = pl_find_filter_function_preset(cfg->window.name))) {
if ((wpreset = pl_find_filter_function_preset(
m_opt_choice_str(cfg->window.functions, cfg->window.function)))) {
par->config.window = wpreset->function;
par->config.wparams[0] = wpreset->function->params[0];
par->config.wparams[1] = wpreset->function->params[1];
@ -1959,7 +1960,7 @@ static const struct pl_filter_config *map_scaler(struct priv *p,
par->config.radius = cfg->radius;
} else {
MP_WARN(p, "Filter radius specified but filter '%s' is not "
"resizable, ignoring\n", cfg->kernel.name);
"resizable, ignoring\n", kernel_name);
}
}