[vlc-devel] [PATCH 5/5] opengl: WIP introduce libplacebo sampling filters

Niklas Haas vlc at haasn.xyz
Thu Nov 12 13:37:45 CET 2020


I want to emphasize just how much of this code is redundant with
libplacebo's `pl_opengl`, which already handles all of the messy
wrapping between libplacebo structs/descriptors/etc. and OpenGL state.

So hopefully most of this code will end up getting deleted soon(tm).
That being said, some notes:

On Fri, 28 Aug 2020 18:49:39 +0200, "Louis Régnier" <louis.videolabs at gmail.com> wrote:
> This patch allows to use libplacebo's polar sampling filters
> (ewa_jinc, ewa_lanczos, ewa_ginseng, ewa_hann, HaasnSoft (blurred EWA Hann))
> in order to perform scaling.
> 
> It introduce a new opengl sampler who handle scaling.
> ---
>  modules/video_output/opengl/sampler.c     | 466 ++++++++++++++++++++++
>  modules/video_output/opengl/vout_helper.h |   3 +
>  modules/video_output/placebo_utils.h      |  23 ++
>  3 files changed, 492 insertions(+)
> 
> diff --git a/modules/video_output/opengl/sampler.c b/modules/video_output/opengl/sampler.c
> index ace1d723e1..997be3030d 100644
> --- a/modules/video_output/opengl/sampler.c
> +++ b/modules/video_output/opengl/sampler.c
> @@ -29,8 +29,10 @@
>  #include <vlc_opengl.h>
>  
>  #ifdef HAVE_LIBPLACEBO
> +#include <libplacebo/dummy.h>
>  #include <libplacebo/shaders.h>
>  #include <libplacebo/shaders/colorspace.h>
> +#include <libplacebo/shaders/sampling.h>
>  #include "../placebo_utils.h"
>  #endif
>  
> @@ -65,6 +67,15 @@ struct vlc_gl_sampler_priv {
>      GLfloat conv_matrix[4*4];
>  
>      /* libplacebo context */
> +    struct {
> +        GLuint lut_texture;
> +        GLuint texture;
> +        GLint *pl_vars;
> +        GLint *pl_desc;
> +        const struct pl_shader_res *sh;
> +        const struct pl_shader_res *sh_color;
> +    } upscale;
> +    const struct pl_gpu *pl_gpu;
>      struct pl_context *pl_ctx;
>      struct pl_shader *pl_sh;
>      const struct pl_shader_res *pl_sh_res;
> @@ -84,6 +95,9 @@ struct vlc_gl_sampler_priv {
>      struct vlc_gl_interop *interop;
>  };
>  
> +static const int polar_filters[] = { 0, SCALE_EWA_JINC, SCALE_EWA_LANCZOS,
> +SCALE_EWA_GINSENG, SCALE_HAASNSOFT, SCALE_EWA_HANN };
> +
>  #define PRIV(sampler) container_of(sampler, struct vlc_gl_sampler_priv, sampler)
>  
>  static const float MATRIX_COLOR_RANGE_LIMITED[4*3] = {
> @@ -698,12 +712,457 @@ InitOrientationMatrix(GLfloat matrix[static 4*4],
>      }
>  }
>  
> +static void
> +sampler_scale_load(const struct vlc_gl_sampler *sampler)
> +{
> +    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
> +    const struct vlc_gl_interop *interop = priv->interop;
> +    const opengl_vtable_t *vt = priv->vt;
> +
> +    /*Default texture setup*/
> +    if (priv->yuv_color)
> +        vt->UniformMatrix4fv(priv->uloc.ConvMatrix, 1, GL_FALSE, priv->conv_matrix);
> +
> +    for (unsigned i = 0; i < interop->tex_count; ++i)
> +    {
> +        vt->Uniform1i(priv->uloc.Textures[i], i);
> +
> +        assert(priv->textures[i] != 0);
> +        vt->ActiveTexture(GL_TEXTURE0 + i);
> +        vt->BindTexture(interop->tex_target, priv->textures[i]);
> +
> +        vt->UniformMatrix3fv(priv->uloc.TexCoordsMaps[i], 1, GL_FALSE,
> +                             priv->var.TexCoordsMaps[i]);
> +    }
> +
> +    const GLfloat *tm = GetTransformMatrix(interop);
> +    vt->UniformMatrix4fv(priv->uloc.TransformMatrix, 1, GL_FALSE, tm);
> +
> +    vt->UniformMatrix4fv(priv->uloc.OrientationMatrix, 1, GL_FALSE,
> +                         priv->var.OrientationMatrix);
> +
> +    if (interop->tex_target == GL_TEXTURE_RECTANGLE)
> +    {
> +        for (unsigned i = 0; i < interop->tex_count; ++i)
> +            vt->Uniform2f(priv->uloc.TexSizes[i], priv->tex_widths[i],
> +                          priv->tex_heights[i]);
> +    }
> +
> +    /*placebo shader requierements*/
> +    const struct pl_shader_res *res = priv->upscale.sh;
> +    for (int i = 0; res && i < res->num_variables; i++)
> +    {
> +        GLint loc = priv->upscale.pl_vars[i];
> +        assert(loc != -1);
> +
> +        struct pl_shader_var sv = res->variables[i];
> +        struct pl_var var = sv.var;
> +
> +        assert(!(var.type != PL_VAR_FLOAT));
> +        assert(!(var.dim_m > 1 && var.dim_m != var.dim_v));
> +
> +        const float *f = sv.data;
> +        switch (var.dim_m)
> +        {
> +            case 4: vt->UniformMatrix4fv(loc, 1, GL_FALSE, f); break;
> +            case 3: vt->UniformMatrix3fv(loc, 1, GL_FALSE, f); break;
> +            case 2: vt->UniformMatrix2fv(loc, 1, GL_FALSE, f); break;
> +
> +            case 1:
> +                    switch (var.dim_v)
> +                    {
> +                        case 1: vt->Uniform1f(loc, f[0]); break;
> +                        case 2: vt->Uniform2f(loc, f[0], f[1]); break;
> +                        case 3: vt->Uniform3f(loc, f[0], f[1], f[2]); break;
> +                        case 4: vt->Uniform4f(loc, f[0], f[1], f[2], f[3]); break;
> +                    }
> +                    break;
> +        }
> +    }
> +
> +    for (int i = 0; res && i < res->num_descriptors; i++)
> +    {
> +        GLint loc = priv->upscale.pl_desc[i];
> +        assert(loc != -1);
> +
> +        vt->ActiveTexture(GL_TEXTURE0 + PICTURE_PLANE_MAX + i );
> +        // TODO: bind the correct target_type
> +
> +        if (i == 0)
> +            vt->BindTexture(GL_TEXTURE_1D, priv->upscale.lut_texture);

This code assumes the LUT texture is the first descriptor. The order
(and number) of descriptors is not something that should be relied on.
Recent versions of libplacebo, in particular, try using a static const
array for the LUT, which doesn't require a descriptor at all. But for
now, you can probably just add an assertion that `num_descriptors` is
either 1 or 0.

Note that you also may need to attach the descriptors for the `sh_color`
shader, which can include a 2D dithering matrix - depending on the
dithering method selected.

I would like to once again re-emphasize here that libplacebo will do all
of this logic correctly and automatically if you switch from
`pl_gpu_dummy_create` to `pl_opengl_create`.

> +        vt->Uniform1i(loc, PICTURE_PLANE_MAX + i);
> +     }
> +}
> +
> +static void
> +sampler_scale_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
> +{
> +    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
> +    const opengl_vtable_t *vt = priv->vt;
> +    const struct vlc_gl_interop *interop = priv->interop;
> +
> +    /*Default texture setup*/
> +    if (priv->yuv_color)
> +    {
> +        priv->uloc.ConvMatrix = vt->GetUniformLocation(program, "ConvMatrix");
> +        assert(priv->uloc.ConvMatrix != -1);
> +    }
> +
> +    priv->uloc.TransformMatrix =
> +        vt->GetUniformLocation(program, "TransformMatrix");
> +    assert(priv->uloc.TransformMatrix != -1);
> +
> +    priv->uloc.OrientationMatrix =
> +        vt->GetUniformLocation(program, "OrientationMatrix");
> +    assert(priv->uloc.OrientationMatrix != -1);
> +
> +    for (unsigned int i = 0; i < interop->tex_count; ++i)
> +    {
> +        char name[sizeof("TexCoordsMapX")];
> +
> +        snprintf(name, sizeof(name), "Texture%1u", i);
> +        priv->uloc.Textures[i] = vt->GetUniformLocation(program, name);
> +        assert(priv->uloc.Textures[i] != -1);
> +
> +        snprintf(name, sizeof(name), "TexCoordsMap%1u", i);
> +        priv->uloc.TexCoordsMaps[i] = vt->GetUniformLocation(program, name);
> +        assert(priv->uloc.TexCoordsMaps[i] != -1);
> +
> +        if (interop->tex_target == GL_TEXTURE_RECTANGLE)
> +        {
> +            snprintf(name, sizeof(name), "TexSize%1u", i);
> +            priv->uloc.TexSizes[i] = vt->GetUniformLocation(program, name);
> +            assert(priv->uloc.TexSizes[i] != -1);
> +        }
> +    }
> +
> +    const struct pl_shader_res *res = priv->upscale.sh;
> +    GLint *pl_vars = priv->upscale.pl_vars;
> +    for (int i = 0; i < res->num_variables; i++)
> +    {
> +        struct pl_shader_var sv = res->variables[i];
> +        pl_vars[i] = vt->GetUniformLocation(program, sv.var.name);
> +        assert(pl_vars[i] != -1);
> +    }
> +
> +    GLint *pl_desc = priv->upscale.pl_desc;
> +    for (int i = 0; i < res->num_descriptors; i++)
> +    {
> +        struct pl_shader_desc desc = res->descriptors[i];
> +        pl_desc[i] = vt->GetUniformLocation(program, desc.desc.name);
> +        assert(pl_desc[i] != -1);
> +    }
> +}
> +
> +#define ADD(x) vlc_memstream_puts(&ms, x)
> +#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
> +
> +static int
> +opengl_init_shader_scale(struct vlc_gl_sampler *sampler, GLenum tex_target,
> +                            vlc_fourcc_t chroma, video_color_space_t yuv_space,
> +                            video_orientation_t orientation)
> +{
> +    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
> +    struct vlc_gl_interop *interop = priv->interop;
> +    const opengl_vtable_t *vt = priv->vt;
> +
> +    const char *swizzle_per_tex[PICTURE_PLANE_MAX] = { NULL };
> +    const bool is_yuv = vlc_fourcc_IsYUV(chroma);
> +
> +    const vlc_chroma_description_t *desc = vlc_fourcc_GetChromaDescription(chroma);
> +    if (desc == NULL)
> +        return VLC_EGENERIC;
> +
> +    InitOrientationMatrix(priv->var.OrientationMatrix, orientation);
> +
> +    if (is_yuv)
> +    {
> +        int ret;
> +        ret = sampler_yuv_base_init(sampler, chroma, desc, yuv_space);
> +        if (ret != VLC_SUCCESS)
> +            return ret;
> +        ret = opengl_init_swizzle(interop, swizzle_per_tex, chroma, desc);
> +        if (ret != VLC_SUCCESS)
> +            return ret;
> +    }
> +
> +    const char *sampler_name;
> +    switch (tex_target)
> +    {
> +        case GL_TEXTURE_EXTERNAL_OES:
> +            sampler_name = "samplerExternalOES";
> +            break;
> +        case GL_TEXTURE_2D:
> +            sampler_name = "sampler2D";
> +            break;
> +        case GL_TEXTURE_RECTANGLE:
> +            sampler_name = "sampler2DRect";
> +            break;
> +        default:
> +            vlc_assert_unreachable();
> +    }
> +
> +    struct pl_plane_data planes[4] = { 0 };
> +    int nb_planes = vlc_placebo_PlaneFormat(&interop->fmt_out, planes);
> +    assert(nb_planes);
> +
> +    int num_components = 0;
> +    while (planes[0].component_size[num_components])
> +        ++num_components;
> +
> +    struct pl_tex_params tex_params = {
> +        .h = interop->fmt_out.i_height * 2,
> +        .w = interop->fmt_out.i_width * 2,

Why is this * 2?

> +        .d = 0,
> +        .format = pl_find_fmt(priv->pl_gpu, PL_FMT_UNORM, num_components, 0, 0, PL_FMT_CAP_RENDERABLE),
> +        .sampleable = true,
> +        .sample_mode = PL_TEX_SAMPLE_LINEAR,
> +        .address_mode = PL_TEX_ADDRESS_CLAMP,
> +        .user_data = NULL,
> +    };
> +    assert(tex_params.format);
> +
> +    struct pl_sample_src sample_src =
> +    {
> +        .tex = NULL,
> +        .sampler_params = tex_params,
> +        .sampled_w = interop->fmt_out.i_width,
> +        .sampled_h = interop->fmt_out.i_height,

This signature was changed in libplacebo v2.72.0. I would recommend
raising the minimum version of libplacebo to v2.72 and using the new
signature, which does away with the need for `tex_params` and
`pl_find_fmt` altogether.

(And also allow us to drop a bunch of #if PL_API_VER checks)

> +        .new_w = 0,
> +        .new_h = 0,
> +        .scale = 0,
> +        .components = num_components,
> +    };
> +
> +    struct pl_shader_obj *lut = NULL;
> +    int filter_id = polar_filters[var_InheritInteger(priv->gl, "pl-scale")];
> +    struct pl_sample_filter_params filter_params = {
> +        .filter = *scale_config[filter_id],
> +        .lut = &lut,
> +    };
> +
> +    struct pl_glsl_desc gl_desc = { .version = 120, .gles = false, .vulkan = false, };

This is inconsistent with the glsl version you specify in
`pl_gpu_dummy_create`, and also unneeded (the `pl_gpu` already has this
information).

> +
> +    struct pl_shader *shader_color = pl_shader_alloc(priv->pl_ctx,
> +        &(struct pl_shader_params) { .id = 0, .gpu = priv->pl_gpu, .glsl = gl_desc, });
> +    assert(shader_color);
> +
> +    struct pl_color_map_params color_params = pl_color_map_default_params;
> +    color_params.intent = var_InheritInteger(priv->gl, "rendering-intent");
> +    color_params.tone_mapping_algo = var_InheritInteger(priv->gl, "tone-mapping");
> +    color_params.tone_mapping_param = var_InheritFloat(priv->gl, "tone-mapping-param");
> +    #if PL_API_VER >= 10
> +        color_params.desaturation_strength = var_InheritFloat(priv->gl, "desat-strength");
> +        color_params.desaturation_exponent = var_InheritFloat(priv->gl, "desat-exponent");
> +        color_params.desaturation_base = var_InheritFloat(priv->gl, "desat-base");
> +    #else
> +        color_params.tone_mapping_desaturate = var_InheritFloat(priv->gl, "tone-mapping-desat");
> +    #endif
> +        color_params.gamut_warning = var_InheritBool(priv->gl, "tone-mapping-warn");
> +
> +    struct pl_color_space dst_space = pl_color_space_unknown;
> +    dst_space.primaries = var_InheritInteger(priv->gl, "target-prim");
> +    dst_space.transfer = var_InheritInteger(priv->gl, "target-trc");
> +
> +    pl_shader_color_map(shader_color, &color_params,
> +            vlc_placebo_ColorSpace(&interop->fmt_out),
> +            dst_space, NULL, false);
> +
> +    struct pl_shader_obj *dither_state = NULL;
> +    int method = var_InheritInteger(priv->gl, "dither-algo");
> +    if (method >= 0)
> +    {
> +        unsigned out_bits = 0;
> +        int override = var_InheritInteger(priv->gl, "dither-depth");
> +        if (override > 0)
> +            out_bits = override;
> +        else
> +        {
> +            GLint fb_depth = 0;
> +            #if !defined(USE_OPENGL_ES2)
> +                   /* fetch framebuffer depth (we are already bound to the default one). */
> +                   if (vt->GetFramebufferAttachmentParameteriv != NULL)
> +                       vt->GetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_BACK_LEFT,
> +                                                               GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE,
> +                                                               &fb_depth);
> +            #endif
> +            if (fb_depth <= 0)
> +                fb_depth = 8;
> +            out_bits = fb_depth;
> +         }
> +
> +         pl_shader_dither(shader_color, out_bits, &dither_state, &(struct pl_dither_params) {
> +             .method = method,
> +         });
> +         pl_shader_obj_destroy(&dither_state);
> +    }
> +    priv->upscale.sh_color = pl_shader_finalize(shader_color);
> +
> +    GLenum texture_type;
> +    const char *sampler_lut;
> +    assert(filter_params.filter.polar);
> +    if (filter_params.filter.polar)
> +    {
> +        struct pl_shader *shader = pl_shader_alloc(priv->pl_ctx,
> +                &(struct pl_shader_params) { .id = 0, .gpu = priv->pl_gpu, .glsl = gl_desc, });
> +        assert(shader);
> +
> +        pl_shader_sample_polar(shader, &sample_src, &filter_params);
> +        priv->upscale.sh = pl_shader_finalize(shader);
> +        texture_type = GL_TEXTURE_1D;
> +        sampler_lut = "sampler1D";
> +    }
> +
> +    const struct pl_shader_res *result = priv->upscale.sh;
> +    assert(result);
> +    float *data = NULL;
> +    unsigned lut_w, lut_h;
> +    for (int n = 0; n < result->num_descriptors; n++)
> +    {
> +      const struct pl_shader_desc *sd = &result->descriptors[n];
> +
> +      if(sd->desc.type != PL_DESC_SAMPLED_TEX)
> +          continue;

You should probably change this `continue` to an error, because you
don't handle any other descriptor types.

> +
> +      const struct pl_tex *tex = sd->object;
> +      data = (float *) pl_tex_dummy_data(tex);
> +      lut_w = tex->params.w;
> +      lut_h = tex->params.h;
> +      if (data)
> +          break;
> +    }
> +    assert(data);

This assertion can fail, because `num_descriptors` can be 0.

> +
> +    vt->ActiveTexture(GL_TEXTURE0 + PICTURE_PLANE_MAX);
> +    vt->GenTextures(1, &priv->upscale.lut_texture);
> +    assert(lut_h == 0);
> +    vt->BindTexture(texture_type, priv->upscale.lut_texture);
> +    vt->TexImage1D(texture_type, 0, GL_RGBA, lut_w, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
> +    vt->TexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
> +    vt->TexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
> +    vt->TexParameteri(texture_type, GL_TEXTURE_WRAP_S, GL_REPEAT);
> +    vt->TexParameteri(texture_type, GL_TEXTURE_WRAP_T, GL_REPEAT);
> +    vt->ActiveTexture(GL_TEXTURE0);
> +
> +    struct vlc_memstream ms;
> +    if (vlc_memstream_open(&ms) != 0)
> +        return VLC_EGENERIC;
> +
> +    if (is_yuv)
> +        ADD("uniform mat4 ConvMatrix;\n");
> +
> +    ADD("uniform mat4 TransformMatrix;\n"
> +        "uniform mat4 OrientationMatrix;\n");
> +    for (unsigned i = 0; i < interop->tex_count; ++i)
> +        ADDF("uniform %s Texture%u;\n"
> +             "uniform mat3 TexCoordsMap%u;\n", sampler_name, i, i);
> +
> +    priv->upscale.pl_vars = calloc(priv->upscale.sh->num_variables, sizeof(GLint));
> +    priv->upscale.pl_desc = calloc(priv->upscale.sh->num_descriptors, sizeof(GLint));
> +
> +    for (int n = 0; n < result->num_variables; n++)
> +    {
> +        const struct pl_shader_var *shader_var = &result->variables[n];
> +        const struct pl_var *var = &shader_var->var;
> +
> +        ADDF("uniform %s %s;\n", pl_var_glsl_type_name(*var), var->name);
> +    }
> +
> +    for (int n = 0; n < result->num_vertex_attribs; n++)
> +    {
> +        const struct pl_shader_va   *vertex_attribs = &result->vertex_attribs[n];
> +        const struct pl_vertex_attrib *attr = &vertex_attribs->attr;
> +        const struct pl_fmt *fmt = attr->fmt;
> +        const char *name = attr->name;
> +
> +        ADDF("uniform %s %s;\n", fmt->glsl_type, name);
> +    }
> +
> +    for (int n = 0; n < result->num_descriptors; n++)
> +    {
> +        const struct pl_shader_desc *sd = &result->descriptors[n];
> +        const struct pl_desc *sh_desc = &sd->desc;
> +
> +        ADDF("uniform %s %s;\n", sampler_lut, sh_desc->name);
> +    }
> +    ADDF("%s", priv->upscale.sh->glsl);
> +    ADDF("%s", priv->upscale.sh_color->glsl);
> +
> +    ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
> +        " vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * "
> +        "vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
> +        " vec2 tex_coords;\n");
> +
> +
> +    unsigned color_count;
> +    if (is_yuv) {
> +        ADD(" vec4 texel;\n"
> +            " vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0);\n");
> +
> +        unsigned color_idx = 0;
> +        for (unsigned i = 0; i < interop->tex_count; ++i)
> +        {
> +            const char *swizzle = swizzle_per_tex[i];
> +            assert(swizzle);
> +            size_t swizzle_count = strlen(swizzle);
> +            ADDF(" tex_coords = (TexCoordsMap%u * pic_hcoords).st;\n", i);
> +            if (tex_target == GL_TEXTURE_RECTANGLE)
> +            {
> +                /* The coordinates are in texels values, not normalized */
> +                ADDF(" tex_coords = vec2(tex_coords.x * TexSize%u.x,\n"
> +                     "                   tex_coords.y * TexSize%u.y);\n", i, i);
> +            }
> +            ADDF(" texel = %s(Texture%.1u, tex_coords);\n", priv->upscale.sh->name, i);
> +            for (unsigned j = 0; j < swizzle_count; ++j)
> +            {
> +                ADDF(" pixel[%u] = texel.%c;\n", color_idx, swizzle[j]);
> +                color_idx++;
> +                assert(color_idx <= PICTURE_PLANE_MAX);
> +            }
> +        }
> +        ADD(" vec4 result = ConvMatrix * pixel;\n");
> +        color_count = color_idx;
> +    }
> +    else
> +    {
> +        ADD(" tex_coords = (TexCoordsMap0 * pic_hcoords).st;\n");
> +        ADDF(" vec4 result = %s(Texture0, tex_coords);\n", priv->upscale.sh->name);
> +        color_count = 1;
> +    }
> +    assert(yuv_space == COLOR_SPACE_UNDEF || color_count == 3);
> +
> +    ADDF(" return result = %s(result);\n", priv->upscale.sh_color->name);
> +    ADD("}\n");
> +#undef ADD
> +#undef ADDF
> +
> +    if (vlc_memstream_close(&ms) != 0)
> +        return VLC_EGENERIC;
> +
> +    sampler->shader.extensions = NULL;
> +    sampler->shader.body = ms.ptr;
> +
> +    static const struct vlc_gl_sampler_ops ops = {
> +        .fetch_locations = sampler_scale_fetch_locations,
> +        .load = sampler_scale_load,
> +    };
> +    sampler->ops = &ops;
> +
> +    return VLC_SUCCESS;
> +}
> +
>  static int
>  opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
>                              vlc_fourcc_t chroma, video_color_space_t yuv_space,
>                              video_orientation_t orientation)
>  {
>      struct vlc_gl_sampler_priv *priv = PRIV(sampler);
> +    int scale_filter = var_InheritInteger(priv->gl, "pl-scale");
> +    if(scale_filter > 0 && scale_filter < 6)
> +    {
> +        return opengl_init_shader_scale(sampler, tex_target, chroma, yuv_space,
> +                                        orientation);
> +    }
>  
>      struct vlc_gl_interop *interop = priv->interop;
>  
> @@ -971,6 +1430,7 @@ vlc_gl_sampler_New(struct vlc_gl_interop *interop)
>  #       endif
>              .vulkan = false,
>          },
> +        .limits = { 0 },
>      };
>  
>      const opengl_vtable_t *vt = interop->vt;
> @@ -1076,6 +1536,12 @@ vlc_gl_sampler_Delete(struct vlc_gl_sampler *sampler)
>  
>  #ifdef HAVE_LIBPLACEBO
>      FREENULL(priv->uloc.pl_vars);
> +    if (priv->upscale.pl_vars)
> +        FREENULL(priv->upscale.pl_vars);
> +    if (priv->upscale.pl_vars)
> +        FREENULL(priv->upscale.pl_desc);
> +    if (priv->pl_gpu)
> +        pl_gpu_dummy_destroy(&priv->pl_gpu);
>      if (priv->pl_ctx)
>          pl_context_destroy(&priv->pl_ctx);
>  #endif
> diff --git a/modules/video_output/opengl/vout_helper.h b/modules/video_output/opengl/vout_helper.h
> index 482eabb554..3937c1e3e6 100644
> --- a/modules/video_output/opengl/vout_helper.h
> +++ b/modules/video_output/opengl/vout_helper.h
> @@ -51,6 +51,9 @@
>  #endif
>  
>  #define add_glopts_placebo() \
> +    set_section(N_("Scaling algorithm"), NULL) \
> +    add_integer("pl-scale", 0, SCALE_FILTER_TEXT, SCALE_FILTER_LONGTEXT, false) \
> +        change_integer_list(upscale_filters_values, upscale_filters_text) \
>      set_section(N_("Colorspace conversion"), NULL) \
>      add_integer("rendering-intent", pl_color_map_default_params.intent, \
>                  RENDER_INTENT_TEXT, RENDER_INTENT_LONGTEXT, false) \
> diff --git a/modules/video_output/placebo_utils.h b/modules/video_output/placebo_utils.h
> index 99c3f318b5..cdfb040f16 100644
> --- a/modules/video_output/placebo_utils.h
> +++ b/modules/video_output/placebo_utils.h
> @@ -330,6 +330,29 @@ static const struct pl_filter_config *const scale_config[] = {
>      [SCALE_CUSTOM]              = NULL,
>  };
>  
> +#define SCALE_FILTER_TEXT "Upscale filters"
> +#define SCALE_FILTER_LONGTEXT "Upscale filters"
> +
> +static const int upscale_filters_values[] = {
> +     0,
> +     1,
> +     2,
> +     3,
> +     4,
> +     5,
> +};
> +
> +static const char * const upscale_filters_text[] = {
> +    "No filter",
> +    "Unwindowed EWA Jinc (clipped)",
> +    "Jinc / EWA Lanczos 3 taps (high quality, slow)",
> +    "EWA Ginseng",
> +    "EWA Hann",
> +    "HaasnSoft (blurred EWA Hann)",
> +    "EWA Robidoux",
> +    "EWA RobidouxSharp",
> +};
> +
>  #define UPSCALER_PRESET_TEXT "Upscaler preset"
>  #define DOWNSCALER_PRESET_TEXT "Downscaler preset"
>  #define SCALER_PRESET_LONGTEXT "Choose from one of the built-in scaler presets. If set to custom, you can choose your own combination of kernel/window functions."
> -- 
> 2.28.0
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel


More information about the vlc-devel mailing list