[vlc-devel] [PATCH v2 15/22] opengl: lazy-load sampler

Romain Vimont rom1v at videolabs.io
Wed Jul 1 12:30:16 CEST 2020


The sampler was created and passed to the filter in the Open() function.

But only non-blend filters need a sampler to read the input picture
(blend filters do not read it).

To avoid creating an unnecessary sampler (including GLSL code
generation), retrieve the sampler from an owner function, which creates
it on-demand.

Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>
---
 modules/video_output/opengl/filter.c      | 10 ++--
 modules/video_output/opengl/filter.h      | 26 +++++++-
 modules/video_output/opengl/filter_draw.c |  9 ++-
 modules/video_output/opengl/filter_priv.h | 15 +++--
 modules/video_output/opengl/filters.c     | 72 ++++++++++++++++++-----
 modules/video_output/opengl/renderer.c    |  9 +--
 6 files changed, 105 insertions(+), 36 deletions(-)

diff --git a/modules/video_output/opengl/filter.c b/modules/video_output/opengl/filter.c
index 91b2627d6f..208ccc12cb 100644
--- a/modules/video_output/opengl/filter.c
+++ b/modules/video_output/opengl/filter.c
@@ -67,9 +67,8 @@ ActivateGLFilter(void *func, bool forced, va_list args)
     struct vlc_gl_filter *filter = va_arg(args, struct vlc_gl_filter *);
     const config_chain_t *config = va_arg(args, config_chain_t *);
     struct vlc_gl_tex_size *size_out = va_arg(args, struct vlc_gl_tex_size *);
-    struct vlc_gl_sampler *sampler = va_arg(args, struct vlc_gl_sampler *);
 
-    return activate(filter, config, size_out, sampler);
+    return activate(filter, config, size_out);
 }
 
 #undef vlc_gl_filter_LoadModule
@@ -77,12 +76,11 @@ int
 vlc_gl_filter_LoadModule(vlc_object_t *parent, const char *name,
                          struct vlc_gl_filter *filter,
                          const config_chain_t *config,
-                         struct vlc_gl_tex_size *size_out,
-                         struct vlc_gl_sampler *sampler)
+                         struct vlc_gl_tex_size *size_out)
 {
     filter->module = vlc_module_load(parent, "opengl filter", name, true,
-                                     ActivateGLFilter, filter, config, size_out,
-                                     sampler);
+                                     ActivateGLFilter, filter, config,
+                                     size_out);
     if (!filter->module)
         return VLC_EGENERIC;
 
diff --git a/modules/video_output/opengl/filter.h b/modules/video_output/opengl/filter.h
index 41bf5b133f..94c9fc3320 100644
--- a/modules/video_output/opengl/filter.h
+++ b/modules/video_output/opengl/filter.h
@@ -34,8 +34,7 @@ struct vlc_gl_tex_size {
 typedef int
 vlc_gl_filter_open_fn(struct vlc_gl_filter *filter,
                       const config_chain_t *config,
-                      struct vlc_gl_tex_size *size_out,
-                      struct vlc_gl_sampler *sampler);
+                      struct vlc_gl_tex_size *size_out);
 
 struct vlc_gl_filter_ops {
     /**
@@ -49,6 +48,21 @@ struct vlc_gl_filter_ops {
     void (*close)(struct vlc_gl_filter *filter);
 };
 
+struct vlc_gl_filter_owner_ops {
+    /**
+     * Get the sampler associated to this filter.
+     *
+     * The instance is lazy-loaded (to avoid creating one for blend filters).
+     * Successive calls to this function for the same filter is guaranteed to
+     * always return the same sampler.
+     *
+     * \param filter the filter
+     * \return sampler the sampler, NULL on error
+     */
+    struct vlc_gl_sampler *
+    (*get_sampler)(struct vlc_gl_filter *filter);
+};
+
 /**
  * OpenGL filter, in charge of a rendering pass.
  */
@@ -69,6 +83,14 @@ struct vlc_gl_filter {
 
     const struct vlc_gl_filter_ops *ops;
     void *sys;
+
+    const struct vlc_gl_filter_owner_ops *owner_ops;
 };
 
+static inline struct vlc_gl_sampler *
+vlc_gl_filter_GetSampler(struct vlc_gl_filter *filter)
+{
+    return filter->owner_ops->get_sampler(filter);
+}
+
 #endif
