[vlc-devel] [PATCH 10/13] opengl: add support for plane samplers

Romain Vimont rom1v at videolabs.io
Fri Mar 5 13:34:47 UTC 2021


A sampler gives access to the input picture, in RGBA.

Add support for giving access to individual input planes, selected by
vlc_gl_sampler_SelectPlane(), in their native format.
---
 modules/video_output/opengl/filters.c      |   6 +-
 modules/video_output/opengl/sampler.c      | 156 ++++++++++++++++++++-
 modules/video_output/opengl/sampler_priv.h |  21 ++-
 3 files changed, 171 insertions(+), 12 deletions(-)

diff --git a/modules/video_output/opengl/filters.c b/modules/video_output/opengl/filters.c
index 493bfd5193..6795af3321 100644
--- a/modules/video_output/opengl/filters.c
+++ b/modules/video_output/opengl/filters.c
@@ -243,7 +243,7 @@ GetSampler(struct vlc_gl_filter *filter)
 
     struct vlc_gl_sampler *sampler;
     if (!priv->prev_filter)
-        sampler = vlc_gl_sampler_NewFromInterop(filters->interop);
+        sampler = vlc_gl_sampler_NewFromInterop(filters->interop, false);
     else
     {
         video_format_t fmt;
@@ -251,8 +251,8 @@ GetSampler(struct vlc_gl_filter *filter)
         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);
+        sampler = vlc_gl_sampler_NewFromTexture2D(filters->gl, filters->api,
+                                                  &fmt, false);
     }
 
     priv->sampler = sampler;
diff --git a/modules/video_output/opengl/sampler.c b/modules/video_output/opengl/sampler.c
index 39b37c2ca2..42550a5a24 100644
--- a/modules/video_output/opengl/sampler.c
+++ b/modules/video_output/opengl/sampler.c
@@ -97,6 +97,11 @@ struct vlc_gl_sampler_priv {
 
     /* Only used for "direct" sampler (when interop == NULL) */
     video_format_t direct_fmt;
+
+    /* If set, vlc_texture() exposes a single plane (without chroma
+     * conversion), selected by vlc_gl_sampler_SetCurrentPlane(). */
+    bool expose_planes;
+    unsigned plane;
 };
 
 #define PRIV(sampler) container_of(sampler, struct vlc_gl_sampler_priv, sampler)
@@ -752,13 +757,138 @@ InitShaderExtensions(struct vlc_gl_sampler *sampler, GLenum tex_target)
     return VLC_SUCCESS;
 }
 
