[vlc-commits] [Git][videolan/vlc][master] 8 commits: opengl: expose API to transform coords in CPU

Hugo Beauzée-Luyssen (@chouquette) gitlab at videolan.org
Thu Oct 7 15:03:02 UTC 2021



Hugo Beauzée-Luyssen pushed to branch master at VideoLAN / VLC


Commits:
31c20009 by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: expose API to transform coords in CPU

All coordinates transforms were applied from the fragment shader
generated by the sampler. This simplified the API a lot: the filters
could just let the generated sampler code manage all the transforms.

However, this is wasteful, since all these transforms are linear.

Instead, expose a new function to let the filters transform their
picture coordinates to texture coordinates, before passing them to their
vertex shader.

The filters will be adapted in the following commits.

- - - - -
01db27e1 by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: replace 3x3 matrices by 3x2 matrices

The transform matrices were square for wider compatibility with OpenGL
versions. Now that they are computed on CPU, we can use 3x2 matrices.

- - - - -
5d958bf3 by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: compute coords on CPU in renderer

- - - - -
30f88d4d by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: compute coords on CPU in filter_draw

- - - - -
98ef71b2 by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: compute coords from vertex shader in mock

Contrary to other filters, the 'mock{mask}' picture coordinates depend
on rotated vertex coordinates, computed from the vertex shader.

As a consequence, the texture coordinates could not be computed on CPU,
so compute them from the vertex shader.

- - - - -
43f0a884 by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: compute coords on CPU in mock-plane

- - - - -
a3f87b8b by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: expose direction matrix

Expose a 2x2 direction matrix to let shaders orient picture vectors.

For example, one pixel to the top in picture coordinates might result in
one pixel to the right expressed in texture coordinates.

- - - - -
b3527deb by Romain Vimont at 2021-10-07T14:49:07+00:00
opengl: compute coords on GPU in glblend

- - - - -


10 changed files:

- modules/video_filter/deinterlace/glblend.c
- modules/video_output/opengl/filter_draw.c
- modules/video_output/opengl/filter_mock.c
- modules/video_output/opengl/gl_util.h
- modules/video_output/opengl/interop.h
- modules/video_output/opengl/interop_android.c
- modules/video_output/opengl/renderer.c
- modules/video_output/opengl/renderer.h
- modules/video_output/opengl/sampler.c
- modules/video_output/opengl/sampler.h


Changes:

=====================================
modules/video_filter/deinterlace/glblend.c
=====================================
@@ -41,8 +41,11 @@ struct sys {
 
     struct {
         GLint vertex_pos;
-        GLint height;
+        GLint tex_coords_in;
+        GLint one_pixel_up;
     } loc;
+
+    float up_vector[2];
 };
 
 static int
@@ -58,12 +61,55 @@ Draw(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
     vlc_gl_sampler_Load(sampler);
 
     vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
+
+    if (vlc_gl_sampler_MustRecomputeCoords(sampler))
+    {
+        float coords[] = {
+            0, 1,
+            0, 0,
+            1, 1,
+            1, 0,
+        };
+
+        /* Transform coordinates in place */
+        vlc_gl_sampler_PicToTexCoords(sampler, 4, coords, coords);
+
+        const float data[] = {
+            -1,  1, coords[0], coords[1],
+            -1, -1, coords[2], coords[3],
+             1,  1, coords[4], coords[5],
+             1, -1, coords[6], coords[7],
+        };
+        vt->BufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
+
+        /* Compute the (normalized) vector representing the _up_ direction in
+         * texture coordinates, to take any orientation/flip into account. */
+        float direction[2*2];
+        vlc_gl_sampler_ComputeDirectionMatrix(sampler, direction);
+        sys->up_vector[0] = direction[2];
+        sys->up_vector[1] = direction[3];
+    }
+
+    const GLsizei stride = 4 * sizeof(float);
+
     vt->EnableVertexAttribArray(sys->loc.vertex_pos);
-    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, 0,
+    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, stride,
                             (const void *) 0);
 
+    intptr_t offset = 2 * sizeof(float);
+    vt->EnableVertexAttribArray(sys->loc.tex_coords_in);
+    vt->VertexAttribPointer(sys->loc.tex_coords_in, 2, GL_FLOAT, GL_FALSE,
+                            stride, (const void *) offset);
+
+    /* If the direction matrix contains a 90° rotation, then the unit vector
+     * should be divided by width rather than by height. Since up_vector is
+     * always a unit vector with one of its components equal to 0, then we can
+     * always devide the horizontal component by width and the vertical
+     * component by height. */
+    GLsizei width = sampler->tex_widths[meta->plane];
     GLsizei height = sampler->tex_heights[meta->plane];