diff --git a/modules/video_output/opengl/filter_draw.c b/modules/video_output/opengl/filter_draw.c
index 9f92b074df..d5b969e24b 100644
--- a/modules/video_output/opengl/filter_draw.c
+++ b/modules/video_output/opengl/filter_draw.c
@@ -37,7 +37,6 @@
 
 struct sys {
     GLuint program_id;
-    struct vlc_gl_sampler *sampler;
 
     GLuint vbo;
 
@@ -55,7 +54,8 @@ Draw(struct vlc_gl_filter *filter)
 
     vt->UseProgram(sys->program_id);
 
-    vlc_gl_sampler_Load(sys->sampler);
+    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
+    vlc_gl_sampler_Load(sampler);
 
     vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
     vt->EnableVertexAttribArray(sys->loc.vertex_pos);
@@ -83,8 +83,7 @@ Close(struct vlc_gl_filter *filter)
 int
 vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
                         const config_chain_t *config,
-                        struct vlc_gl_tex_size *size_out,
-                        struct vlc_gl_sampler *sampler)
+                        struct vlc_gl_tex_size *size_out)
 {
     (void) config;
     (void) size_out;
@@ -93,7 +92,7 @@ vlc_gl_filter_draw_Open(struct vlc_gl_filter *filter,
     if (!sys)
         return VLC_EGENERIC;
 
-    sys->sampler = sampler;
+    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
 
 #ifdef USE_OPENGL_ES2
 # define SHADER_VERSION "#version 100\n"
diff --git a/modules/video_output/opengl/filter_priv.h b/modules/video_output/opengl/filter_priv.h
index 509a74ec64..de1c4cd319 100644
--- a/modules/video_output/opengl/filter_priv.h
+++ b/modules/video_output/opengl/filter_priv.h
@@ -43,6 +43,14 @@ struct vlc_gl_filter_priv {
     GLuint texture_out; /* owned (attached to framebuffer_out) */
     /* } */
 
+    /* For lazy-loading sampler */
+    struct vlc_gl_filters *filters; /* weak reference to the container */
+
+    /* Previous filter to construct the expected sampler. It is necessary
+     * because owner_ops->get_sampler() may be called during the Open(), while
+     * the filter is not added to the filter chain yet. */
+    struct vlc_gl_filter_priv *prev_filter;
+
     struct vlc_list node; /**< node of vlc_gl_filters.list */
 
     /* Blend filters are attached to their non-blend "parent" instead of the
@@ -61,10 +69,9 @@ int
 vlc_gl_filter_LoadModule(vlc_object_t *parent, const char *name,
                          struct vlc_gl_filter *filter,
                          const config_chain_t *config,
-                         struct vlc_gl_tex_size *size_out,
-                         struct vlc_gl_sampler *sampler);
-#define vlc_gl_filter_LoadModule(o, a, b, c, d, e) \
-    vlc_gl_filter_LoadModule(VLC_OBJECT(o), a, b, c, d, e)
+                         struct vlc_gl_tex_size *size_out);
+#define vlc_gl_filter_LoadModule(o, a, b, c, d) \
+    vlc_gl_filter_LoadModule(VLC_OBJECT(o), a, b, c, d)
 
 void
 vlc_gl_filter_Delete(struct vlc_gl_filter *filter);
diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c
index 6a8ba0ec4c..4e5dd9a1ae 100644
--- a/modules/video_output/opengl/filters.c
+++ b/modules/video_output/opengl/filters.c
@@ -108,6 +108,36 @@ InitFramebufferOut(struct vlc_gl_filter_priv *priv)
     return VLC_SUCCESS;
 }
 
+static struct vlc_gl_sampler *
+GetSampler(struct vlc_gl_filter *filter)
+{
+    struct vlc_gl_filter_priv *priv = vlc_gl_filter_PRIV(filter);
+    if (priv->sampler)
+        /* already initialized */
+        return priv->sampler;
+
+    struct vlc_gl_filters *filters = priv->filters;
+    struct vlc_gl_filter_priv *prev_filter = priv->prev_filter;
+
+    struct vlc_gl_sampler *sampler;
+    if (!priv->prev_filter)
+        sampler = vlc_gl_sampler_NewFromInterop(filters->interop);
+    else
+    {
+        video_format_t fmt;
+        video_format_Init(&fmt, VLC_CODEC_RGBA);
+        fmt.i_width = fmt.i_visible_width = prev_filter->size_out.width;
+        fmt.i_height = fmt.i_visible_height = prev_filter->size_out.height;
+
+        sampler =
+            vlc_gl_sampler_NewFromTexture2D(filters->gl, filters->api, &fmt);
+    }
+
+    priv->sampler = sampler;
+
+    return sampler;
+}
+
 struct vlc_gl_filter *
 vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
                       const config_chain_t *config)
@@ -127,33 +157,26 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
     {
         size_in.width = filters->interop->fmt_out.i_visible_width;
         size_in.height = filters->interop->fmt_out.i_visible_height;
-        priv->sampler = vlc_gl_sampler_NewFromInterop(filters->interop);
     }
     else
     {
         size_in = prev_filter->size_out;
-
-        video_format_t fmt;
-        video_format_Init(&fmt, VLC_CODEC_RGBA);
-        fmt.i_width = fmt.i_visible_width = size_in.width;
-        fmt.i_height = fmt.i_visible_height = size_in.height;
-
-        priv->sampler =
-            vlc_gl_sampler_NewFromTexture2D(filters->gl, filters->api, &fmt);
     }
 
-    if (!priv->sampler)
-    {
-        vlc_gl_filter_Delete(filter);
-        return NULL;
-    }
+    priv->filters = filters;
+    priv->prev_filter = prev_filter;
+
+    static const struct vlc_gl_filter_owner_ops owner_ops = {
+        .get_sampler = GetSampler,
+    };
+    filter->owner_ops = &owner_ops;
 
     /* By default, the output size is the same as the input size. The filter
      * may change it during its Open(). */
     priv->size_out = size_in;
 
     int ret = vlc_gl_filter_LoadModule(filters->gl, name, filter, config,
-                                       &priv->size_out, priv->sampler);
+                                       &priv->size_out);
     if (ret != VLC_SUCCESS)
     {
         /* Creation failed, do not call close() */
@@ -167,6 +190,15 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
            || (priv->size_out.width == size_in.width
             && priv->size_out.height == size_in.height));
 