+static void
+sampler_planes_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+
+    const opengl_vtable_t *vt = priv->vt;
+
+    priv->uloc.TransformMatrix =
+        vt->GetUniformLocation(program, "TransformMatrix");
+    assert(priv->uloc.TransformMatrix != -1);
+
+    priv->uloc.OrientationMatrix =
+        vt->GetUniformLocation(program, "OrientationMatrix");
+    assert(priv->uloc.OrientationMatrix != -1);
+
+    priv->uloc.Textures[0] = vt->GetUniformLocation(program, "Texture");
+    assert(priv->uloc.Textures[0] != -1);
+
+    priv->uloc.TexCoordsMaps[0] =
+        vt->GetUniformLocation(program, "TexCoordsMap");
+    assert(priv->uloc.TexCoordsMaps[0] != -1);
+
+    if (priv->tex_target == GL_TEXTURE_RECTANGLE)
+    {
+        priv->uloc.TexSizes[0] = vt->GetUniformLocation(program, "TexSize");
+        assert(priv->uloc.TexSizes[0] != -1);
+    }
+}
+
+static void
+sampler_planes_load(const struct vlc_gl_sampler *sampler)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    unsigned plane = priv->plane;
+
+    const opengl_vtable_t *vt = priv->vt;
+
+    vt->Uniform1i(priv->uloc.Textures[0], 0);
+
+    assert(priv->textures[plane] != 0);
+    vt->ActiveTexture(GL_TEXTURE0);
+    vt->BindTexture(priv->tex_target, priv->textures[plane]);
+
+    vt->UniformMatrix3fv(priv->uloc.TexCoordsMaps[0], 1, GL_FALSE,
+                         priv->var.TexCoordsMaps[0]);
+
+    /* Return the expected transform matrix if interop == NULL */
+    const GLfloat *tm = GetTransformMatrix(priv->interop);
+    vt->UniformMatrix4fv(priv->uloc.TransformMatrix, 1, GL_FALSE, tm);
+
+    vt->UniformMatrix4fv(priv->uloc.OrientationMatrix, 1, GL_FALSE,
+                         priv->var.OrientationMatrix);
+
+    if (priv->tex_target == GL_TEXTURE_RECTANGLE)
+    {
+        vt->Uniform2f(priv->uloc.TexSizes[0], priv->tex_widths[plane],
+                      priv->tex_heights[plane]);
+    }
+}
+
+static int
+sampler_planes_init(struct vlc_gl_sampler *sampler)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    GLenum tex_target = priv->tex_target;
+
+    struct vlc_memstream ms;
+    if (vlc_memstream_open(&ms))
+        return VLC_EGENERIC;
+
+#define ADD(x) vlc_memstream_puts(&ms, x)
+#define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
+
+    const char *sampler_type;
+    const char *texture_fn;
+    GetNames(tex_target, &sampler_type, &texture_fn);
+
+    ADDF("uniform %s Texture;\n", sampler_type);
+    ADD("uniform mat3 TexCoordsMap;\n"
+        "uniform mat4 TransformMatrix;\n"
+        "uniform mat4 OrientationMatrix;\n");
+
+    if (tex_target == GL_TEXTURE_RECTANGLE)
+        ADD("uniform vec2 TexSize;\n");
+
+    ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
+        /* Homogeneous (oriented) coordinates */
+        "  vec3 pic_hcoords = vec3((TransformMatrix * OrientationMatrix * vec4(pic_coords, 0.0, 1.0)).st, 1.0);\n"
+        "  vec2 tex_coords = (TexCoordsMap * pic_hcoords).st;\n");
+
+    if (tex_target == GL_TEXTURE_RECTANGLE)
+    {
+        /* The coordinates are in texels values, not normalized */
+        ADD(" tex_coords = vec2(tex_coords.x * TexSize.x,\n"
+            "                   tex_coords.y * TexSize.y);\n");
+    }
+
+    ADDF("  return %s(Texture, tex_coords);\n", texture_fn);
+    ADD("}\n");
+
+#undef ADD
+#undef ADDF
+
+    if (vlc_memstream_close(&ms) != 0)
+        return VLC_EGENERIC;
+
+    int ret = InitShaderExtensions(sampler, tex_target);
+    if (ret != VLC_SUCCESS)
+    {
+        free(ms.ptr);
+        return VLC_EGENERIC;
+    }
+    sampler->shader.body = ms.ptr;
+
+    static const struct vlc_gl_sampler_ops ops = {
+        .fetch_locations = sampler_planes_fetch_locations,
+        .load = sampler_planes_load,
+    };
+    sampler->ops = &ops;
+
+    return VLC_SUCCESS;
+}
+
 static int
 opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