-    vt->Uniform1f(sys->loc.height, height);
+    vt->Uniform2f(sys->loc.one_pixel_up, sys->up_vector[0] / width,
+                                         sys->up_vector[1] / height);
 
     vt->Clear(GL_COLOR_BUFFER_BIT);
     vt->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@@ -107,15 +153,14 @@ Open(struct vlc_gl_filter *filter, const config_chain_t *config,
 
     static const char *const VERTEX_SHADER =
         "attribute vec2 vertex_pos;\n"
+        "attribute vec2 tex_coords_in;\n"
         "varying vec2 tex_coords;\n"
         "varying vec2 tex_coords_up;\n"
-        "uniform float height;\n"
+        "uniform vec2 one_pixel_up;\n"
         "void main() {\n"
         "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
-        "  tex_coords = vec2((vertex_pos.x + 1.0) / 2.0,\n"
-        "                    (vertex_pos.y + 1.0) / 2.0);\n"
-        "  tex_coords_up = vec2(tex_coords.x,\n"
-        "                       tex_coords.y + 1.0 / height);\n"
+        "  tex_coords = tex_coords_in;\n"
+        "  tex_coords_up = tex_coords_in + one_pixel_up;\n"
         "}\n";
 
     static const char *const FRAGMENT_SHADER =
@@ -169,23 +214,14 @@ Open(struct vlc_gl_filter *filter, const config_chain_t *config,
     sys->loc.vertex_pos = vt->GetAttribLocation(program_id, "vertex_pos");
     assert(sys->loc.vertex_pos != -1);
 
-    sys->loc.height = vt->GetUniformLocation(program_id, "height");
-    assert(sys->loc.height != -1);
+    sys->loc.tex_coords_in = vt->GetAttribLocation(program_id, "tex_coords_in");
+    assert(sys->loc.tex_coords_in != -1);
 
-    vt->GenBuffers(1, &sys->vbo);
-
-    static const GLfloat vertex_pos[] = {
-        -1,  1,
-        -1, -1,
-         1,  1,
-         1, -1,
-    };
+    sys->loc.one_pixel_up = vt->GetUniformLocation(program_id,
+                                                   "one_pixel_up");
+    assert(sys->loc.one_pixel_up != -1);
 
-    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
-    vt->BufferData(GL_ARRAY_BUFFER, sizeof(vertex_pos), vertex_pos,
-                   GL_STATIC_DRAW);
-
-    vt->BindBuffer(GL_ARRAY_BUFFER, 0);
+    vt->GenBuffers(1, &sys->vbo);
 
     return VLC_SUCCESS;
 


=====================================
modules/video_output/opengl/filter_draw.c
=====================================
@@ -48,7 +48,10 @@ struct sys {
 
     struct {
         GLint vertex_pos;
+        GLint tex_coords_in;
     } loc;
+
+    bool vflip;
 };
 
 static int
@@ -66,10 +69,39 @@ Draw(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
     vlc_gl_sampler_Load(sampler);
 
     vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
+
+    if (vlc_gl_sampler_MustRecomputeCoords(sampler))
+    {
+        float coords[] = {
+            0, sys->vflip ? 0 : 1,
+            0, sys->vflip ? 1 : 0,
+            1, sys->vflip ? 0 : 1,
+            1, sys->vflip ? 1 : 0,
+        };
+
+        /* Transform coordinates in place */
+        vlc_gl_sampler_PicToTexCoords(sampler, 4, coords, coords);
+
+        const float data[] = {
+            -1,  1, coords[0], coords[1],
+            -1, -1, coords[2], coords[3],
+             1,  1, coords[4], coords[5],
+             1, -1, coords[6], coords[7],
+        };
+        vt->BufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
+    }
+
+    const GLsizei stride = 4 * sizeof(float);
+
     vt->EnableVertexAttribArray(sys->loc.vertex_pos);
-    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, 0,
+    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, stride,
                             (const void *) 0);
 
+    intptr_t offset = 2 * sizeof(float);
+    vt->EnableVertexAttribArray(sys->loc.tex_coords_in);
+    vt->VertexAttribPointer(sys->loc.tex_coords_in, 2, GL_FLOAT, GL_FALSE,
+                            stride, (const void *) offset);
+
     vt->Clear(GL_COLOR_BUFFER_BIT);
     vt->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
@@ -104,20 +136,11 @@ Open(struct vlc_gl_filter *filter, const config_chain_t *config,
 
     static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
+        "attribute vec2 tex_coords_in;\n"
         "varying vec2 tex_coords;\n"
         "void main() {\n"
         "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
-        "  tex_coords = vec2((vertex_pos.x + 1.0) / 2.0,\n"
-        "                    (vertex_pos.y + 1.0) / 2.0);\n"
-        "}\n";
-
-    static const char *const VERTEX_SHADER_BODY_VFLIP =
-        "attribute vec2 vertex_pos;\n"
-        "varying vec2 tex_coords;\n"
-        "void main() {\n"
-        "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
-        "  tex_coords = vec2((vertex_pos.x + 1.0) / 2.0,\n"
-        "                    (-vertex_pos.y + 1.0) / 2.0);\n"
+        "  tex_coords = tex_coords_in;\n"
         "}\n";
 
     static const char *const FRAGMENT_SHADER_BODY =
@@ -145,11 +168,11 @@ Open(struct vlc_gl_filter *filter, const config_chain_t *config,
     }
 
     config_ChainParse(filter, DRAW_CFG_PREFIX, filter_options, config);
-    bool vflip = var_InheritBool(filter, DRAW_CFG_PREFIX "vflip");
+    sys->vflip = var_InheritBool(filter, DRAW_CFG_PREFIX "vflip");
 
     const char *vertex_shader[] = {
         shader_version,
-        vflip ? VERTEX_SHADER_BODY_VFLIP : VERTEX_SHADER_BODY,
+        VERTEX_SHADER_BODY,
     };
     const char *fragment_shader[] = {
         shader_version,
@@ -173,20 +196,10 @@ Open(struct vlc_gl_filter *filter, const config_chain_t *config,
     sys->loc.vertex_pos = vt->GetAttribLocation(program_id, "vertex_pos");
     assert(sys->loc.vertex_pos != -1);
 
-    vt->GenBuffers(1, &sys->vbo);
+    sys->loc.tex_coords_in = vt->GetAttribLocation(program_id, "tex_coords_in");
+    assert(sys->loc.tex_coords_in != -1);
 
-    static const GLfloat vertex_pos[] = {
-        -1,  1,
-        -1, -1,
-         1,  1,
-         1, -1,
-    };
-
-    vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
-    vt->BufferData(GL_ARRAY_BUFFER, sizeof(vertex_pos), vertex_pos,
-                   GL_STATIC_DRAW);
-
-    vt->BindBuffer(GL_ARRAY_BUFFER, 0);
+    vt->GenBuffers(1, &sys->vbo);
 
     static const struct vlc_gl_filter_ops ops = {
         .draw = Draw,


=====================================
modules/video_output/opengl/filter_mock.c
=====================================
@@ -87,7 +87,9 @@ struct sys {
     struct {
         GLint vertex_pos;
         GLint rotation_matrix;
+        GLint pic_to_tex; // mask only
         GLint vertex_color; // blend (non-mask) only
+        GLint tex_coords_in; // plane only
         GLint offset;
     } loc;
 
@@ -184,6 +186,16 @@ DrawMask(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
     vt->UniformMatrix4fv(sys->loc.rotation_matrix, 1, GL_FALSE,
                          sys->rotation_matrix);
 
+    const float *mtx = sampler->pic_to_tex_matrix;
+    assert(mtx);
+
+    /* Expand the 3x2 matrix to 3x3 to store it in a mat3 uniform (for better
+     * compatibility). Both are in column-major order. */
+    float pic_to_tex[] = { mtx[0], mtx[1], 0,
+                           mtx[2], mtx[3], 0,
+                           mtx[4], mtx[5], 1 };
+    vt->UniformMatrix3fv(sys->loc.pic_to_tex, 1, GL_FALSE, pic_to_tex);
+
     vt->Clear(GL_COLOR_BUFFER_BIT);
     vt->DrawArrays(GL_TRIANGLES, 0, 3);
 
@@ -205,10 +217,39 @@ DrawPlane(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
     vt->Uniform1f(sys->loc.offset, 0.02 * meta->plane);
 
     vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
+
+    if (vlc_gl_sampler_MustRecomputeCoords(sampler))
+    {
+        float coords[] = {
+            0, 1,
+            0, 0,
+            1, 1,
+            1, 0,
+        };
+
+        /* Transform coordinates in place */
+        vlc_gl_sampler_PicToTexCoords(sampler, 4, coords, coords);
+
+        const float data[] = {
+            -1,  1, coords[0], coords[1],
+            -1, -1, coords[2], coords[3],
+             1,  1, coords[4], coords[5],
+             1, -1, coords[6], coords[7],
+        };
+        vt->BufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
+    }
+
+    const GLsizei stride = 4 * sizeof(float);
+
     vt->EnableVertexAttribArray(sys->loc.vertex_pos);
-    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, 0,
+    vt->VertexAttribPointer(sys->loc.vertex_pos, 2, GL_FLOAT, GL_FALSE, stride,
                             (const void *) 0);
 
+    intptr_t offset = 2 * sizeof(float);
+    vt->EnableVertexAttribArray(sys->loc.tex_coords_in);
+    vt->VertexAttribPointer(sys->loc.tex_coords_in, 2, GL_FLOAT, GL_FALSE,
+                            stride, (const void *) offset);
+
     vt->Clear(GL_COLOR_BUFFER_BIT);
     vt->DrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
@@ -329,11 +370,12 @@ InitMask(struct vlc_gl_filter *filter)
     static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
         "uniform mat4 rotation_matrix;\n"
+        "uniform mat3 pix_to_tex;\n"
         "varying vec2 tex_coords;\n"
         "void main() {\n"
         "  vec4 pos = rotation_matrix * vec4(vertex_pos, 0.0, 1.0);\n"
-        "  tex_coords = vec2((pos.x + 1.0) / 2.0,\n"
-        "                    (pos.y + 1.0) / 2.0);\n"
+        "  vec2 pic_coords = (pos.xy + vec2(1.0)) / 2.0;\n"
+        "  tex_coords = (pix_to_tex * vec3(pic_coords, 1.0)).xy;\n"
         "  gl_Position = pos\n;"
         "}\n";
 
@@ -389,6 +431,9 @@ InitMask(struct vlc_gl_filter *filter)
                                                       "rotation_matrix");
     assert(sys->loc.rotation_matrix != -1);
 
+    sys->loc.pic_to_tex = vt->GetUniformLocation(sys->program_id, "pix_to_tex");
+    assert(sys->loc.pic_to_tex != -1);
+
     vt->GenBuffers(1, &sys->vbo);
 
     static const GLfloat data[] = {
@@ -428,11 +473,12 @@ InitPlane(struct vlc_gl_filter *filter)
 
     static const char *const VERTEX_SHADER_BODY =
         "attribute vec2 vertex_pos;\n"
+        "attribute vec2 tex_coords_in;\n"
         "varying vec2 tex_coords;\n"
         "uniform float offset;\n"
         "void main() {\n"
         "  gl_Position = vec4(vertex_pos, 0.0, 1.0);\n"
-        "  tex_coords = (vertex_pos + vec2(1.0)) / 2.0 + offset;\n"
+        "  tex_coords = tex_coords_in + offset;\n"
         "}\n";
 
     static const char *const FRAGMENT_SHADER_BODY =
@@ -483,21 +529,14 @@ InitPlane(struct vlc_gl_filter *filter)
     sys->loc.vertex_pos = vt->GetAttribLocation(sys->program_id, "vertex_pos");
     assert(sys->loc.vertex_pos != -1);
 
+    sys->loc.tex_coords_in = vt->GetAttribLocation(sys->program_id,
+                                                   "tex_coords_in");
+    assert(sys->loc.tex_coords_in != -1);
+
     sys->loc.offset = vt->GetUniformLocation(program_id, "offset");
     assert(sys->loc.offset != -1);
 
-    static const GLfloat vertex_pos[] = {
-        -1,  1,
-        -1, -1,
-         1,  1,
-         1, -1,
-    };
-
     vt->BindBuffer(GL_ARRAY_BUFFER, sys->vbo);
-    vt->BufferData(GL_ARRAY_BUFFER, sizeof(vertex_pos), vertex_pos,
-                   GL_STATIC_DRAW);
-
-    vt->BindBuffer(GL_ARRAY_BUFFER, 0);
 
     static const struct vlc_gl_filter_ops ops = {
         .draw = DrawPlane,


=====================================
modules/video_output/opengl/gl_util.h
=====================================
@@ -42,6 +42,13 @@ static const float MATRIX3_IDENTITY[3*3] = {
     0, 0, 1,
 };
 
+/* In column-major order */
+static const float MATRIX3x2_IDENTITY[3*2] = {
+    1, 0,
+    0, 1,
+    0, 0,
+};
+
 /** Return the smallest larger or equal power of 2 */
 static inline unsigned vlc_align_pot(unsigned x)
 {


=====================================
modules/video_output/opengl/interop.h
=====================================
@@ -76,7 +76,7 @@ struct vlc_gl_interop_ops {
      *
      * This function pointer can be NULL. If it is set, it may return NULL.
      *
-     * Otherwise, it must return a 3x3 matrix, as an array of 9 floats in
+     * Otherwise, it must return a 3x2 matrix, as an array of 6 floats in
      * column-major order.
      *
      * This transform matrix maps 2D homogeneous texture coordinates of the
@@ -88,7 +88,7 @@ struct vlc_gl_interop_ops {
      * freed before the module is closed.
      *
      * \param interop the OpenGL interop
-     * \return a 3x3 transformation matrix (possibly NULL)
+     * \return a 3x2 transformation matrix (possibly NULL)
      */
     const float *
     (*get_transform_matrix)(const struct vlc_gl_interop *interop);


=====================================
modules/video_output/opengl/interop_android.c
=====================================
@@ -34,7 +34,7 @@
 
 struct priv
 {
-    float mtx_3x3[3*3];
+    float mtx_3x2[3*2];
     const float *transform_mtx;
 
     bool stex_attached;
@@ -43,7 +43,7 @@ struct priv
 };
 
 static void
-ReductMatrix(float *mtx_3x3, const float *mtx_4x4)
+ReductMatrix(float *mtx_3x2, const float *mtx_4x4)
 {
     /*
      * The transform matrix provided by Android is 4x4:
@@ -53,25 +53,22 @@ ReductMatrix(float *mtx_3x3, const float *mtx_4x4)
      * the form (s, t, 0, 1). Similarly, the third row is never used either,
      * since only the two first coordinates of the output vector are kept.
      *
-     *       mat_4x4        mat_3x3
+     *       mat_4x4        mat_3x2
      *
-     *     / a b . c \     / a b c \
-     *     | d e . f | --> | d e f |
-     *     | . . . . |     \ g h i /
-     *     \ g h . i /
+     *     / a b . c \
+     *     | d e . f | --> / a b c \
+     *     | . . . . |     \ d e f /
+     *     \ . . . . /
      */
 
 #define MTX4(COL,ROW) mtx_4x4[(COL)*4 + (ROW)]
-#define MTX3(COL,ROW) mtx_3x3[(COL)*3 + (ROW)]
+#define MTX3(COL,ROW) mtx_3x2[(COL)*2 + (ROW)]
     MTX3(0,0) = MTX4(0,0); // a
     MTX3(1,0) = MTX4(1,0); // b
     MTX3(2,0) = MTX4(3,0); // c
     MTX3(0,1) = MTX4(0,1); // d
     MTX3(1,1) = MTX4(1,1); // e
     MTX3(2,1) = MTX4(3,1); // f
-    MTX3(0,2) = MTX4(0,3); // g
-    MTX3(1,2) = MTX4(1,3); // h
-    MTX3(2,2) = MTX4(3,3); // i
 #undef MTX4
 #undef MTX3
 }
@@ -147,8 +144,8 @@ tc_anop_update(struct vlc_gl_interop *interop, GLuint *textures,
         goto error;
     }
 
-    ReductMatrix(priv->mtx_3x3, mtx_4x4);
-    priv->transform_mtx = priv->mtx_3x3;
+    ReductMatrix(priv->mtx_3x2, mtx_4x4);
+    priv->transform_mtx = priv->mtx_3x2;
 
     interop->vt->ActiveTexture(GL_TEXTURE0);
     interop->vt->BindTexture(interop->tex_target, textures[0]);


=====================================
modules/video_output/opengl/renderer.c
=====================================
@@ -359,12 +359,8 @@ vlc_gl_renderer_Open(struct vlc_gl_filter *filter,
     vt->GenBuffers(1, &renderer->index_buffer_object);
     vt->GenBuffers(1, &renderer->texture_buffer_object);
 
-    ret = SetupCoords(renderer);
-    if (ret != VLC_SUCCESS)
-    {
-        Close(filter);
-        return ret;
-    }
+    /* The coords will be initialized on first draw */
+    renderer->valid_coords = false;
 
     return VLC_SUCCESS;
 }
@@ -689,7 +685,8 @@ static int BuildRectangle(GLfloat **vertexCoord, GLfloat **textureCoord, unsigne
 static int SetupCoords(struct vlc_gl_renderer *renderer)
 {
     const opengl_vtable_t *vt = renderer->vt;
-    const video_format_t *fmt = &renderer->sampler->fmt;
+    struct vlc_gl_sampler *sampler = renderer->sampler;
+    const video_format_t *fmt = &sampler->fmt;
 
     GLfloat *vertexCoord, *textureCoord;
     GLushort *indices;
@@ -720,6 +717,10 @@ static int SetupCoords(struct vlc_gl_renderer *renderer)
     if (i_ret != VLC_SUCCESS)
         return i_ret;
 
+    /* Transform picture-to-texture coordinates in place */
+    vlc_gl_sampler_PicToTexCoords(sampler, nbVertices, textureCoord,
+                                  textureCoord);
+
     vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
     vt->BufferData(GL_ARRAY_BUFFER, nbVertices * 2 * sizeof(GLfloat),
                    textureCoord, GL_STATIC_DRAW);
@@ -757,6 +758,18 @@ Draw(struct vlc_gl_filter *filter, const struct vlc_gl_input_meta *meta)
     struct vlc_gl_sampler *sampler = vlc_gl_filter_GetSampler(filter);
     vlc_gl_sampler_Load(sampler);
 
+    if (vlc_gl_sampler_MustRecomputeCoords(sampler))
+        renderer->valid_coords = false;
+
+    if (!renderer->valid_coords)
+    {
+        int ret = SetupCoords(renderer);
+        if (ret != VLC_SUCCESS)
+            return ret;
+
+        renderer->valid_coords = true;
+    }
+
     vt->BindBuffer(GL_ARRAY_BUFFER, renderer->texture_buffer_object);
     assert(renderer->aloc.PicCoordsIn != -1);
     vt->EnableVertexAttribArray(renderer->aloc.PicCoordsIn);


=====================================
modules/video_output/opengl/renderer.h
=====================================
@@ -83,6 +83,8 @@ struct vlc_gl_renderer
     GLuint index_buffer_object;
     GLuint texture_buffer_object;
 
+    bool valid_coords;
+
     /* View point */
     vlc_viewpoint_t vp;
     float f_teta;


=====================================
modules/video_output/opengl/sampler.c
=====================================
@@ -46,19 +46,11 @@ struct vlc_gl_sampler_priv {
     const struct vlc_gl_api *api;
     const opengl_vtable_t *vt; /* for convenience, same as &api->vt */
 
-    struct {
-        GLfloat OrientationMatrix[3*3];
-        GLfloat TexCoordsMap[3*3];
-    } var;
     struct {
         GLint Textures[PICTURE_PLANE_MAX];
         GLint TexSizes[PICTURE_PLANE_MAX]; /* for GL_TEXTURE_RECTANGLE */
         GLint ConvMatrix;
         GLint *pl_vars; /* for pl_sh_res */
-
-        GLint TransformMatrix;
-        GLint OrientationMatrix;
-        GLint TexCoordsMap;
     } uloc;
 
     bool yuv_color;
@@ -101,6 +93,34 @@ struct vlc_gl_sampler_priv {
      * conversion), selected by vlc_gl_sampler_SetCurrentPlane(). */
     bool expose_planes;
     unsigned plane;
+
+    /* All matrices below are stored in column-major order. */
+
+    float mtx_orientation[3*2];
+    float mtx_coords_map[3*2];
+
+    float mtx_transform[3*2];
+    bool mtx_transform_defined;
+
+    /**
+     * tex_coords =   mtx_all  × pic_coords
+     *
+     *  / tex_x \    / a b c \   / pic_x \
+     *  \ tex_y / =  \ d e f / × | pic_y |
+     *                           \   1   /
+     *
+     * Semantically, it represents the result of:
+     *
+     *     get_transform_matrix() * mtx_coords_map * mtx_orientation
+     *
+     * (The intermediate matrices are implicitly expanded to 3x3 with [0 0 1]
+     * as the last row.)
+     *
+     * It is stored in column-major order: [a, d, b, e, c, f].
+     */
+    float mtx_all[3*2];
+    bool mtx_all_defined;
+    bool mtx_all_has_changed; /* since the previous picture */
 };
 
 static inline struct vlc_gl_sampler_priv *
@@ -288,17 +308,6 @@ sampler_base_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
         assert(priv->uloc.ConvMatrix != -1);
     }
 
-    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.TexCoordsMap = vt->GetUniformLocation(program, "TexCoordsMap");
-    assert(priv->uloc.TexCoordsMap != -1);
-
     assert(sampler->tex_count < 10); /* to guarantee variable names length */
     for (unsigned int i = 0; i < sampler->tex_count; ++i)
     {
@@ -331,8 +340,6 @@ GetTransformMatrix(const struct vlc_gl_interop *interop)
     const GLfloat *tm = NULL;
     if (interop && interop->ops && interop->ops->get_transform_matrix)
         tm = interop->ops->get_transform_matrix(interop);
-    if (!tm)
-        tm = MATRIX3_IDENTITY;
     return tm;
 }
 
@@ -357,16 +364,6 @@ sampler_base_load(struct vlc_gl_sampler *sampler)
 
     }
 
-    vt->UniformMatrix3fv(priv->uloc.TexCoordsMap, 1, GL_FALSE,
-                         priv->var.TexCoordsMap);
-
-    /* Return the expected transform matrix if interop == NULL */
-    const GLfloat *tm = GetTransformMatrix(priv->interop);
-    vt->UniformMatrix3fv(priv->uloc.TransformMatrix, 1, GL_FALSE, tm);
-
-    vt->UniformMatrix3fv(priv->uloc.OrientationMatrix, 1, GL_FALSE,
-                         priv->var.OrientationMatrix);
-
     if (priv->tex_target == GL_TEXTURE_RECTANGLE)
     {
         for (unsigned i = 0; i < sampler->tex_count; ++i)
@@ -416,17 +413,6 @@ sampler_xyz12_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
 
     priv->uloc.Textures[0] = vt->GetUniformLocation(program, "Textures[0]");
     assert(priv->uloc.Textures[0] != -1);
-
-    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.TexCoordsMap = vt->GetUniformLocation(program, "TexCoordsMap");
-    assert(priv->uloc.TexCoordsMap != -1);
 }
 
 static void
@@ -440,16 +426,6 @@ sampler_xyz12_load(struct vlc_gl_sampler *sampler)
     assert(priv->textures[0] != 0);
     vt->ActiveTexture(GL_TEXTURE0);
     vt->BindTexture(priv->tex_target, priv->textures[0]);
-
-    vt->UniformMatrix3fv(priv->uloc.TexCoordsMap, 1, GL_FALSE,
-                         priv->var.TexCoordsMap);
-
-    /* Return the expected transform matrix if interop == NULL */
-    const GLfloat *tm = GetTransformMatrix(priv->interop);
-    vt->UniformMatrix3fv(priv->uloc.TransformMatrix, 1, GL_FALSE, tm);
-
-    vt->UniformMatrix3fv(priv->uloc.OrientationMatrix, 1, GL_FALSE,
-                         priv->var.OrientationMatrix);
 }
 
 static int
@@ -479,13 +455,9 @@ xyz12_shader_init(struct vlc_gl_sampler *sampler)
         "    0.0,      0.0,         0.0,        1.0 "
         " );"
 
-        "uniform mat3 TransformMatrix;\n"
-        "uniform mat3 OrientationMatrix;\n"
-        "uniform mat3 TexCoordsMap;\n"
-        "vec4 vlc_texture(vec2 pic_coords)\n"
+        "vec4 vlc_texture(vec2 tex_coords)\n"
         "{ "
         " vec4 v_in, v_out;"
-        " vec2 tex_coords = (TransformMatrix * TexCoordsMap * OrientationMatrix * vec3(pic_coords, 1.0)).xy;\n"
         " v_in  = texture2D(Textures[0], tex_coords);\n"
         " v_in = pow(v_in, xyz_gamma);"
         " v_out = matrix_xyz_rgb * v_in ;"
@@ -563,26 +535,22 @@ opengl_init_swizzle(struct vlc_gl_sampler *sampler,
 }
 
 static void
-InitOrientationMatrix(GLfloat matrix[static 3*3],
-                      video_orientation_t orientation)
+InitOrientationMatrix(float matrix[static 3*2], video_orientation_t orientation)
 {
-    memcpy(matrix, MATRIX3_IDENTITY, sizeof(MATRIX3_IDENTITY));
-
 /**
  * / C0R0  C1R0  C3R0 \
- * | C0R1  C1R1  C3R1 |
- * \    0     0     1 /
+ * \ C0R1  C1R1  C3R1 /
  *
  * (note that in memory, the matrix is stored in column-major order)
  */
 #define MATRIX_SET(C0R0, C1R0, C3R0, \
                    C0R1, C1R1, C3R1) \
-    matrix[0*3 + 0] = C0R0; \
-    matrix[1*3 + 0] = C1R0; \
-    matrix[2*3 + 0] = C3R0; \
-    matrix[0*3 + 1] = C0R1; \
-    matrix[1*3 + 1] = C1R1; \
-    matrix[2*3 + 1] = C3R1;
+    matrix[0*2 + 0] = C0R0; \
+    matrix[1*2 + 0] = C1R0; \
+    matrix[2*2 + 0] = C3R0; \
+    matrix[0*2 + 1] = C0R1; \
+    matrix[1*2 + 1] = C1R1; \
+    matrix[2*2 + 1] = C3R1;
 
     /**
      * The following schemas show how the video picture is oriented in the
@@ -760,20 +728,9 @@ sampler_planes_fetch_locations(struct vlc_gl_sampler *sampler, GLuint program)
 
     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.TexCoordsMap = vt->GetUniformLocation(program, "TexCoordsMap");
-    assert(priv->uloc.TexCoordsMap != -1);
-
     if (priv->tex_target == GL_TEXTURE_RECTANGLE)
     {
         priv->uloc.TexSizes[0] = vt->GetUniformLocation(program, "TexSize");
@@ -795,16 +752,6 @@ sampler_planes_load(struct vlc_gl_sampler *sampler)
     vt->ActiveTexture(GL_TEXTURE0);
     vt->BindTexture(priv->tex_target, priv->textures[plane]);
 
-    vt->UniformMatrix3fv(priv->uloc.TexCoordsMap, 1, GL_FALSE,
-                         priv->var.TexCoordsMap);
-
-    /* Return the expected transform matrix if interop == NULL */
-    const GLfloat *tm = GetTransformMatrix(priv->interop);
-    vt->UniformMatrix3fv(priv->uloc.TransformMatrix, 1, GL_FALSE, tm);
-
-    vt->UniformMatrix3fv(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],
@@ -830,15 +777,11 @@ sampler_planes_init(struct vlc_gl_sampler *sampler)
     GetNames(tex_target, &sampler_type, &texture_fn);
 
     ADDF("uniform %s Texture;\n", sampler_type);
-    ADD("uniform mat3 TexCoordsMap;\n"
-        "uniform mat3 TransformMatrix;\n"
-        "uniform mat3 OrientationMatrix;\n");
 
     if (tex_target == GL_TEXTURE_RECTANGLE)
         ADD("uniform vec2 TexSize;\n");
 
-    ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
-        " vec2 tex_coords = (TransformMatrix * TexCoordsMap * OrientationMatrix * vec3(pic_coords, 1.0)).xy;\n");
+    ADD("vec4 vlc_texture(vec2 tex_coords) {\n");
 
     if (tex_target == GL_TEXTURE_RECTANGLE)
     {
@@ -897,7 +840,7 @@ opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
     unsigned tex_count = desc->plane_count;
     sampler->tex_count = tex_count;
 
-    InitOrientationMatrix(priv->var.OrientationMatrix, orientation);
+    InitOrientationMatrix(priv->mtx_orientation, orientation);
 
     if (expose_planes)
         return sampler_planes_init(sampler);
@@ -925,10 +868,7 @@ opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
 #define ADD(x) vlc_memstream_puts(&ms, x)
 #define ADDF(x, ...) vlc_memstream_printf(&ms, x, ##__VA_ARGS__)
 
-    ADD("uniform mat3 TransformMatrix;\n"
-        "uniform mat3 OrientationMatrix;\n");
     ADDF("uniform %s Textures[%u];\n", glsl_sampler, tex_count);
-    ADD("uniform mat3 TexCoordsMap;\n");
 
 #ifdef HAVE_LIBPLACEBO
     if (priv->pl_sh) {
@@ -1013,8 +953,7 @@ opengl_fragment_shader_init(struct vlc_gl_sampler *sampler, GLenum tex_target,
     if (is_yuv)
         ADD("uniform mat4 ConvMatrix;\n");
 
-    ADD("vec4 vlc_texture(vec2 pic_coords) {\n"
-        " vec2 tex_coords = (TransformMatrix * TexCoordsMap * OrientationMatrix * vec3(pic_coords, 1.0)).xy;\n");
+    ADD("vec4 vlc_texture(vec2 tex_coords) {\n");
 
     unsigned color_count;
     if (is_yuv) {
@@ -1105,6 +1044,11 @@ CreateSampler(struct vlc_gl_interop *interop, struct vlc_gl_t *gl,
     priv->api = api;
     priv->vt = &api->vt;
 
+    priv->mtx_transform_defined = false;
+    sampler->pic_to_tex_matrix = NULL;
+    priv->mtx_all_defined = false;
+    priv->mtx_all_has_changed = false;
+
     /* Formats with palette are not supported. This also allows to copy
      * video_format_t without possibility of failure. */
     assert(!sampler->fmt.p_palette);
@@ -1147,8 +1091,8 @@ CreateSampler(struct vlc_gl_interop *interop, struct vlc_gl_t *gl,
     assert(!interop || interop->tex_count == tex_count);
 
     /* This might be updated in UpdatePicture for non-direct samplers */
-    memcpy(&priv->var.TexCoordsMap, MATRIX3_IDENTITY,
-           sizeof(MATRIX3_IDENTITY));
+    memcpy(&priv->mtx_coords_map, MATRIX3x2_IDENTITY,
+           sizeof(MATRIX3x2_IDENTITY));
 
     if (interop)
     {
@@ -1225,6 +1169,36 @@ vlc_gl_sampler_Delete(struct vlc_gl_sampler *sampler)
     free(priv);
 }
 
+/**
+ * Compute out = a * b, as if the 3x2 matrices were expanded to 3x3 with
+ *  [0 0 1] as the last row.
+ */
+static void
+MatrixMultiply(float out[static 3*2],
+               const float a[static 3*2], const float b[static 3*2])
+{
+    /* All matrices are stored in column-major order. */
+    for (unsigned i = 0; i < 3; ++i)
+        for (unsigned j = 0; j < 2; ++j)
+            out[i*2+j] = a[0*2+j] * b[i*2+0]
+                       + a[1*2+j] * b[i*2+1]
+                       + a[2*2+j];
+}
+
+static void
+UpdateMatrixAll(struct vlc_gl_sampler_priv *priv)
+{
+    float tmp[3*2];
+
+    float *out = priv->mtx_transform_defined ? tmp : priv->mtx_all;
+    /* out = mtx_coords_map * mtx_orientation */
+    MatrixMultiply(out, priv->mtx_coords_map, priv->mtx_orientation);
+
+    if (priv->mtx_transform_defined)
+        /* mtx_all = mtx_transform * tmp */
+        MatrixMultiply(priv->mtx_all, priv->mtx_transform, tmp);
+}
+
 int
 vlc_gl_sampler_UpdatePicture(struct vlc_gl_sampler *sampler, picture_t *picture)
 {
@@ -1235,12 +1209,15 @@ vlc_gl_sampler_UpdatePicture(struct vlc_gl_sampler *sampler, picture_t *picture)
 
     const video_format_t *source = &picture->format;
 
-    if (source->i_x_offset != priv->last_source.i_x_offset
+    bool mtx_changed = false;
+
+    if (!priv->mtx_all_defined
+     || source->i_x_offset != priv->last_source.i_x_offset
      || source->i_y_offset != priv->last_source.i_y_offset
      || source->i_visible_width != priv->last_source.i_visible_width
      || source->i_visible_height != priv->last_source.i_visible_height)
     {
-        memset(priv->var.TexCoordsMap, 0, sizeof(priv->var.TexCoordsMap));
+        memset(priv->mtx_coords_map, 0, sizeof(priv->mtx_coords_map));
 
         /* The transformation is the same for all planes, even with power-of-two
          * textures. */
@@ -1286,27 +1263,27 @@ vlc_gl_sampler_UpdatePicture(struct vlc_gl_sampler *sampler, picture_t *picture)
          *
          * This is an affine 2D transformation, so the input coordinates
          * are given as a 3D vector in the form (x, y, 1), and the output
-         * is (x', y', 1).
+         * is (x', y').
          *
          * The paddings are l (left), r (right), t (top) and b (bottom).
          *
-         *               / (r-l)   0     l \
-         *      matrix = |   0   (b-t)   t |
-         *               \   0     0     1 /
+         *      matrix = / (r-l)   0     l \
+         *               \   0   (b-t)   t /
          *
          * It is stored in column-major order.
          */
-        GLfloat *matrix = priv->var.TexCoordsMap;
-#define COL(x) (x*3)
+        float *matrix = priv->mtx_coords_map;
+#define COL(x) (x*2)
 #define ROW(x) (x)
         matrix[COL(0) + ROW(0)] = right - left;
         matrix[COL(1) + ROW(1)] = bottom - top;
         matrix[COL(2) + ROW(0)] = left;
         matrix[COL(2) + ROW(1)] = top;
-        matrix[COL(2) + ROW(2)] = 1;
 #undef COL
 #undef ROW
 
+        mtx_changed = true;
+
         priv->last_source.i_x_offset = source->i_x_offset;
         priv->last_source.i_y_offset = source->i_y_offset;
         priv->last_source.i_visible_width = source->i_visible_width;
@@ -1314,9 +1291,34 @@ vlc_gl_sampler_UpdatePicture(struct vlc_gl_sampler *sampler, picture_t *picture)
     }
 
     /* Update the texture */
-    return interop->ops->update_textures(interop, priv->textures,
-                                         priv->visible_widths,
-                                         priv->visible_heights, picture, NULL);
+    int ret = interop->ops->update_textures(interop, priv->textures,
+                                            priv->visible_widths,
+                                            priv->visible_heights, picture,
+                                            NULL);
+
+    const float *tm = GetTransformMatrix(interop);
+    if (tm) {
+        memcpy(priv->mtx_transform, tm, sizeof(priv->mtx_transform));
+        priv->mtx_transform_defined = true;
+        mtx_changed = true;
+    }
+    else if (priv->mtx_transform_defined)
+    {
+        priv->mtx_transform_defined = false;
+        mtx_changed = true;
+    }
+
+    if (!priv->mtx_all_defined || mtx_changed)
+    {
+        UpdateMatrixAll(priv);
+        priv->mtx_all_defined = true;
+        sampler->pic_to_tex_matrix = priv->mtx_all;
+        priv->mtx_all_has_changed = true;
+    }
+    else
+        priv->mtx_all_has_changed = false;
+
+    return ret;
 }
 
 int
@@ -1326,6 +1328,17 @@ vlc_gl_sampler_UpdateTextures(struct vlc_gl_sampler *sampler, GLuint textures[],
     struct vlc_gl_sampler_priv *priv = PRIV(sampler);
     assert(!priv->interop);
 
+    if (!priv->mtx_all_defined)
+    {
+        memcpy(priv->mtx_all, MATRIX3x2_IDENTITY, sizeof(MATRIX3x2_IDENTITY));
+        priv->mtx_all_defined = true;
+        priv->mtx_all_has_changed = true;
+
+        sampler->pic_to_tex_matrix = priv->mtx_all;
+    }
+    else
+        priv->mtx_all_has_changed = false;
+
     unsigned tex_count = sampler->tex_count;
     memcpy(priv->textures, textures, tex_count * sizeof(textures[0]));
     memcpy(priv->tex_widths, tex_widths, tex_count * sizeof(tex_widths[0]));
@@ -1340,3 +1353,69 @@ vlc_gl_sampler_SelectPlane(struct vlc_gl_sampler *sampler, unsigned plane)
     struct vlc_gl_sampler_priv *priv = PRIV(sampler);
     priv->plane = plane;
 }
+
+void
+vlc_gl_sampler_PicToTexCoords(struct vlc_gl_sampler *sampler,
+                              unsigned coords_count, const float *pic_coords,
+                              float *tex_coords_out)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    const float *mtx = priv->mtx_all;
+#define MTX(col,row) mtx[(col*2)+row]
+    for (unsigned i = 0; i < coords_count; ++i)
+    {
+        /* Store the coordinates, in case the transform must be applied in
+         * place (i.e. with pic_coords == tex_coords_out) */
+        float x = pic_coords[0];
+        float y = pic_coords[1];
+        tex_coords_out[0] = MTX(0,0) * x + MTX(1,0) * y + MTX(2,0);
+        tex_coords_out[1] = MTX(0,1) * x + MTX(1,1) * y + MTX(2,1);
+        pic_coords += 2;
+        tex_coords_out += 2;
+    }
+}
+
+void
+vlc_gl_sampler_ComputeDirectionMatrix(struct vlc_gl_sampler *sampler,
+                                      float direction[static 2*2])
+{
+    /**
+     * The direction matrix is extracted from priv->mtx_all:
+     *
+     *    mtx_all = / a b c \
+     *              \ d e f /
+     *
+     * The last column (the offset part of the affine transformation) is
+     * discarded, and the 2 remaining column vectors are normalized to remove
+     * any scaling:
+     *
+     *    direction = / a/unorm  b/vnorm \
+     *                \ d/unorm  e/vnorm /
+     *
+     * where unorm = norm( / a \ ) and vnorm = norm( / b \ ).
+     *                     \ d /                     \ e /
+     */
+
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    assert(priv->mtx_all_defined);
+
+    float ux = priv->mtx_all[0];
+    float uy = priv->mtx_all[1];
+    float vx = priv->mtx_all[2];
+    float vy = priv->mtx_all[3];
+
+    float unorm = sqrt(ux * ux + uy * uy);
+    float vnorm = sqrt(vx * vx + vy * vy);
+
+    direction[0] = ux / unorm;
+    direction[1] = uy / unorm;
+    direction[2] = vx / vnorm;
+    direction[3] = vy / vnorm;
+}
+
+bool
+vlc_gl_sampler_MustRecomputeCoords(struct vlc_gl_sampler *sampler)
+{
+    struct vlc_gl_sampler_priv *priv = PRIV(sampler);
+    return priv->mtx_all_has_changed;
+}


=====================================
modules/video_output/opengl/sampler.h
=====================================
@@ -60,6 +60,34 @@ struct vlc_gl_sampler {
     const GLsizei *tex_widths;
     const GLsizei *tex_heights;
 
+    /**
+     * Matrix to convert from picture coordinates to texture coordinates
+     *
+     * The matrix is 3x2 and is stored in column-major order:
+     *
+     *     / a b c \
+     *     \ d e f /
+     *
+     * It is stored as an array of 6 floats:
+     *
+     *     [a, d, b, e, c, f]
+     *
+     * To compute texture coordinates, left-multiply the picture coordinates
+     * by this matrix:
+     *
+     *     tex_coords = pic_to_tex_matrix × pic_coords
+     *
+     *      / tex_x \       / a b c \       / pic_x \
+     *      \ tex_y / =     \ d e f /     × | pic_y |
+     *                                      \   1   /
+     *
+     * It is NULL before the first picture is available and may theoretically
+     * change on every picture (the transform matrix provided by Android may
+     * change). If it has changed since the last picture, then
+     * vlc_gl_sampler_MustRecomputeCoords() will return true.
+     */
+    const float *pic_to_tex_matrix;
+
     struct {
         /**
          * Piece of fragment shader code declaration OpenGL extensions.
@@ -124,4 +152,87 @@ vlc_gl_sampler_Load(struct vlc_gl_sampler *sampler)
     sampler->ops->load(sampler);
 }
 
+/**
+ * Convert from picture coordinates to texture coordinates, which can be used to
+ * sample at the correct location.
+ *
+ * This is a equivalent to retrieve the matrix and multiply manually.
+ *
+ * The picture and texture coords may point to the same memory, in that case
+ * the transformation is applied in place (overwriting the picture coordinates
+ * by the texture coordinates).
+ *
+ * \param sampler the sampler
+ * \param coords_count the number of coordinates (x,y) coordinates to convert
+ * \param pic_coords picture coordinates as an array of 2*coords_count floats
+ * \param tex_coords_out texture coordinates as an array of 2*coords_count
+ *                       floats
+ */
+void
+vlc_gl_sampler_PicToTexCoords(struct vlc_gl_sampler *sampler,
+                              unsigned coords_count, const float *pic_coords,
+                              float *tex_coords_out);
+
+/**
+ * Return a matrix to orient texture coordinates
+ *
+ * This matrix is 2x2 and is stored in column-major order.
+ *
+ * While pic_to_tex_matrix transforms any picture coordinates into texture
+ * coordinates, it may be useful for example for vertex or fragment shaders to
+ * sample one pixel to the left of the current one, or two pixels to the top.
+ * Since the input texture may be rotated or flipped, the shaders need to
+ * know in which direction is the top and in which direction is the right of
+ * the picture.
+ *
+ * This 2x2 matrix allows to transform a 2D vector expressed in picture
+ * coordinates into a 2D vector expressed in texture coordinates.
+ *
+ * Concretely, it contains the coordinates (U, V) of the transformed unit
+ * vectors u = / 1 \ and v = / 0 \:
+ *             \ 0 /         \ 1 /
+ *
+ *     / Ux Vx \
+ *     \ Uy Vy /
+ *
+ * It is guaranteed that:
+ *  - both U and V are unit vectors (this matrix does not change the scaling);
+ *  - only one of their components have a non-zero value (they may not be
+ *    oblique); in other words, here are the possible values for U and V:
+ *
+ *        /  0 \  or  / 0 \  or  / 1 \  or  / -1 \
+ *        \ -1 /      \ 1 /      \ 0 /      \  0 /
+ *
+ *  - U and V are orthogonal.
+ *
+ * Therefore, there are 8 possible matrices (4 possible rotations, flipped or
+ * not).
+ *
+ * Calling this function before the first picture (i.e. when
+ * sampler->pic_to_tex_matrix is NULL) results in undefined behavior.
+ *
+ * It may theoretically change on every picture (the transform matrix provided
+ * by Android may change). If it has changed since the last picture, then
+ * vlc_gl_sampler_MustRecomputeCoords() will return true.
+ */
+void
+vlc_gl_sampler_ComputeDirectionMatrix(struct vlc_gl_sampler *sampler,
+                                      float direction[static 2*2]);
+
+/**
+ * Indicate if the transform to convert picture coordinates to textures
+ * coordinates have changed due to the last picture.
+ *
+ * The filters should call this function on every draw() call, and update their
+ * coordinates if necessary (using vlc_gl_sampler_PicToTexCoords()).
+ *
+ * It is guaranteed that it returns true for the first picture.
+ *
+ * \param sampler the sampler
+ * \retval true if the transform has changed due to the last picture
+ * \retval false if the transform remains the same
+ */
+bool
+vlc_gl_sampler_MustRecomputeCoords(struct vlc_gl_sampler *sampler);
+
 #endif



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/19fc95b3eace9f53dbfebdfcfc53af2fa48551ad...b3527deb879178f9f2f8b95fe4a363a717c993b4

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/19fc95b3eace9f53dbfebdfcfc53af2fa48551ad...b3527deb879178f9f2f8b95fe4a363a717c993b4
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list