summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2015-03-26 01:55:32 +0100
committerNiklas Haas <git@nand.wakku.to>2015-04-04 15:36:14 +0200
commit068ff812e4f958a83098e9ed27b2b96ffcab1eb2 (patch)
treeaf06b926adf3981b35055f378b18546681506dad
parent586dc5574f519a336fda0e8c1d3c94e0c1df38b2 (diff)
downloadmpv-068ff812e4f958a83098e9ed27b2b96ffcab1eb2.tar.bz2
mpv-068ff812e4f958a83098e9ed27b2b96ffcab1eb2.tar.xz
vo_opengl: refactor scaler configuration
This merges all of the scaler-related options into a single configuration struct, and also cleans up the way they're passed through the code. (For example, the scaler index is no longer threaded through pass_sample, just the scaler configuration itself, and there's no longer duplication of the params etc.) In addition, this commit makes scale-down more principled, and turns it into a scaler in its own right - so there's no longer an ugly separation between scale and scale-down in the code. Finally, the radius stuff has been made more proper - filters always have a radius now (there's no more radius -1), and get a new .resizable attribute instead for when it's tunable. User-visible changes: 1. scale-down has been renamed dscale and now has its own set of config options (dscale-param1, dscale-radius) etc., instead of reusing scale-param1 (which was arguably a bug). 2. The default radius is no longer fixed at 3, but instead uses that filter's preferred radius by default. (Scalers with a default radius other than 3 include sinc, gaussian, box and triangle) 3. scale-radius etc. now goes down to 0.5, rather than 1.0. 0.5 is the smallest radius that theoretically makes sense, and indeed it's used by at least one filter (nearest). Apart from that, it should just be internal changes only. Note that this sets up for the refactor discussed in #1720, which would be to merge scaler and window configurations (include parameters etc.) into a single, simplified string. In the code, this would now basically just mean getting rid of all the OPT_FLOATRANGE etc. lines related to scalers and replacing them by a single function that parses a string and updates the struct scaler_config as appropriate.
-rw-r--r--DOCS/man/vo.rst23
-rw-r--r--video/out/filter_kernels.c26
-rw-r--r--video/out/filter_kernels.h3
-rw-r--r--video/out/gl_video.c258
-rw-r--r--video/out/gl_video.h21
5 files changed, 190 insertions, 141 deletions
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index da59a2a7c4..2e07b55530 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -329,7 +329,7 @@ Available video output drivers are:
``mitchell``
Mitchell-Netravali. The ``B`` and ``C`` parameters can be set with
``scale-param1`` and ``scale-param2``. This filter is very good at
- downscaling (see ``scale-down``).
+ downscaling (see ``dscale``).
``oversample``
A version of nearest neighbour that (naively) oversamples pixels,
@@ -373,8 +373,8 @@ Available video output drivers are:
recommended to stick to values between 0.8 and 1.2.
``scale-radius=<value>``
- Set radius for filters listed below, must be a float number between 1.0
- and 16.0. Defaults to be 3.0 if not specified.
+ Set radius for filters listed below, must be a float number between 0.5
+ and 16.0. Defaults to the filter's preferred radius if not specified.
``sinc`` and derivatives, ``jinc`` and derivatives, ``gaussian``, ``box`` and ``triangle``
@@ -470,16 +470,14 @@ Available video output drivers are:
also lead to bad results, as can missing or incorrect display FPS
information (see ``--display-fps``).
+ ``dscale=<filter>``
+ Like ``scale``, but apply these filters on downscaling instead. If this
+ option is unset, the filter implied by ``scale`` will be applied.
+
``cscale=<filter>``
As ``scale``, but for interpolating chroma information. If the image
is not subsampled, this option is ignored entirely.
- ``scale-down=<filter>``
- Like ``scale``, but apply these filters on downscaling instead. If this
- option is unset, the filter implied by ``scale`` will be applied. Note
- that this is also affected by the other options related to ``scale``,
- ie. there is no ``scale-down-param1`` or similar.
-
``tscale=<filter>``
The filter used for interpolating the temporal axis (frames). This is
only used if ``interpolation`` is enabled. The only valid choices
@@ -491,8 +489,9 @@ Available video output drivers are:
framestepping, proportional to the radius used. It is recommended to
stick to a radius of 1 or 2.
- ``cscale-radius``, ``tscale-radius``, ``cscale-blur``, ``tscale-blur``, etc.
- Set filter parameters for ``cscale`` and ``tscale``, respectively.
+ ``dscale-radius``, ``cscale-radius``, ``tscale-radius``, etc.
+ Set filter parameters for ``dscale``, ``cscale`` and ``tscale``,
+ respectively.
See the corresponding options for ``scale``.
@@ -706,7 +705,7 @@ Available video output drivers are:
This is equivalent to::
- --vo=opengl:scale=spline36:cscale=spline36:scale-down=mitchell:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
+ --vo=opengl:scale=spline36:cscale=spline36:dscale=mitchell:dither-depth=auto:fbo-format=rgba16:fancy-downscaling:sigmoid-upscaling
Note that some cheaper LCDs do dithering that gravely interferes with
``opengl``'s dithering. Disabling dithering with ``dither-depth=no`` helps.
diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c
index 4f82e80328..12ecd9edf4 100644
--- a/video/out/filter_kernels.c
+++ b/video/out/filter_kernels.c
@@ -45,7 +45,6 @@ const struct filter_window *mp_find_filter_window(const char *name)
{
if (!name)
return NULL;
-
for (const struct filter_window *w = mp_filter_windows; w->name; w++) {
if (strcmp(w->name, name) == 0)
return w;
@@ -55,6 +54,8 @@ const struct filter_window *mp_find_filter_window(const char *name)
const struct filter_kernel *mp_find_filter_kernel(const char *name)
{
+ if (!name)
+ return NULL;
for (const struct filter_kernel *k = mp_filter_kernels; k->f.name; k++) {
if (strcmp(k->f.name, name) == 0)
return k;
@@ -313,7 +314,7 @@ const struct filter_window mp_filter_windows[] = {
{"quadric", 1.5, quadric},
{"kaiser", 1, kaiser, .params = {6.33, NAN} },
{"hermite", 1, cubic_bc, .params = {0.0, 0.0} },
- {"gaussian", -1, gaussian, .params = {1.0, NAN} },
+ {"gaussian", 2, gaussian, .params = {1.0, NAN} },
{"sinc", 1, sinc},
{"jinc", 1.2196698912665045, jinc},
{"sphinx", 1.4302966531242027, sphinx},
@@ -326,14 +327,14 @@ const struct filter_kernel mp_filter_kernels[] = {
{{"spline36", 3, spline36}},
{{"spline64", 4, spline64}},
// Sinc filters
- {{"sinc", -1, sinc}},
- {{"lanczos", -1, sinc}, .window = "sinc"},
- {{"ginseng", -1, sinc}, .window = "jinc"},
+ {{"sinc", 2, sinc, .resizable = true}},
+ {{"lanczos", 3, sinc, .resizable = true}, .window = "sinc"},
+ {{"ginseng", 3, sinc, .resizable = true}, .window = "jinc"},
// Jinc filters
- {{"jinc", -1, jinc}, .polar = true},
- {{"ewa_lanczos", -1, jinc}, .polar = true, .window = "jinc"},
- {{"ewa_hanning", -1, jinc}, .polar = true, .window = "hanning" },
- {{"ewa_ginseng", -1, jinc}, .polar = true, .window = "sinc"},
+ {{"jinc", 3, jinc, .resizable = true}, .polar = true},
+ {{"ewa_lanczos", 3, jinc, .resizable = true}, .polar = true, .window = "jinc"},
+ {{"ewa_hanning", 3, jinc, .resizable = true}, .polar = true, .window = "hanning" },
+ {{"ewa_ginseng", 3, jinc, .resizable = true}, .polar = true, .window = "sinc"},
// Radius is based on the true jinc radius, slightly sharpened as per
// calculations by Nicolas Robidoux. Source: Imagemagick's magick/resize.c
{{"ewa_lanczossharp", 3.2383154841662362, jinc, .blur = 0.9812505644269356},
@@ -350,10 +351,9 @@ const struct filter_kernel mp_filter_kernels[] = {
{{"robidoux", 2, cubic_bc, .params = {0.3782, 0.3109}}, .polar = true},
{{"robidouxsharp", 2, cubic_bc, .params = {0.2620, 0.3690}}, .polar = true},
// Miscalleaneous filters
- {{"box", -1, box}},
+ {{"box", 1, box, .resizable = true}},
{{"nearest", 0.5, box}},
- {{"triangle", -1, triangle}},
- {{"bilinear_slow", 1, triangle}},
- {{"gaussian", -1, gaussian, .params = {1.0, NAN} }},
+ {{"triangle", 1, triangle, .resizable = true}},
+ {{"gaussian", 2, gaussian, .params = {1.0, NAN}, .resizable = true}},
{{0}}
};
diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h
index 99776d2f07..8d97cf6786 100644
--- a/video/out/filter_kernels.h
+++ b/video/out/filter_kernels.h
@@ -23,8 +23,9 @@
struct filter_window {
const char *name;
- double radius; // A negative value will use user specified radius instead.
+ 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
double params[2]; // User-defined custom filter parameters. Not used by
// all filters
double blur; // Blur coefficient (sharpens or widens the filter)
diff --git a/video/out/gl_video.c b/video/out/gl_video.c
index 7f426bfd1f..a087adeee0 100644
--- a/video/out/gl_video.c
+++ b/video/out/gl_video.c
@@ -48,7 +48,7 @@
// Other texture units are reserved for specific purposes
#define TEXUNIT_SCALERS TEXUNIT_VIDEO_NUM
-#define TEXUNIT_3DLUT (TEXUNIT_SCALERS+3)
+#define TEXUNIT_3DLUT (TEXUNIT_SCALERS+4)
#define TEXUNIT_DITHER (TEXUNIT_3DLUT+1)
// scale/cscale arguments that map directly to shader filter routines.
@@ -112,11 +112,8 @@ struct video_image {
struct scaler {
int index;
- const char *name;
+ struct scaler_config conf;
double scale_factor;
- float params[2];
- float antiring;
-
bool initialized;
struct filter_kernel *kernel;
GLuint gl_lut;
@@ -190,8 +187,8 @@ struct gl_video {
int surface_now;
bool is_interpolated;
- // state for luma (0), chroma (1) and temporal (2) scalers
- struct scaler scalers[3];
+ // state for luma (0), luma-down(1), chroma (2) and temporal (3) scalers
+ struct scaler scaler[4];
struct mp_csp_equalizer video_eq;
@@ -326,10 +323,12 @@ const struct gl_video_opts gl_video_opts_def = {
.fbo_format = GL_RGBA,
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
- .scalers = { "bilinear", "bilinear", "oversample" },
- .dscaler = "bilinear",
- .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3, 3},
+ .scaler = {
+ {{"bilinear", .params={NAN, NAN}}}, // scale
+ {{NULL, .params={NAN, NAN}}}, // dscale (defaults to scale)
+ {{"bilinear", .params={NAN, NAN}}}, // cscale
+ {{"oversample", .params={NAN, NAN}}} // tscale
+ },
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -344,10 +343,12 @@ const struct gl_video_opts gl_video_opts_hq_def = {
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
.sigmoid_upscaling = 1,
- .scalers = { "spline36", "spline36", "oversample" },
- .dscaler = "mitchell",
- .scaler_params = {{NAN, NAN}, {NAN, NAN}, {NAN, NAN}},
- .scaler_radius = {3, 3, 3},
+ .scaler = {
+ {{"spline36", .params={NAN, NAN}}}, // scale
+ {{"mitchell", .params={NAN, NAN}}}, // dscale
+ {{"spline36", .params={NAN, NAN}}}, // cscale
+ {{"oversample", .params={NAN, NAN}}} // tscale
+ },
.alpha_mode = 2,
.background = {0, 0, 0, 255},
.gamma = 1.0f,
@@ -380,28 +381,34 @@ const struct m_sub_options gl_video_conf = {
{"gamma22", MP_CSP_TRC_GAMMA22})),
OPT_FLAG("npot", npot, 0),
OPT_FLAG("pbo", pbo, 0),
- OPT_STRING_VALIDATE("scale", scalers[0], 0, validate_scaler_opt),
- OPT_STRING_VALIDATE("cscale", scalers[1], 0, validate_scaler_opt),
- OPT_STRING_VALIDATE("tscale", scalers[2], 0, validate_scaler_opt),
- OPT_STRING_VALIDATE("scale-down", dscaler, 0, validate_scaler_opt),
- OPT_FLOAT("scale-param1", scaler_params[0][0], 0),
- OPT_FLOAT("scale-param2", scaler_params[0][1], 0),
- OPT_FLOAT("cscale-param1", scaler_params[1][0], 0),
- OPT_FLOAT("cscale-param2", scaler_params[1][1], 0),
- OPT_FLOAT("tscale-param1", scaler_params[2][0], 0),
- OPT_FLOAT("tscale-param2", scaler_params[2][1], 0),
- OPT_FLOAT("scale-blur", scaler_blur[0], 0),
- OPT_FLOAT("cscale-blur", scaler_blur[1], 0),
- OPT_FLOAT("tscale-blur", scaler_blur[2], 0),
- OPT_STRING_VALIDATE("scale-window", scaler_window[0], 0, validate_window_opt),
- OPT_STRING_VALIDATE("cscale-window", scaler_window[1], 0, validate_window_opt),
- OPT_STRING_VALIDATE("tscale-window", scaler_window[2], 0, validate_window_opt),
- OPT_FLOATRANGE("scale-radius", scaler_radius[0], 0, 1.0, 16.0),
- OPT_FLOATRANGE("cscale-radius", scaler_radius[1], 0, 1.0, 16.0),
- OPT_FLOATRANGE("tscale-radius", scaler_radius[2], 0, 1.0, 3.0),
- OPT_FLOATRANGE("scale-antiring", scaler_antiring[0], 0, 0.0, 1.0),
- OPT_FLOATRANGE("cscale-antiring", scaler_antiring[1], 0, 0.0, 1.0),
- OPT_FLOATRANGE("tscale-antiring", scaler_antiring[2], 0, 0.0, 1.0),
+ OPT_STRING_VALIDATE("scale", scaler[0].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("dscale", scaler[1].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("cscale", scaler[2].kernel.name, 0, validate_scaler_opt),
+ OPT_STRING_VALIDATE("tscale", scaler[3].kernel.name, 0, validate_scaler_opt),
+ OPT_FLOAT("scale-param1", scaler[0].kernel.params[0], 0),
+ OPT_FLOAT("scale-param2", scaler[0].kernel.params[1], 0),
+ OPT_FLOAT("dscale-param1", scaler[1].kernel.params[0], 0),
+ OPT_FLOAT("dscale-param2", scaler[1].kernel.params[1], 0),
+ OPT_FLOAT("cscale-param1", scaler[2].kernel.params[0], 0),
+ OPT_FLOAT("cscale-param2", scaler[2].kernel.params[1], 0),
+ OPT_FLOAT("tscale-param1", scaler[3].kernel.params[0], 0),
+ OPT_FLOAT("tscale-param2", scaler[3].kernel.params[1], 0),
+ OPT_FLOAT("scale-blur", scaler[0].kernel.blur, 0),
+ OPT_FLOAT("dscale-blur", scaler[1].kernel.blur, 0),
+ OPT_FLOAT("cscale-blur", scaler[2].kernel.blur, 0),
+ OPT_FLOAT("tscale-blur", scaler[3].kernel.blur, 0),
+ OPT_STRING_VALIDATE("scale-window", scaler[0].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("dscale-window", scaler[1].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("cscale-window", scaler[2].window.name, 0, validate_window_opt),
+ OPT_STRING_VALIDATE("tscale-window", scaler[3].window.name, 0, validate_window_opt),
+ OPT_FLOATRANGE("scale-radius", scaler[0].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("dscale-radius", scaler[1].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("cscale-radius", scaler[2].radius, 0, 0.5, 16.0),
+ OPT_FLOATRANGE("tscale-radius", scaler[3].radius, 0, 0.5, 3.0),
+ OPT_FLOATRANGE("scale-antiring", scaler[0].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("dscale-antiring", scaler[1].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("cscale-antiring", scaler[2].antiring, 0, 0.0, 1.0),
+ OPT_FLOATRANGE("tscale-antiring", scaler[3].antiring, 0, 0.0, 1.0),
OPT_FLAG("scaler-resizes-only", scaler_resizes_only, 0),
OPT_FLAG("linear-scaling", linear_scaling, 0),
OPT_FLAG("fancy-downscaling", fancy_downscaling, 0),
@@ -454,6 +461,7 @@ const struct m_sub_options gl_video_conf = {
OPT_REPLACED("cantiring", "cscale-antiring"),
OPT_REPLACED("smoothmotion", "interpolation"),
OPT_REPLACED("smoothmotion-threshold", "tscale-param1"),
+ OPT_REPLACED("scale-down", "dscale"),
{0}
},
@@ -462,7 +470,7 @@ const struct m_sub_options gl_video_conf = {
};
static void uninit_rendering(struct gl_video *p);
-static void uninit_scaler(struct gl_video *p, int scaler_unit);
+static void uninit_scaler(struct gl_video *p, struct scaler *scaler);
static void check_gl_features(struct gl_video *p);
static bool init_format(int fmt, struct gl_video *init);
@@ -541,8 +549,8 @@ static void uninit_rendering(struct gl_video *p)
{
GL *gl = p->gl;
- for (int n = 0; n < 3; n++)
- uninit_scaler(p, n);
+ for (int n = 0; n < 4; n++)
+ uninit_scaler(p, &p->scaler[n]);
gl->DeleteTextures(1, &p->dither_texture);
p->dither_texture = 0;
@@ -870,11 +878,9 @@ static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo,
pass_load_fbotex(p, dst_fbo, tex, w, h);
}
-static void uninit_scaler(struct gl_video *p, int scaler_unit)
+static void uninit_scaler(struct gl_video *p, struct scaler *scaler)
{
GL *gl = p->gl;
- struct scaler *scaler = &p->scalers[scaler_unit];
-
fbotex_uninit(&scaler->sep_fbo);
gl->DeleteTextures(1, &scaler->gl_lut);
scaler->gl_lut = 0;
@@ -882,53 +888,79 @@ static void uninit_scaler(struct gl_video *p, int scaler_unit)
scaler->initialized = false;
}
-static void reinit_scaler(struct gl_video *p, int scaler_unit, const char *name,
- double scale_factor, int sizes[])
+// Semantic equality
+static bool double_seq(double a, double b)
+{
+ return (isnan(a) && isnan(b)) || a == 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) &&
+ double_seq(a.params[0], b.params[0]) &&
+ double_seq(a.params[1], b.params[1]) &&
+ a.blur == b.blur;
+}
+
+static bool scaler_conf_eq(struct scaler_config a, struct scaler_config b)
+{
+ // Note: antiring isn't compared because it doesn't affect LUT
+ // generation
+ return scaler_fun_eq(a.kernel, b.kernel) &&
+ scaler_fun_eq(a.window, b.window) &&
+ a.radius == b.radius;
+}
+
+static void reinit_scaler(struct gl_video *p, struct scaler *scaler,
+ const struct scaler_config *conf,
+ double scale_factor,
+ int sizes[])
{
GL *gl = p->gl;
- struct scaler *scaler = &p->scalers[scaler_unit];
- if (scaler->name && strcmp(scaler->name, name) == 0 &&
+ if (scaler_conf_eq(scaler->conf, *conf) &&
scaler->scale_factor == scale_factor &&
scaler->initialized)
return;
- uninit_scaler(p, scaler_unit);
+ uninit_scaler(p, scaler);
- scaler->name = name;
+ scaler->conf = *conf;
scaler->scale_factor = scale_factor;
scaler->insufficient = false;
scaler->initialized = true;
- for (int n = 0; n < 2; n++)
- scaler->params[n] = p->opts.scaler_params[scaler->index][n];
- scaler->antiring = p->opts.scaler_antiring[scaler->index];
-
- const struct filter_kernel *t_kernel = mp_find_filter_kernel(scaler->name);
+ const struct filter_kernel *t_kernel = mp_find_filter_kernel(conf->kernel.name);
if (!t_kernel)
return;
scaler->kernel_storage = *t_kernel;
scaler->kernel = &scaler->kernel_storage;
- const char *win = p->opts.scaler_window[scaler->index];
+ const char *win = conf->window.name;
if (!win || !win[0])
- win = t_kernel->window;
+ win = t_kernel->window; // fall back to the scaler's default window
const struct filter_window *t_window = mp_find_filter_window(win);
if (t_window)
scaler->kernel->w = *t_window;
for (int n = 0; n < 2; n++) {
- if (!isnan(scaler->params[n]))
- scaler->kernel->f.params[n] = scaler->params[n];
+ if (!isnan(conf->kernel.params[n]))
+ scaler->kernel->f.params[n] = conf->kernel.params[n];
+ if (!isnan(conf->window.params[n]))
+ scaler->kernel->w.params[n] = conf->window.params[n];
}
- float blur = p->opts.scaler_blur[scaler->index];
- if (blur > 0.0)
- scaler->kernel->f.blur = blur;
+ if (conf->kernel.blur > 0.0)
+ scaler->kernel->f.blur = conf->kernel.blur;
+ if (conf->window.blur > 0.0)
+ scaler->kernel->w.blur = conf->window.blur;
- if (scaler->kernel->f.radius < 0)
- scaler->kernel->f.radius = p->opts.scaler_radius[scaler->index];
+ if (scaler->kernel->f.resizable && conf->radius > 0.0)
+ scaler->kernel->f.radius = conf->radius;
scaler->insufficient = !mp_init_filter(scaler->kernel, sizes, scale_factor);
@@ -1026,7 +1058,7 @@ static void pass_sample_separated_gen(struct gl_video *p, struct scaler *scaler,
int d_x, int d_y)
{
int N = scaler->kernel->size;
- bool use_ar = scaler->antiring > 0;
+ bool use_ar = scaler->conf.antiring > 0;
bool planar = d_x == 0 && d_y == 0;
GLSL(vec4 color = vec4(0.0);)
GLSLF("{\n");
@@ -1056,7 +1088,8 @@ static void pass_sample_separated_gen(struct gl_video *p, struct scaler *scaler,
}
}
if (use_ar)
- GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n", scaler->antiring);
+ GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n",
+ scaler->conf.antiring);
GLSLF("}\n");
}
@@ -1085,7 +1118,7 @@ static void pass_sample_polar(struct gl_video *p, struct scaler *scaler)
{
double radius = scaler->kernel->f.radius;
int bound = (int)ceil(radius);
- bool use_ar = scaler->antiring > 0;
+ bool use_ar = scaler->conf.antiring > 0;
GLSL(vec4 color = vec4(0.0);)
GLSLF("{\n");
GLSL(vec2 fcoord = fract(pos * size - vec2(0.5));)
@@ -1127,7 +1160,8 @@ static void pass_sample_polar(struct gl_video *p, struct scaler *scaler)
}
GLSL(color = color / vec4(wsum);)
if (use_ar)
- GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n", scaler->antiring);
+ GLSLF("color = mix(color, clamp(color, lo, hi), %f);\n",
+ scaler->conf.antiring);
GLSLF("}\n");
}
@@ -1181,7 +1215,8 @@ static void pass_sample_sharpen3(struct gl_video *p, struct scaler *scaler)
+ texture(tex, pos + st * vec2(+1, -1))
+ texture(tex, pos + st * vec2(-1, +1))
+ texture(tex, pos + st * vec2(-1, -1));)
- double param = isnan(scaler->params[0]) ? 0.5 : scaler->params[0];
+ float param = scaler->conf.kernel.params[0];
+ param = isnan(param) ? 0.5 : param;
GLSLF("color = p + (p - 0.25 * sum) * %f;\n", param);
GLSLF("}\n");
}
@@ -1202,7 +1237,8 @@ static void pass_sample_sharpen5(struct gl_video *p, struct scaler *scaler)
+ texture(tex, pos + st2 * vec2(-1, 0))
+ texture(tex, pos + st2 * vec2( 0, -1));)
GLSL(vec4 t = p * 0.859375 + sum2 * -0.1171875 + sum1 * -0.09765625;)
- double param = isnan(scaler->params[0]) ? 0.5 : scaler->params[0];
+ float param = scaler->conf.kernel.params[0];
+ param = isnan(param) ? 0.5 : param;
GLSLF("color = p + t * %f;\n", param);
GLSLF("}\n");
}
@@ -1224,11 +1260,12 @@ static void pass_sample_oversample(struct gl_video *p, struct scaler *scaler,
gl_sc_uniform_vec2(p->sc, "output_size", (float[2]){w, h});
GLSL(vec2 coeff = vec2((baseSE - pos) * output_size);)
GLSL(coeff = clamp(coeff, 0.0, 1.0);)
- if (scaler->params[0] > 0) { // also rules out NAN
+ float threshold = scaler->conf.kernel.params[0];
+ if (threshold > 0) { // also rules out NAN
GLSLF("coeff = mix(coeff, vec2(0.0), "
- "lessThanEqual(coeff, vec2(%f)));\n", scaler->params[0]);
+ "lessThanEqual(coeff, vec2(%f)));\n", threshold);
GLSLF("coeff = mix(coeff, vec2(1.0), "
- "greaterThanEqual(coeff, vec2(%f)));\n", scaler->params[0]);
+ "greaterThanEqual(coeff, vec2(%f)));\n", threshold);
}
// Compute the right blend of colors
GLSL(vec4 left = mix(texture(tex, baseSW),
@@ -1250,12 +1287,11 @@ static void pass_sample_oversample(struct gl_video *p, struct scaler *scaler,
// This will declare "vec4 color;", which contains the scaled contents.
// The scaler unit is initialized by this function; in order to avoid cache
// thrashing, the scaler unit should usually use the same parameters.
-static void pass_sample(struct gl_video *p, int src_tex,
- int scaler_unit, const char *name, double scale_factor,
+static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler,
+ const struct scaler_config *conf, double scale_factor,
int w, int h, struct gl_transform transform)
{
- struct scaler *scaler = &p->scalers[scaler_unit];
- reinit_scaler(p, scaler_unit, name, scale_factor, filter_sizes);
+ reinit_scaler(p, scaler, conf, scale_factor, filter_sizes);
sampler_prelude(p, src_tex);
// Set up the transformation for everything other than separated scaling
@@ -1263,15 +1299,16 @@ static void pass_sample(struct gl_video *p, int src_tex,
gl_transform_rect(transform, &p->pass_tex[src_tex].src);
// Dispatch the scaler. They're all wildly different.
- if (strcmp(scaler->name, "bilinear") == 0) {
+ const char *name = scaler->conf.kernel.name;
+ if (strcmp(name, "bilinear") == 0) {
GLSL(vec4 color = texture(tex, pos);)
- } else if (strcmp(scaler->name, "bicubic_fast") == 0) {
+ } else if (strcmp(name, "bicubic_fast") == 0) {
pass_sample_bicubic_fast(p);
- } else if (strcmp(scaler->name, "sharpen3") == 0) {
+ } else if (strcmp(name, "sharpen3") == 0) {
pass_sample_sharpen3(p, scaler);
- } else if (strcmp(scaler->name, "sharpen5") == 0) {
+ } else if (strcmp(name, "sharpen5") == 0) {
pass_sample_sharpen5(p, scaler);
- } else if (strcmp(scaler->name, "oversample") == 0) {
+ } else if (strcmp(name, "oversample") == 0) {
pass_sample_oversample(p, scaler, w, h);
} else if (scaler->kernel && scaler->kernel->polar) {
pass_sample_polar(p, scaler);
@@ -1298,9 +1335,9 @@ static void pass_read_video(struct gl_video *p)
return;
}
- const char *cscale = p->opts.scalers[1];
+ const struct scaler_config *cscale = &p->opts.scaler[2];
if (p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED &&
- strcmp(cscale, "bilinear") != 0) {
+ strcmp(cscale->kernel.name, "bilinear") != 0) {
struct src_tex luma = p->pass_tex[0];
if (p->plane_count > 2) {
// For simplicity and performance, we merge the chroma planes
@@ -1317,7 +1354,8 @@ static void pass_read_video(struct gl_video *p)
finish_pass_fbo(p, &p->chroma_merge_fbo, c_w, c_h, 1, 0);
}
GLSLF("// chroma scaling\n");
- pass_sample(p, 1, 1, cscale, 1.0, p->image_w, p->image_h, chromafix);
+ pass_sample(p, 1, &p->scaler[2], cscale, 1.0, p->image_w, p->image_h,
+ chromafix);
GLSL(vec2 chroma = color.rg;)
// Always force rendering to a FBO before main scaling, or we would
// scale chroma incorrectly.
@@ -1463,11 +1501,14 @@ static void pass_scale_main(struct gl_video *p)
bool upscaling = !downscaling && (xy[0] > 1.0 || xy[1] > 1.0);
double scale_factor = 1.0;
- char *scaler = p->opts.scalers[0];
+ struct scaler *scaler = &p->scaler[0];
+ struct scaler_config scaler_conf = p->opts.scaler[0];
if (p->opts.scaler_resizes_only && !downscaling && !upscaling)
- scaler = "bilinear";
- if (downscaling)
- scaler = p->opts.dscaler;
+ scaler_conf.kernel.name = "bilinear";
+ if (downscaling && p->opts.scaler[1].kernel.name) {
+ scaler_conf = p->opts.scaler[1];
+ scaler = &p->scaler[1];
+ }
double f = MPMIN(xy[0], xy[1]);
if (p->opts.fancy_downscaling && f < 1.0 &&
@@ -1519,7 +1560,7 @@ static void pass_scale_main(struct gl_video *p)
}
GLSLF("// main scaling\n");
- if (!p->use_indirect && strcmp(scaler, "bilinear") == 0) {
+ if (!p->use_indirect && strcmp(scaler_conf.kernel.name, "bilinear") == 0) {
// implicitly scale in pass_video_to_screen, but set up the textures
// manually (for cropping etc.). Special care has to be taken for the
// chroma planes (everything except luma=tex0), to make sure the offset
@@ -1533,7 +1574,8 @@ static void pass_scale_main(struct gl_video *p)
gl_transform_rect(n > 0 ? tchroma : transform, &p->pass_tex[n].src);
} else {
finish_pass_fbo(p, &p->indirect_fbo, p->image_w, p->image_h, 0, 0);
- pass_sample(p, 0, 0, scaler, scale_factor, vp_w, vp_h, transform);
+ pass_sample(p, 0, scaler, &scaler_conf, scale_factor, vp_w, vp_h,
+ transform);
}
GLSLF("// scaler post-conversion\n");
@@ -1834,9 +1876,9 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
// look like this: _ A [B] C D _
// A is surface_bse, B is surface_now, C is surface_nxt and D is
// surface_end.
- struct scaler *tscale = &p->scalers[2];
- reinit_scaler(p, 2, p->opts.scalers[2], 1, tscale_sizes);
- bool oversample = strcmp(tscale->name, "oversample") == 0;
+ struct scaler *tscale = &p->scaler[3];
+ reinit_scaler(p, tscale, &p->opts.scaler[3], 1, tscale_sizes);
+ bool oversample = strcmp(tscale->conf.kernel.name, "oversample") == 0;
int size;
if (oversample) {
size = 2;
@@ -1910,7 +1952,8 @@ static void gl_video_interpolate_frame(struct gl_video *p, int fbo,
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];
+ threshold = tscale->conf.kernel.params[0];
+ threshold = isnan(threshold) ? 0.0 : threshold;
mix = (pts_nxt - t->next_vsync) / vsync_interval;
mix = mix <= 0 + threshold ? 0 : mix;
mix = mix >= 1 - threshold ? 1 : mix;
@@ -2133,8 +2176,9 @@ static void check_gl_features(struct gl_video *p)
// because they will be slow (not critically slow, but still slower).
// Without FP textures, we must always disable them.
// I don't know if luminance alpha float textures exist, so disregard them.
- for (int n = 0; n < 2; n++) {
- const struct filter_kernel *kernel = mp_find_filter_kernel(p->opts.scalers[n]);
+ for (int n = 0; n < 4; n++) {
+ const struct filter_kernel *kernel =
+ mp_find_filter_kernel(p->opts.scaler[n].kernel.name);
if (kernel) {
char *reason = NULL;
if (!test_fbo(p, &have_fbo))
@@ -2144,7 +2188,7 @@ static void check_gl_features(struct gl_video *p)
if (!have_1d_tex && kernel->polar)
reason = "scaler (1D tex.)";
if (reason) {
- p->opts.scalers[n] = "bilinear";
+ p->opts.scaler[n].kernel.name = "bilinear";
disabled[n_disabled++] = reason;
}
}
@@ -2490,11 +2534,7 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log)
.opts = gl_video_opts_def,
.gl_target = GL_TEXTURE_2D,
.texture_16bit_depth = 16,
- .scalers = {
- { .index = 0, .name = "bilinear" },
- { .index = 1, .name = "bilinear" },
- { .index = 2, .name = "oversample" },
- },
+ .scaler = {{.index = 0}, {.index = 1}, {.index = 2}, {.index = 3}},
.sc = gl_sc_create(gl, log),
};
gl_video_set_debug(p, true);
@@ -2528,19 +2568,21 @@ void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,
int *queue_size)
{
p->opts = *opts;
- for (int n = 0; n < 3; n++)
- p->opts.scalers[n] = (char *)handle_scaler_opt(p->opts.scalers[n], n==2);
- p->opts.dscaler = (char *)handle_scaler_opt(p->opts.dscaler, false);
+ for (int n = 0; n < 4; n++) {
+ p->opts.scaler[n].kernel.name =
+ (char *)handle_scaler_opt(p->opts.scaler[n].kernel.name, n==3);
+ }
// Figure out an adequate size for the interpolation queue. The larger
// the radius, the earlier we need to queue frames. This rough heuristic
// seems to work for now, but ideally we want to rework the pause/unpause
// logic to make larger queue sizes the default.
- if (queue_size && p->opts.interpolation && p->opts.scalers[2]) {
- const struct filter_kernel *kernel = mp_find_filter_kernel(p->opts.scalers[2]);
+ if (queue_size && p->opts.interpolation) {
+ const struct filter_kernel *kernel =
+ mp_find_filter_kernel(p->opts.scaler[3].kernel.name);
if (kernel) {
double radius = kernel->f.radius;
- radius = radius > 0 ? radius : p->opts.scaler_radius[2];
+ radius = radius > 0 ? radius : p->opts.scaler[3].radius;
*queue_size = 50e3 * ceil(radius);
}
}
diff --git a/video/out/gl_video.h b/video/out/gl_video.h
index f3267d69c4..d28473d58f 100644
--- a/video/out/gl_video.h
+++ b/video/out/gl_video.h
@@ -28,18 +28,25 @@ struct lut3d {
int size[3];
};
+struct scaler_fun {
+ char *name;
+ float params[2];
+ float blur;
+};
+
+struct scaler_config {
+ struct scaler_fun kernel;
+ struct scaler_fun window;
+ float radius;
+ float antiring;
+};
+
struct gl_video_opts {
- char *scalers[3];
- char *dscaler;
+ struct scaler_config scaler[4];
float gamma;
int gamma_auto;
int target_prim;
int target_trc;
- float scaler_params[3][2];
- float scaler_blur[3];
- float scaler_radius[3];
- float scaler_antiring[3];
- char *scaler_window[3];
int linear_scaling;
int fancy_downscaling;
int sigmoid_upscaling;