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

Romain Vimont rom1v at videolabs.io
Mon Jun 29 17:24:25 CEST 2020


On Mon, Jun 29, 2020 at 04:57:18PM +0200, Alexandre Janniaux wrote:
> 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);

GL_MAJOR_VERSION is available only for OpenGL >= 3.

Note that it could be a hacky way to detect OpenGL >= 3 ;)

> 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
> >
> _______________________________________________
> 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