[vlc-devel] [PATCH v3 11/22] opengl: create and bind filters framebuffers

Romain Vimont rom1v at videolabs.io
Mon Jul 6 12:47:04 CEST 2020


Create one framebuffer and its associate texture for each filter, and
bind them as necessary before drawing so that the output of a filter is
the input of the next one.

Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
Co-authored-by: Maxime Meissonnier <mmeisson at outlook.fr>
---
 modules/video_output/opengl/filter.c      | 11 ++++
 modules/video_output/opengl/filter_priv.h |  4 ++
 modules/video_output/opengl/filters.c     | 72 +++++++++++++++++++++++
 3 files changed, 87 insertions(+)

diff --git a/modules/video_output/opengl/filter.c b/modules/video_output/opengl/filter.c
index 80a0459be9..11007edfcd 100644
--- a/modules/video_output/opengl/filter.c
+++ b/modules/video_output/opengl/filter.c
@@ -30,6 +30,7 @@
 #include <vlc_common.h>
 #include <vlc_modules.h>
 
+#include "gl_api.h"
 #include "sampler_priv.h"
 
 #undef vlc_gl_filter_New
@@ -44,6 +45,8 @@ vlc_gl_filter_New(vlc_object_t *parent, const struct vlc_gl_api *api)
     priv->size_out.width = 0;
     priv->size_out.height = 0;
 
+    priv->has_framebuffer_out = false;
+
     struct vlc_gl_filter *filter = &priv->filter;
     filter->api = api;
     filter->ops = NULL;
@@ -94,8 +97,16 @@ vlc_gl_filter_Delete(struct vlc_gl_filter *filter)
         module_unneed(filter, filter->module);
 
     struct vlc_gl_filter_priv *priv = vlc_gl_filter_PRIV(filter);
+
     if (priv->sampler)
         vlc_gl_sampler_Delete(priv->sampler);
 
+    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);
+    }
+
     vlc_object_delete(&filter->obj);
 }
diff --git a/modules/video_output/opengl/filter_priv.h b/modules/video_output/opengl/filter_priv.h
index 50134faf50..8e177908f3 100644
--- a/modules/video_output/opengl/filter_priv.h
+++ b/modules/video_output/opengl/filter_priv.h
@@ -33,6 +33,10 @@ struct vlc_gl_filter_priv {
     struct vlc_gl_tex_size size_out;
     struct vlc_gl_sampler *sampler; /* owned */
 
+    bool has_framebuffer_out;
+    GLuint framebuffer_out; /* owned (this filter must delete it) */
+    GLuint texture_out; /* owned (attached to framebuffer_out) */
+
     struct vlc_list node; /**< node of vlc_gl_filters.list */
 };
 
diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c
index 47c222f533..d6762b0b56 100644
--- a/modules/video_output/opengl/filters.c
+++ b/modules/video_output/opengl/filters.c
@@ -73,6 +73,41 @@ vlc_gl_filters_Delete(struct vlc_gl_filters *filters)
     free(filters);
 }
 
+static int
+InitFramebufferOut(struct vlc_gl_filter_priv *priv)
+{
+    assert(priv->size_out.width > 0 && priv->size_out.height > 0);
+
+    const opengl_vtable_t *vt = &priv->filter.api->vt;
+
+    /* Create a texture having the expected size */
+    vt->GenTextures(1, &priv->texture_out);
+    vt->BindTexture(GL_TEXTURE_2D, priv->texture_out);
+    vt->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, priv->size_out.width,
+                   priv->size_out.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    /* iOS needs GL_CLAMP_TO_EDGE or power-of-two textures */
+    vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    vt->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+    /* Create a framebuffer and attach the texture */
+    vt->GenFramebuffers(1, &priv->framebuffer_out);
+    vt->BindFramebuffer(GL_FRAMEBUFFER, priv->framebuffer_out);
+    vt->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                             GL_TEXTURE_2D, priv->texture_out, 0);
+
+    priv->has_framebuffer_out = true;
+
+    GLenum status = vt->CheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE)
+        return VLC_EGENERIC;
+
+    vt->BindFramebuffer(GL_FRAMEBUFFER, 0);
+    return VLC_SUCCESS;
+}
+
 struct vlc_gl_filter *
 vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
                       const config_chain_t *config)
@@ -127,6 +162,20 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
         return NULL;
     }
 
+    if (prev_filter)
+    {
+        /* It was the last filter before we append this one */
+        assert(!prev_filter->has_framebuffer_out);
+
+        /* Every non-last filter needs its own framebuffer */
+        ret = InitFramebufferOut(prev_filter);
+        if (ret != VLC_SUCCESS)
+        {
+            vlc_gl_filter_Delete(filter);
+            return NULL;
+        }
+    }
+
     vlc_list_append(&priv->node, &filters->list);
 
     return filter;
@@ -150,9 +199,32 @@ vlc_gl_filters_UpdatePicture(struct vlc_gl_filters *filters,
 int
 vlc_gl_filters_Draw(struct vlc_gl_filters *filters)
 {
+    const opengl_vtable_t *vt = &filters->api->vt;
+
     struct vlc_gl_filter_priv *priv;
     vlc_list_foreach(priv, &filters->list, node)
     {
+        struct vlc_gl_filter_priv *previous =
+            vlc_list_prev_entry_or_null(&filters->list, priv,
+                                        struct vlc_gl_filter_priv, node);
+        if (previous)
+        {
+            /* Read from the output of the previous filter */
+            int ret = vlc_gl_sampler_UpdateTexture(priv->sampler,
+                                                   previous->texture_out,
+                                                   previous->size_out.width,
+                                                   previous->size_out.height);
+            if (ret != VLC_SUCCESS)
+            {
+                msg_Err(filters->gl, "Could not update sampler texture");
+                return ret;
+            }
+        }
+
+        GLuint draw_fb = priv->has_framebuffer_out ? priv->framebuffer_out : 0;
+
+        vt->BindFramebuffer(GL_DRAW_FRAMEBUFFER, draw_fb);
+
         struct vlc_gl_filter *filter = &priv->filter;
         int ret = filter->ops->draw(filter);
         if (ret != VLC_SUCCESS)
-- 
2.27.0



More information about the vlc-devel mailing list