[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