+    /* A blend filter may not read its input, so it is an error if a sampler
+     * has been requested.
+     *
+     * We assert it here instead of in vlc_gl_filter_GetSampler() because the
+     * filter implementation may set the "blend" flag after it get the sampler
+     * in its Open() function.
+     */
+    assert(!filter->config.blend || !priv->sampler);
+
     if (filter->config.blend && !prev_filter)
     {
         /* We cannot blend with nothing, so insert a "draw" filter to draw the
@@ -204,8 +236,18 @@ vlc_gl_filters_Append(struct vlc_gl_filters *filters, const char *name,
         vlc_list_append(&priv->node, &last_filter->blend_subfilters);
     }
     else
+    {
+        /* Make sure the sampler of non-blend filters is initialized */
+        struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
+        if (!sampler)
+        {
+            vlc_gl_filter_Delete(filter);
+            return NULL;
+        }
+
         /* Append to the main filter list */
         vlc_list_append(&priv->node, &filters->list);
+    }
 
     return filter;
 }
diff --git a/modules/video_output/opengl/renderer.c b/modules/video_output/opengl/renderer.c
index a579163779..10b79e7742 100644
--- a/modules/video_output/opengl/renderer.c
+++ b/modules/video_output/opengl/renderer.c
@@ -333,14 +333,12 @@ Draw(struct vlc_gl_filter *filter);
 int
 vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
                      const config_chain_t *config,
-                     struct vlc_gl_tex_size *size_out,
-                     struct vlc_gl_sampler *sampler)
+                     struct vlc_gl_tex_size *size_out)
 {
     (void) config;
     (void) size_out;
 
     const opengl_vtable_t *vt = &filter->api->vt;
-    const video_format_t *fmt = &sampler->fmt;
 
     struct vlc_gl_renderer *renderer = calloc(1, sizeof(*renderer));
     if (!renderer)
@@ -353,6 +351,7 @@ vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
     filter->ops = &filter_ops;
     filter->sys = renderer;
 
+    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
     renderer->sampler = sampler;
 
     renderer->api = filter->api;
@@ -366,6 +365,7 @@ vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
         return ret;
     }
 
+    const video_format_t *fmt = &sampler->fmt;
     InitStereoMatrix(renderer->var.StereoMatrix, fmt->multiview_mode);
 
     getViewpointMatrixes(renderer, fmt->projection_mode);
@@ -766,7 +766,8 @@ Draw(struct vlc_gl_filter *filter)
 
     vt->UseProgram(renderer->program_id);
 
-    vlc_gl_sampler_Load(renderer->sampler);
+    struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
+    vlc_gl_sampler_Load(sampler);
 
     vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
     assert(renderer->aloc.PicCoordsIn != -1);
-- 
2.27.0



More information about the vlc-devel mailing list