[vlc-devel] [PATCH 20/27] opengl: enable multisampling

Alexandre Janniaux ajanni at videolabs.io
Mon Jun 29 16:57:18 CEST 2020


On Thu, Jun 25, 2020 at 02:23:07PM +0200, Romain Vimont wrote:
> Enable multisampling anti-aliasing:
>  - render to an intermediate multisample renderbuffer;
>  - resolve it to the output framebuffer.
>
> Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
> ---
>  modules/video_output/opengl/filter.c      |  10 ++-
>  modules/video_output/opengl/filter.h      |  12 +++
>  modules/video_output/opengl/filter_priv.h |   4 +
>  modules/video_output/opengl/filters.c     | 100 +++++++++++++++++++++-
>  modules/video_output/opengl/gl_api.c      |  16 ++++
>  modules/video_output/opengl/gl_api.h      |   3 +
>  6 files changed, 141 insertions(+), 4 deletions(-)
>
> diff --git a/modules/video_output/opengl/filter.c b/modules/video_output/opengl/filter.c
> index 208ccc12cb..819f5078aa 100644
> --- a/modules/video_output/opengl/filter.c
> +++ b/modules/video_output/opengl/filter.c
> @@ -50,6 +50,7 @@ vlc_gl_filter_New(vlc_object_t *parent, const struct vlc_gl_api *api)
>      struct vlc_gl_filter *filter = &priv->filter;
>      filter->api = api;
>      filter->config.blend = false;
> +    filter->config.msaa_level = 0;
>      filter->ops = NULL;
>      filter->sys = NULL;
>      filter->module = NULL;
> @@ -109,12 +110,19 @@ vlc_gl_filter_Delete(struct vlc_gl_filter *filter)
>      if (priv->sampler)
>          vlc_gl_sampler_Delete(priv->sampler);
>
> +    const opengl_vtable_t *vt = &filter->api->vt;
> +
>      if (priv->has_framebuffer_out)
>      {
> -        const opengl_vtable_t *vt = &filter->api->vt;
>          vt->DeleteFramebuffers(1, &priv->framebuffer_out);
>          vt->DeleteTextures(1, &priv->texture_out);
>      }
>
> +    if (filter->config.msaa_level)
> +    {
> +        vt->DeleteFramebuffers(1, &priv->framebuffer_msaa);
> +        vt->DeleteRenderbuffers(1, &priv->renderbuffer_msaa);
> +    }
> +
>      vlc_object_delete(&filter->obj);
>  }
> diff --git a/modules/video_output/opengl/filter.h b/modules/video_output/opengl/filter.h
> index 88b4f572b0..399087d68f 100644
> --- a/modules/video_output/opengl/filter.h
> +++ b/modules/video_output/opengl/filter.h
> @@ -86,6 +86,18 @@ struct vlc_gl_filter {
>           * This flag must be set by the filter module (default is false).
>           */
>          bool blend;
> +
> +        /**
> +         * Request MSAA level.
> +         *
> +         * This value must be set by the filter module (default is 0, which
> +         * means disabled).
> +         *
> +         * The actual MSAA level may be overwritten to 0 if multisampling is
> +         * not supported, or to a higher value if another filter rendering on
> +         * the same framebuffer requested a higher MSAA level.
> +         */
> +        unsigned msaa_level;
>      } config;
>
>      const struct vlc_gl_filter_ops *ops;
> diff --git a/modules/video_output/opengl/filter_priv.h b/modules/video_output/opengl/filter_priv.h
> index de1c4cd319..f4c983ce85 100644
> --- a/modules/video_output/opengl/filter_priv.h
> +++ b/modules/video_output/opengl/filter_priv.h
> @@ -41,6 +41,10 @@ struct vlc_gl_filter_priv {
>      bool has_framebuffer_out;
>      GLuint framebuffer_out; /* owned (this filter must delete it) */
>      GLuint texture_out; /* owned (attached to framebuffer_out) */
> +
> +    /* For multisampling, if msaa_level != 0 */
> +    GLuint framebuffer_msaa; /* owned */
> +    GLuint renderbuffer_msaa; /* owned (attached to framebuffer_msaa) */
>      /* } */
>
>      /* For lazy-loading sampler */
> diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c
> index 70710d69af..f22478893b 100644
> --- a/modules/video_output/opengl/filters.c
> +++ b/modules/video_output/opengl/filters.c
> @@ -124,6 +124,34 @@ InitFramebufferOut(struct vlc_gl_filter_priv *priv)
>      return VLC_SUCCESS;
>  }
>
> +static int
> +InitFramebufferMSAA(struct vlc_gl_filter_priv *priv, unsigned msaa_level)
> +{
> +    assert(msaa_level);
> +    assert(priv->size_out.width > 0 && priv->size_out.height > 0);
> +
> +    const opengl_vtable_t *vt = &priv->filter.api->vt;
> +
> +    vt->GenRenderbuffers(1, &priv->renderbuffer_msaa);
> +    vt->BindRenderbuffer(GL_RENDERBUFFER, priv->renderbuffer_msaa);
> +    vt->RenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level,
> +                                       GL_RGBA8,
> +                                       priv->size_out.width,
> +                                       priv->size_out.height);
> +
> +    vt->GenFramebuffers(1, &priv->framebuffer_msaa);
> +    vt->BindFramebuffer(GL_FRAMEBUFFER, priv->framebuffer_msaa);
> +    vt->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
> +                                GL_RENDERBUFFER, priv->renderbuffer_msaa);
> +
> +    GLenum status = vt->CheckFramebufferStatus(GL_FRAMEBUFFER);
> +    if (status != GL_FRAMEBUFFER_COMPLETE)
> +        return VLC_EGENERIC;
> +
> +    vt->BindFramebuffer(GL_FRAMEBUFFER, 0);
> +    return VLC_SUCCESS;
> +}
> +
>  static struct vlc_gl_sampler *
>  GetSampler(struct vlc_gl_filter *filter)
>  {
> @@ -256,9 +284,53 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
>  int
>  vlc_gl_filters_InitFramebuffers(struct vlc_gl_filters *filters)
>  {
> -    struct vlc_gl_filter_priv *priv;
> +    struct vlc_gl_filter_priv *priv = NULL;
> +    struct vlc_gl_filter_priv *subfilter_priv;
> +
>      vlc_list_foreach(priv, &filters->list, node)
>      {
> +        /* Compute the highest msaa_level among the filter and its subfilters */
> +        unsigned msaa_level = 0;
> +        if (filters->api->supports_multisample)
> +        {
> +            msaa_level = priv->filter.config.msaa_level;
> +            vlc_list_foreach(subfilter_priv, &priv->blend_subfilters, node)
> +            {
> +                if (subfilter_priv->filter.config.msaa_level > msaa_level)
> +                    msaa_level = subfilter_priv->filter.config.msaa_level;
> +            }
> +        }
> +
> +        /* Update the actual msaa_level used to create the MSAA framebuffer */
> +        priv->filter.config.msaa_level = msaa_level;
> +        /* Also update the actual msaa_level for subfilters, just for info */
> +        vlc_list_foreach(subfilter_priv, &priv->blend_subfilters, node)
> +            subfilter_priv->filter.config.msaa_level = msaa_level;
> +    }
> +
> +    /* "priv" is the last filter */
> +    assert(priv); /* There is at least one filter */
> +
> +    if (priv->filter.config.msaa_level)
> +    {
> +        /* Resolving multisampling to the default framebuffer might fail,
> +         * because its format may be different. So insert a "draw" filter. */
> +        struct vlc_gl_filter *draw =
> +            vlc_gl_filters_Append(filters, "draw", NULL);
> +        if (!draw)
> +            return VLC_EGENERIC;
> +    }
> +
> +    vlc_list_foreach(priv, &filters->list, node)
> +    {
> +        unsigned msaa_level = priv->filter.config.msaa_level;
> +        if (msaa_level)
> +        {
> +            int ret = InitFramebufferMSAA(priv, msaa_level);
> +            if (ret != VLC_SUCCESS)
> +                return ret;
> +        }
> +
>          bool is_last = vlc_list_is_last(&priv->node, &filters->list);
>          if (!is_last)
>          {
> @@ -326,8 +398,13 @@ vlc_gl_filters_Draw(struct vlc_gl_filters *filters)
>              }
>          }
>
> -        GLuint draw_fb = priv->has_framebuffer_out ? priv->framebuffer_out
> -                                                   : draw_framebuffer;
> +        unsigned msaa_level = priv->filter.config.msaa_level;
> +        GLuint draw_fb;
> +        if (msaa_level)
> +            draw_fb = priv->framebuffer_msaa;
> +        else
> +            draw_fb = priv->has_framebuffer_out ? priv->framebuffer_out
> +                                                : draw_framebuffer;
>
>          vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb);
>
> @@ -358,6 +435,23 @@ vlc_gl_filters_Draw(struct vlc_gl_filters *filters)
>              if (ret != VLC_SUCCESS)
>                  return ret;
>          }
> +
> +        if (filter->config.msaa_level)
> +        {
> +            /* Never resolve multisampling to the default framebuffer */
> +            assert(priv->has_framebuffer_out);
> +            assert(priv->framebuffer_out != draw_framebuffer);
> +
> +            /* Resolve the MSAA into the target framebuffer */
> +            vt->BindFramebuffer(GL_READ_FRAMEBUFFER, priv->framebuffer_msaa);
> +            vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, priv->framebuffer_out);
> +
> +            GLint width = priv->size_out.width;
> +            GLint height = priv->size_out.height;
> +            vt->BlitFramebuffer(0, 0, width, height,
> +                                0, 0, width, height,
> +                                GL_COLOR_BUFFER_BIT, GL_NEAREST);
> +        }
>      }
>
>      return VLC_SUCCESS;
> diff --git a/modules/video_output/opengl/gl_api.c b/modules/video_output/opengl/gl_api.c
> index 68c42cc6d4..46a27384fd 100644
> --- a/modules/video_output/opengl/gl_api.c
> +++ b/modules/video_output/opengl/gl_api.c
> @@ -151,6 +151,22 @@ vlc_gl_api_Init(struct vlc_gl_api *api, vlc_gl_t *gl)
>          return VLC_EGENERIC;
>      }
>
> +    const char *version = (const char *) api->vt.GetString(GL_VERSION);
> +#ifdef USE_OPENGL_ES2
> +#   define PREFIX "OpenGL ES "
> +    /* starts with "OpenGL ES " */
> +    assert(!strncmp(version, PREFIX, sizeof(PREFIX) - 1));
> +    /* skip the prefix */
> +    version += sizeof(PREFIX) - 1;
> +#endif
> +
> +    /* OpenGL >= 3.0:
> +     *     https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glRenderbufferStorageMultisample.xhtml
> +     * OpenGL ES >= 3.0:
> +     *     https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glRenderbufferStorageMultisample.xhtml
> +     */
> +    api->supports_multisample = strverscmp(version, "3.0") >= 0;

Might be replaced and simplified into

```
GL_ASSERT_NO_ERROR(vt);
GLint version;
vt->GetIntegerv(GL_MAJOR_VERSION, &version);
GLenum error = vt->GetError();
api->supports_multisample = version >= 3 && error == GL_NO_ERROR;
```

> +
>  #ifdef USE_OPENGL_ES2
>      api->is_gles = true;
>      /* OpenGL ES 2 includes support for non-power of 2 textures by specification
> diff --git a/modules/video_output/opengl/gl_api.h b/modules/video_output/opengl/gl_api.h
> index 2ddae33178..49b46ae110 100644
> --- a/modules/video_output/opengl/gl_api.h
> +++ b/modules/video_output/opengl/gl_api.h
> @@ -43,6 +43,9 @@ struct vlc_gl_api {
>
>      /* Non-power-of-2 texture size support */
>      bool supports_npot;
> +
> +    /* Multisampling for anti-aliasing */
> +    bool supports_multisample;
>  };
>
>  int
> --
> 2.27.0
>


More information about the vlc-devel mailing list