-                            const video_format_t *fmt)
+                            const video_format_t *fmt, bool expose_planes)
 {
     struct vlc_gl_sampler_priv *priv = PRIV(sampler);
 
     priv->tex_target = tex_target;
+    priv->expose_planes = expose_planes;
+    priv->plane = 0;
 
     vlc_fourcc_t chroma = fmt->i_chroma;
     video_color_space_t yuv_space = fmt->space;
@@ -777,6 +907,9 @@ opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
 
     InitOrientationMatrix(priv->var.OrientationMatrix, orientation);
 
+    if (expose_planes)
+        return sampler_planes_init(sampler);
+
     if (chroma == VLC_CODEC_XYZ12)
         return xyz12_shader_init(sampler);
 
@@ -974,7 +1107,7 @@ opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
 static struct vlc_gl_sampler *
 CreateSampler(struct vlc_gl_interop *interop, struct vlc_gl_t *gl,
               const struct vlc_gl_api *api, const video_format_t *fmt,
-              unsigned tex_target)
+              unsigned tex_target, bool expose_planes)
 {
     struct vlc_gl_sampler_priv *priv = calloc(1, sizeof(*priv));
     if (!priv)
@@ -1028,7 +1161,8 @@ CreateSampler(struct vlc_gl_interop *interop, struct vlc_gl_t *gl,
     }
 #endif
 
-    int ret = opengl_fragment_shader_init(sampler, tex_target, fmt);
+    int ret = opengl_fragment_shader_init(sampler, tex_target, fmt,
+                                          expose_planes);
     if (ret != VLC_SUCCESS)
     {
         free(sampler);
@@ -1081,18 +1215,19 @@ CreateSampler(struct vlc_gl_interop *interop, struct vlc_gl_t *gl,
 }
 
 struct vlc_gl_sampler *
-vlc_gl_sampler_NewFromInterop(struct vlc_gl_interop *interop)
+vlc_gl_sampler_NewFromInterop(struct vlc_gl_interop *interop,
+                              bool expose_planes)
 {
     return CreateSampler(interop, interop->gl, interop->api, &interop->fmt_out,
-                         interop->tex_target);
+                         interop->tex_target, expose_planes);
 }
 
 struct vlc_gl_sampler *
 vlc_gl_sampler_NewFromTexture2D(struct vlc_gl_t *gl,
                                 const struct vlc_gl_api *api,
-                                const video_format_t *fmt)
+                                const video_format_t *fmt, bool expose_planes)
 {
-    return CreateSampler(NULL, gl, api, fmt, GL_TEXTURE_2D);
+    return CreateSampler(NULL, gl, api, fmt, GL_TEXTURE_2D, expose_planes);
 }
 
 void
@@ -1228,3 +1363,10 @@ vlc_gl_sampler_UpdateTexture(struct vlc_gl_sampler *sampler, GLuint texture,
 
     return VLC_SUCCESS;
 }
+
+void
+vlc_gl_sampler_SelectPlane(struct vlc_gl_sampler *sampler, unsigned plane)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    priv->plane = plane;
+}
diff --git a/modules/video_output/opengl/sampler_priv.h b/modules/video_output/opengl/sampler_priv.h
index e6c977d467..0fcb99e9c3 100644
--- a/modules/video_output/opengl/sampler_priv.h
+++ b/modules/video_output/opengl/sampler_priv.h
@@ -35,9 +35,12 @@ struct vlc_gl_interop;
  * uploaded them to OpenGL textures.
  *
  * \param interop the interop
+ * \param expose_planes if set, vlc_texture() exposes a single plane at a time
+ *                      (selected by vlc_gl_sampler_SetCurrentPlane())
  */
 struct vlc_gl_sampler *
-vlc_gl_sampler_NewFromInterop(struct vlc_gl_interop *interop);
+vlc_gl_sampler_NewFromInterop(struct vlc_gl_interop *interop,
+                              bool expose_planes);
 
 /**
  * Create a new direct sampler
@@ -48,11 +51,13 @@ vlc_gl_sampler_NewFromInterop(struct vlc_gl_interop *interop);
  * \param gl the OpenGL context
  * \param api the OpenGL API
  * \param fmt the input format
+ * \param expose_planes if set, vlc_texture() exposes a single plane at a time
+ *                      (selected by vlc_gl_sampler_SetCurrentPlane())
  */
 struct vlc_gl_sampler *
 vlc_gl_sampler_NewFromTexture2D(struct vlc_gl_t *gl,
                                 const struct vlc_gl_api *api,
-                                const video_format_t *fmt);
+                                const video_format_t *fmt, bool expose_planes);
 
 /**
  * Delete a sampler
@@ -90,4 +95,16 @@ int
 vlc_gl_sampler_UpdateTexture(struct vlc_gl_sampler *sampler, GLuint texture,
                              GLsizei tex_width, GLsizei tex_height);
 
+/**
+ * Select the plane to expose
+ *
+ * If the sampler exposes planes separately (for plane filters), select the
+ * plane to expose via the GLSL function vlc_texture().
+ *
+ * \param sampler the sampler
+ * \param plane the plane number
+ */
+void
+vlc_gl_sampler_SelectPlane(struct vlc_gl_sampler *sampler, unsigned plane);
+
 #endif
-- 
2.30.1



More information about the vlc-devel mailing list