[vlc-commits] [Git][videolan/vlc][master] 18 commits: opengl: vaapi: load GL functions from interop

Rémi Denis-Courmont (@Courmisch) gitlab at videolan.org
Sun Jan 23 21:41:36 UTC 2022



Rémi Denis-Courmont pushed to branch master at VideoLAN / VLC


Commits:
326dba18 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: vaapi: load GL functions from interop

Only glBindTexture is used from the interop code, so remove the
dependency from the vtable in vlc_gl_api_t altogether.

The rationale for removing vlc_gl_api_t is that interop implementations
are already required to load specific function for most of them (like
the eglCreateImageKHR function) and having vlc_gl_api_t in the public
module API require exposing the OpenGL vtable and types to the public
scope, which is pretty ard to justify and port/maintain for every
platform.

- - - - -
c1195653 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: importer: forward-declare structs

interop and picture_t definition are not needed in the header.

- - - - -
0803976a by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: importer: remove config.h include

config.h is ought to be included in source files, not headers.

- - - - -
fc2e04da by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
nvdec_gl: load OpenGL function from GL

This removes the dependency from the vtable in vlc_gl_api_t.

- - - - -
f575cb60 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_vdpau: avoid checking surface type

The surface type doesn't matter, especially since initialization for X11
is done by the decoder device now. All that matter is that the extension
is available for the given context, providing access to the vdpau
interop functions from this context.

- - - - -
b42d8e9e by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_vdpau: load OpenGL functions from GL

This removes the dependency from the vtable in vlc_gl_api_t, for future
removal of the api from the interop API.

- - - - -
f4b2bfd1 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_sw: load OpenGL functions from gl

This removes the dependency from the vtable in vlc_gl_api_t, for future
removal of the api from the interop API.

- - - - -
d1d33406 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_android: load GL functions from gl

- - - - -
69bea27b by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_cvpx: load functions from GL

- - - - -
b8ad7a78 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: add vlc_gl_CheckExtension in API

Checking an extension is different between OpenGL compatibility profile
or ES2 profile, and OpenGL core profile. Separating the call in a
different function simplify the handling of those extension checks, and
provides better compatibility with the core profile.

- - - - -
1235aa62 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
interop: use vlc_gl_HasExtension

This improves compatibility with the core profile for OpenGL and removes
usage of the extension field from vlc_gl_api_t, to remove dependency
from the interop to the vlc_gl_api_t.

- - - - -
daa14ec5 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop_sw: remove usage of api->is_gles

The API type is already available in the vlc_gl_t object.

- - - - -
b66ef9ed by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: importer: remove usage of vlc_gl_api_t

Directly load the function in the call. This is not very pretty, but
much easier to write than storing the pointer in a private part of the
importer.

- - - - -
64d4415f by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
interop: load function from GL

Allocate a private part for the interop containing the actual OpenGL
vtable used by the interop "core" code.

- - - - -
7648af6e by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: importer: inline npot capability detection

Avoid using vlc_gl_api_t to determine whether npot are usable or not and
directly inline the same check from gl_api.c.

- - - - -
562ba66c by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop: remove vlc_gl_api_t references

The implementations are now independant of the vlc_gl_api_t structure
and the OpenGL virtual table.

The rationale for removing vlc_gl_api_t is that interop implementations
are already required to load specific function for most of them (like
the eglCreateImageKHR function) and having vlc_gl_api_t in the public
module API require exposing the OpenGL vtable and types to the public
scope, which is pretty ard to justify and port/maintain for every
platform.

- - - - -
a3803e96 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
vlc_opengl_interop: publish vlc_gl_interop entrypoint

Create an header file containing the interop definition and operations,
so that the entrypoint can also be implemented out-of-tree and doesn't
depend on code from the modules/ directory.

- - - - -
5a010800 by Alexandre Janniaux at 2022-01-23T19:13:10+00:00
opengl: interop: unify type signatures

We don't depend on OpenGL types for the signature anymore.

- - - - -


18 changed files:

- include/vlc_opengl.h
- + include/vlc_opengl_interop.h
- modules/hw/nvdec/nvdec_gl.c
- modules/video_filter/opengl.c
- modules/video_output/opengl/importer.c
- modules/video_output/opengl/importer.h
- modules/video_output/opengl/interop.c
- modules/video_output/opengl/interop.h
- modules/video_output/opengl/interop_android.c
- modules/video_output/opengl/interop_cvpx.m
- modules/video_output/opengl/interop_dxva2.c
- modules/video_output/opengl/interop_sw.c
- modules/video_output/opengl/interop_vaapi.c
- modules/video_output/opengl/interop_vdpau.c
- modules/video_output/opengl/vout_helper.c
- src/Makefile.am
- src/libvlccore.sym
- src/video_output/opengl.c


Changes:

=====================================
include/vlc_opengl.h
=====================================
@@ -100,6 +100,8 @@ VLC_API vlc_gl_t *vlc_gl_CreateOffscreen(vlc_object_t *parent,
 VLC_API void vlc_gl_Release(vlc_gl_t *);
 VLC_API void vlc_gl_Hold(vlc_gl_t *);
 
+VLC_API bool vlc_gl_HasExtension(vlc_gl_t *gl, const char *extension);
+
 static inline int vlc_gl_MakeCurrent(vlc_gl_t *gl)
 {
     return gl->make_current(gl);


=====================================
include/vlc_opengl_interop.h
=====================================
@@ -0,0 +1,161 @@
+/*****************************************************************************
+ * vlc_opengl_interop.h: VLC picture_t to GL texture API
+ *****************************************************************************
+ * Copyright (C) 2019-2022 Videolabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_GL_INTEROP_H
+#define VLC_GL_INTEROP_H 1
+
+#include <vlc_es.h>
+#include <vlc_picture.h>
+
+struct vlc_gl_interop;
+struct vlc_video_context;
+
+struct vlc_gl_interop_ops {
+    /**
+     * Callback to allocate data for bound textures
+     *
+     * This function pointer can be NULL. Software converters should call
+     * glTexImage2D() to allocate textures data (it will be deallocated by the
+     * caller when calling glDeleteTextures()). Won't be called if
+     * handle_texs_gen is true.
+     *
+     * \param interop the OpenGL interop
+     * \param textures array of textures to bind (one per plane)
+     * \param tex_width array of tex width (one per plane)
+     * \param tex_height array of tex height (one per plane)
+     * \return VLC_SUCCESS or a VLC error
+     */
+    int (*allocate_textures)(const struct vlc_gl_interop *interop,
+            uint32_t textures[], const int32_t tex_width[],
+            const int32_t tex_height[]);
+
+    /**
+     * Callback to update a picture
+     *
+     * This function pointer cannot be NULL. The implementation should upload
+     * every planes of the picture.
+     *
+     * \param interop the OpenGL interop
+     * \param textures array of textures to bind (one per plane)
+     * \param tex_width array of tex width (one per plane)
+     * \param tex_height array of tex height (one per plane)
+     * \param pic picture to update
+     * \param plane_offset offsets of each picture planes to read data from
+     * (one per plane, can be NULL)
+     * \return VLC_SUCCESS or a VLC error
+     */
+    int (*update_textures)(const struct vlc_gl_interop *interop,
+            uint32_t textures[], const int32_t tex_width[],
+            const int32_t tex_height[], picture_t *pic,
+            const size_t plane_offsets[]);
+
+    /**
+     * Callback to retrieve the transform matrix to apply to texture coordinates
+     *
+     * This function pointer can be NULL. If it is set, it may return NULL.
+     *
+     * Otherwise, it must return a 2x3 matrix, as an array of 6 floats in
+     * column-major order.
+     *
+     * This transform matrix maps 2D homogeneous texture coordinates of the
+     * form (s, t, 1) with s and t in the inclusive range [0, 1] to the
+     * texture coordinate that should be used to sample that location from the
+     * texture.
+     *
+     * The returned pointer is owned by the converter module, and must not be
+     * freed before the module is closed.
+     *
+     * \param interop the OpenGL interop
+     * \return a 2x3 transformation matrix (possibly NULL)
+     */
+    const float *
+    (*get_transform_matrix)(const struct vlc_gl_interop *interop);
+
+    /**
+     * Called before the interop is destroyed
+     *
+     * This function pointer can be NULL.
+     *
+     * \param interop the OpenGL interop
+     */
+    void (*close)(struct vlc_gl_interop *interop);
+};
+
+struct vlc_gl_interop {
+    vlc_object_t obj;
+    module_t *module;
+
+    vlc_gl_t *gl;
+    uint32_t tex_target;
+
+    /* Input format
+     *
+     * This is the format of the pictures received from the core.
+     *
+     * It can be modified from the module open function to request changes from
+     * the core.
+     */
+    video_format_t fmt_in;
+
+    /* Output format
+     *
+     * This is the format of the pictures exposed by the interop to the sampler.
+     *
+     * It may differ from the input format:
+     *  - the orientation may be vertically flipped
+     *  - the chroma contains the "software" chroma if the input chroma is opaque
+     *  - the chroma may also be changed internally to a fallback (see
+     *    opengl_interop_generic_init())
+     */
+    video_format_t fmt_out;
+
+    /* Pointer to decoder video context, set by the caller (can be NULL) */
+    struct vlc_video_context *vctx;
+
+    /* Set to true if textures are generated from pf_update() */
+    bool handle_texs_gen;
+
+    /* Initialized by the interop */
+    struct vlc_gl_tex_cfg {
+        /*
+         * Texture scale factor, cannot be 0.
+         * In 4:2:0, 1/1 for the Y texture and 1/2 for the UV texture(s)
+         */
+        vlc_rational_t w;
+        vlc_rational_t h;
+
+        int32_t internal;
+        uint32_t format;
+        uint32_t type;
+    } texs[PICTURE_PLANE_MAX];
+    unsigned tex_count;
+
+    void *priv;
+    const struct vlc_gl_interop_ops *ops;
+
+    /* Set by the caller to opengl_interop_init_impl().
+     * This avoids each module to link against opengl_interop_init_impl()
+     * directly. */
+    int
+    (*init)(struct vlc_gl_interop *interop, uint32_t tex_target,
+            vlc_fourcc_t chroma, video_color_space_t yuv_space);
+};
+
+#endif


=====================================
modules/hw/nvdec/nvdec_gl.c
=====================================
@@ -58,12 +58,18 @@ typedef struct {
     CUcontext cuConverterCtx;
     CUgraphicsResource cu_res[PICTURE_PLANE_MAX]; // Y, UV for NV12/P010
     CUarray mappedArray[PICTURE_PLANE_MAX];
+
+    struct {
+        PFNGLBINDTEXTUREPROC BindTexture;
+        PFNGLGETERRORPROC GetError;
+        PFNGLTEXIMAGE2DPROC TexImage2D;
+    } gl;
 } converter_sys_t;
 
 #define CALL_CUDA(func, ...) CudaCheckErr(VLC_OBJECT(interop->gl), devsys->cudaFunctions, devsys->cudaFunctions->func(__VA_ARGS__), #func)
 
-static int tc_nvdec_gl_allocate_texture(const struct vlc_gl_interop *interop, GLuint *textures,
-                                const GLsizei *tex_width, const GLsizei *tex_height)
+static int tc_nvdec_gl_allocate_texture(const struct vlc_gl_interop *interop, uint32_t textures[],
+                                const int32_t tex_width[], const int32_t tex_height[])
 {
     converter_sys_t *p_sys = interop->priv;
     vlc_decoder_device *device = p_sys->device;
@@ -76,11 +82,11 @@ static int tc_nvdec_gl_allocate_texture(const struct vlc_gl_interop *interop, GL
 
     for (unsigned i = 0; i < interop->tex_count; i++)
     {
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
-        interop->vt->TexImage2D(interop->tex_target, 0, interop->texs[i].internal,
+        p_sys->gl.BindTexture(interop->tex_target, textures[i]);
+        p_sys->gl.TexImage2D(interop->tex_target, 0, interop->texs[i].internal,
                            tex_width[i], tex_height[i], 0, interop->texs[i].format,
                            interop->texs[i].type, NULL);
-        if (interop->vt->GetError() != GL_NO_ERROR)
+        if (p_sys->gl.GetError() != GL_NO_ERROR)
         {
             msg_Err(interop->gl, "could not alloc PBO buffers");
             return VLC_EGENERIC;
@@ -92,7 +98,7 @@ static int tc_nvdec_gl_allocate_texture(const struct vlc_gl_interop *interop, GL
         result = CALL_CUDA(cuGraphicsSubResourceGetMappedArray, &p_sys->mappedArray[i], p_sys->cu_res[i], 0, 0);
         result = CALL_CUDA(cuGraphicsUnmapResources, 1, &p_sys->cu_res[i], 0);
 
-        interop->vt->BindTexture(interop->tex_target, 0);
+        p_sys->gl.BindTexture(interop->tex_target, 0);
     }
 
     CALL_CUDA(cuCtxPopCurrent, NULL);
@@ -100,8 +106,8 @@ static int tc_nvdec_gl_allocate_texture(const struct vlc_gl_interop *interop, GL
 }
 
 static int
-tc_nvdec_gl_update(const struct vlc_gl_interop *interop, GLuint textures[],
-                   GLsizei const tex_widths[], GLsizei const tex_heights[],
+tc_nvdec_gl_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+                   int32_t const tex_widths[], int32_t const tex_heights[],
                    picture_t *pic, size_t const plane_offsets[])
 {
     VLC_UNUSED(plane_offsets);
@@ -167,6 +173,19 @@ static int Open(vlc_object_t *obj)
         vlc_decoder_device_Release(device);
         return VLC_ENOMEM;
     }
+
+    p_sys->gl.BindTexture =
+        vlc_gl_GetProcAddress(interop->gl, "glBindTexture");
+    assert(p_sys->gl.BindTexture);
+
+    p_sys->gl.GetError =
+        vlc_gl_GetProcAddress(interop->gl, "glGetError");
+    assert(p_sys->gl.GetError);
+
+    p_sys->gl.TexImage2D =
+        vlc_gl_GetProcAddress(interop->gl, "glTexImage2D");
+    assert(p_sys->gl.TexImage2D);
+
     for (size_t i=0; i < ARRAY_SIZE(p_sys->cu_res); i++)
         p_sys->cu_res[i] = NULL;
     p_sys->cuConverterCtx = NULL;


=====================================
modules/video_filter/opengl.c
=====================================
@@ -209,7 +209,7 @@ static int Open( vlc_object_t *obj )
         goto gl_api_failure;
     }
 
-    sys->interop = vlc_gl_interop_New(sys->gl, api, filter->vctx_in,
+    sys->interop = vlc_gl_interop_New(sys->gl, filter->vctx_in,
                                       &filter->fmt_in.video);
     if (!sys->interop)
     {


=====================================
modules/video_output/opengl/importer.c
=====================================
@@ -23,6 +23,7 @@
 #endif
 
 #include <vlc_common.h>
+#include <vlc_picture.h>
 
 #include "importer_priv.h"
 
@@ -204,8 +205,6 @@ vlc_gl_importer_New(struct vlc_gl_interop *interop)
         return NULL;
 
     importer->interop = interop;
-    importer->api = interop->api;
-    importer->vt = &interop->api->vt;
 
     importer->mtx_transform_defined = false;
     importer->pic_mtx_defined = false;
@@ -227,6 +226,11 @@ vlc_gl_importer_New(struct vlc_gl_interop *interop)
 
     InitOrientationMatrix(importer->mtx_orientation, glfmt->fmt.orientation);
 
+    /* OpenGL ES 2 includes support for non-power of 2 textures by specification. */
+    bool supports_npot = interop->gl->api_type == VLC_OPENGL_ES2
+        || vlc_gl_HasExtension(interop->gl, "GL_ARB_texture_non_power_of_two")
+        || vlc_gl_HasExtension(interop->gl, "GL_APPLE_texture_2D_limited_npot");
+
     /* Texture size */
     for (unsigned j = 0; j < interop->tex_count; j++) {
         GLsizei w = interop->fmt_out.i_visible_width  * interop->texs[j].w.num
@@ -235,7 +239,7 @@ vlc_gl_importer_New(struct vlc_gl_interop *interop)
                   / interop->texs[j].h.den;
         glfmt->visible_widths[j] = w;
         glfmt->visible_heights[j] = h;
-        if (interop->api->supports_npot) {
+        if (supports_npot) {
             glfmt->tex_widths[j]  = w;
             glfmt->tex_heights[j] = h;
         } else {
@@ -263,10 +267,12 @@ void
 vlc_gl_importer_Delete(struct vlc_gl_importer *importer)
 {
     struct vlc_gl_interop *interop = importer->interop;
+
     if (interop && !interop->handle_texs_gen)
     {
-        const opengl_vtable_t *vt = interop->vt;
-        vt->DeleteTextures(interop->tex_count, importer->pic.textures);
+        void (*DeleteTextures)(uint32_t, uint32_t*) =
+            vlc_gl_GetProcAddress(interop->gl, "glDeleteTextures");
+        (*DeleteTextures)(interop->tex_count, importer->pic.textures);
     }
 
     free(importer);


=====================================
modules/video_output/opengl/importer.h
=====================================
@@ -21,18 +21,14 @@
 #ifndef VLC_GL_IMPORTER_H
 #define VLC_GL_IMPORTER_H
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <vlc_picture.h>
-#include "interop.h"
+typedef struct picture_t picture_t;
 
 /**
  * An importer uses an interop to convert picture_t to a valid vlc_gl_picture,
  * with all necessary transformations computed.
  */
 struct vlc_gl_importer;
+struct vlc_gl_interop;
 
 struct vlc_gl_importer *
 vlc_gl_importer_New(struct vlc_gl_interop *interop);


=====================================
modules/video_output/opengl/interop.c
=====================================
@@ -30,27 +30,51 @@
 #include "interop_sw.h"
 #include "vout_helper.h"
 
+struct vlc_gl_interop_private
+{
+    struct vlc_gl_interop interop;
+
+#define OPENGL_VTABLE_F(X) \
+        X(PFNGLDELETETEXTURESPROC, DeleteTextures) \
+        X(PFNGLGENTEXTURESPROC,    GenTextures) \
+        X(PFNGLBINDTEXTUREPROC,    BindTexture) \
+        X(PFNGLTEXIMAGE2DPROC,     TexImage2D) \
+        X(PFNGLTEXENVFPROC,        TexEnvf) \
+        X(PFNGLTEXPARAMETERFPROC,  TexParameterf) \
+        X(PFNGLTEXPARAMETERIPROC,  TexParameteri) \
+        X(PFNGLGETERRORPROC,       GetError) \
+        X(PFNGLGETTEXLEVELPARAMETERIVPROC, GetTexLevelParameteriv) \
+
+    struct {
+#define DECLARE_SYMBOL(type, name) type name;
+        OPENGL_VTABLE_F(DECLARE_SYMBOL)
+    } gl;
+};
+
 int
 vlc_gl_interop_GenerateTextures(const struct vlc_gl_interop *interop,
                                 const GLsizei *tex_width,
                                 const GLsizei *tex_height, GLuint *textures)
 {
-    interop->vt->GenTextures(interop->tex_count, textures);
+    struct vlc_gl_interop_private *priv =
+        container_of(interop, struct vlc_gl_interop_private, interop);
+
+    priv->gl.GenTextures(interop->tex_count, textures);
 
     for (unsigned i = 0; i < interop->tex_count; i++)
     {
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
 
 #if !defined(USE_OPENGL_ES2)
         /* Set the texture parameters */
-        interop->vt->TexParameterf(interop->tex_target, GL_TEXTURE_PRIORITY, 1.0);
-        interop->vt->TexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+        priv->gl.TexParameterf(interop->tex_target, GL_TEXTURE_PRIORITY, 1.0);
+        priv->gl.TexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 #endif
 
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     }
 
     if (interop->ops->allocate_textures != NULL)
@@ -58,7 +82,7 @@ vlc_gl_interop_GenerateTextures(const struct vlc_gl_interop *interop,
         int ret = interop->ops->allocate_textures(interop, textures, tex_width, tex_height);
         if (ret != VLC_SUCCESS)
         {
-            interop->vt->DeleteTextures(interop->tex_count, textures);
+            priv->gl.DeleteTextures(interop->tex_count, textures);
             memset(textures, 0, interop->tex_count * sizeof(GLuint));
             return ret;
         }
@@ -70,15 +94,21 @@ void
 vlc_gl_interop_DeleteTextures(const struct vlc_gl_interop *interop,
                               GLuint *textures)
 {
-    interop->vt->DeleteTextures(interop->tex_count, textures);
+    struct vlc_gl_interop_private *priv =
+        container_of(interop, struct vlc_gl_interop_private, interop);
+    priv->gl.DeleteTextures(interop->tex_count, textures);
     memset(textures, 0, interop->tex_count * sizeof(GLuint));
 }
 
-static int GetTexFormatSize(const opengl_vtable_t *vt, int target,
+static int GetTexFormatSize(struct vlc_gl_interop *interop, int target,
                             int tex_format, int tex_internal, int tex_type)
 {
-    GL_ASSERT_NOERROR(vt);
-    if (!vt->GetTexLevelParameteriv)
+    struct vlc_gl_interop_private *priv =
+        container_of(interop, struct vlc_gl_interop_private, interop);
+
+    GL_ASSERT_NOERROR(&priv->gl);
+
+    if (!priv->gl.GetTexLevelParameteriv)
         return -1;
 
     GLint tex_param_size;
@@ -100,16 +130,16 @@ static int GetTexFormatSize(const opengl_vtable_t *vt, int target,
     }
     GLuint texture;
 
-    vt->GenTextures(1, &texture);
-    vt->BindTexture(target, texture);
-    vt->TexImage2D(target, 0, tex_internal, 64, 64, 0, tex_format, tex_type, NULL);
+    priv->gl.GenTextures(1, &texture);
+    priv->gl.BindTexture(target, texture);
+    priv->gl.TexImage2D(target, 0, tex_internal, 64, 64, 0, tex_format, tex_type, NULL);
     GLint size = 0;
-    vt->GetTexLevelParameteriv(target, 0, tex_param_size, &size);
+    priv->gl.GetTexLevelParameteriv(target, 0, tex_param_size, &size);
 
-    vt->DeleteTextures(1, &texture);
+    priv->gl.DeleteTextures(1, &texture);
 
     bool has_error = false;
-    while (vt->GetError() != GL_NO_ERROR)
+    while (priv->gl.GetError() != GL_NO_ERROR)
         has_error = true;
 
     if (has_error)
@@ -136,7 +166,7 @@ interop_yuv_base_init(struct vlc_gl_interop *interop, GLenum tex_target,
     GLint oneplane_texfmt, oneplane16_texfmt,
           twoplanes_texfmt, twoplanes16_texfmt;
 
-    if (vlc_gl_StrHasToken(interop->api->extensions, "GL_ARB_texture_rg"))
+    if (vlc_gl_HasExtension(interop->gl, "GL_ARB_texture_rg"))
     {
         oneplane_texfmt = GL_RED;
         oneplane16_texfmt = GL_R16;
@@ -153,7 +183,7 @@ interop_yuv_base_init(struct vlc_gl_interop *interop, GLenum tex_target,
 
     if (desc->pixel_size == 2)
     {
-        if (GetTexFormatSize(interop->vt, tex_target, oneplane_texfmt,
+        if (GetTexFormatSize(interop, tex_target, oneplane_texfmt,
                              oneplane16_texfmt, GL_UNSIGNED_SHORT) != 16)
             return VLC_EGENERIC;
     }
@@ -208,7 +238,7 @@ interop_yuv_base_init(struct vlc_gl_interop *interop, GLenum tex_target,
         else if (desc->pixel_size == 2)
         {
             if (twoplanes16_texfmt == 0
-             || GetTexFormatSize(interop->vt, tex_target, twoplanes_texfmt,
+             || GetTexFormatSize(interop, tex_target, twoplanes_texfmt,
                                  twoplanes16_texfmt, GL_UNSIGNED_SHORT) != 16)
                 return VLC_EGENERIC;
             interop->texs[0] = (struct vlc_gl_tex_cfg) {
@@ -283,7 +313,7 @@ interop_rgb_base_init(struct vlc_gl_interop *interop, GLenum tex_target,
             };
             break;
         case VLC_CODEC_BGRA: {
-            if (GetTexFormatSize(interop->vt, tex_target, GL_BGRA, GL_RGBA,
+            if (GetTexFormatSize(interop, tex_target, GL_BGRA, GL_RGBA,
                                  GL_UNSIGNED_BYTE) != 32)
                 return VLC_EGENERIC;
             interop->texs[0] = (struct vlc_gl_tex_cfg) {
@@ -336,23 +366,22 @@ opengl_interop_init_impl(struct vlc_gl_interop *interop, GLenum tex_target,
 }
 
 struct vlc_gl_interop *
-vlc_gl_interop_New(struct vlc_gl_t *gl, const struct vlc_gl_api *api,
-                   vlc_video_context *context, const video_format_t *fmt)
+vlc_gl_interop_New(struct vlc_gl_t *gl, vlc_video_context *context,
+                   const video_format_t *fmt)
 {
-    struct vlc_gl_interop *interop = vlc_object_create(gl, sizeof(*interop));
-    if (!interop)
+    struct vlc_gl_interop_private *priv = vlc_object_create(gl, sizeof *priv);
+    if (priv == NULL)
         return NULL;
 
+    struct vlc_gl_interop *interop = &priv->interop;
+
     interop->init = opengl_interop_init_impl;
     interop->ops = NULL;
     interop->fmt_out = interop->fmt_in = *fmt;
+    interop->gl = gl;
     /* this is the only allocated field, and we don't need it */
     interop->fmt_out.p_palette = interop->fmt_in.p_palette = NULL;
 
-    interop->gl = gl;
-    interop->api = api;
-    interop->vt = &api->vt;
-
     const vlc_chroma_description_t *desc =
         vlc_fourcc_GetChromaDescription(fmt->i_chroma);
 
@@ -362,6 +391,11 @@ vlc_gl_interop_New(struct vlc_gl_t *gl, const struct vlc_gl_api *api,
         return NULL;
     }
 
+#define LOAD_SYMBOL(type, name) \
+    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name);
+    OPENGL_VTABLE_F(LOAD_SYMBOL);
+#undef LOAD_SYMBOL
+
     if (desc->plane_count == 0)
     {
         /* Opaque chroma: load a module to handle it */
@@ -378,6 +412,7 @@ vlc_gl_interop_New(struct vlc_gl_t *gl, const struct vlc_gl_api *api,
             goto error;
     }
 
+
     return interop;
 
 error:
@@ -386,22 +421,25 @@ error:
 }
 
 struct vlc_gl_interop *
-vlc_gl_interop_NewForSubpictures(struct vlc_gl_t *gl,
-                                 const struct vlc_gl_api *api)
+vlc_gl_interop_NewForSubpictures(struct vlc_gl_t *gl)
 {
-    struct vlc_gl_interop *interop = vlc_object_create(gl, sizeof(*interop));
-    if (!interop)
+    struct vlc_gl_interop_private *priv = vlc_object_create(gl, sizeof *priv);
+    if (priv == NULL)
         return NULL;
 
+    struct vlc_gl_interop *interop = &priv->interop;
     interop->init = opengl_interop_init_impl;
     interop->ops = NULL;
     interop->gl = gl;
-    interop->api = api;
-    interop->vt = &api->vt;
 
     video_format_Init(&interop->fmt_in, VLC_CODEC_RGB32);
     interop->fmt_out = interop->fmt_in;
 
+#define LOAD_SYMBOL(type, name) \
+    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name);
+    OPENGL_VTABLE_F(LOAD_SYMBOL);
+#undef LOAD_SYMBOL
+
     int ret = opengl_interop_generic_init(interop, false);
     if (ret != VLC_SUCCESS)
     {


=====================================
modules/video_output/opengl/interop.h
=====================================
@@ -18,161 +18,23 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
-#ifndef VLC_GL_INTEROP_H
-#define VLC_GL_INTEROP_H
+#ifndef VLC_GL_INTEROP_PRIV_H
+#define VLC_GL_INTEROP_PRIV_H
 
 #include <vlc_common.h>
 #include <vlc_opengl.h>
 #include <vlc_picture.h>
 #include <vlc_picture_pool.h>
-
+#include <vlc_opengl_interop.h>
 #include "gl_common.h"
 
-struct vlc_gl_interop;
-
-struct vlc_gl_interop_ops {
-    /**
-     * Callback to allocate data for bound textures
-     *
-     * This function pointer can be NULL. Software converters should call
-     * glTexImage2D() to allocate textures data (it will be deallocated by the
-     * caller when calling glDeleteTextures()). Won't be called if
-     * handle_texs_gen is true.
-     *
-     * \param interop the OpenGL interop
-     * \param textures array of textures to bind (one per plane)
-     * \param tex_width array of tex width (one per plane)
-     * \param tex_height array of tex height (one per plane)
-     * \return VLC_SUCCESS or a VLC error
-     */
-    int
-    (*allocate_textures)(const struct vlc_gl_interop *interop,
-                         GLuint textures[], const GLsizei tex_width[],
-                         const GLsizei tex_height[]);
-
-    /**
-     * Callback to update a picture
-     *
-     * This function pointer cannot be NULL. The implementation should upload
-     * every planes of the picture.
-     *
-     * \param interop the OpenGL interop
-     * \param textures array of textures to bind (one per plane)
-     * \param tex_width array of tex width (one per plane)
-     * \param tex_height array of tex height (one per plane)
-     * \param pic picture to update
-     * \param plane_offset offsets of each picture planes to read data from
-     * (one per plane, can be NULL)
-     * \return VLC_SUCCESS or a VLC error
-     */
-    int
-    (*update_textures)(const struct vlc_gl_interop *interop,
-                       GLuint textures[], const GLsizei tex_width[],
-                       const GLsizei tex_height[], picture_t *pic,
-                       const size_t plane_offsets[]);
-
-    /**
-     * Callback to retrieve the transform matrix to apply to texture coordinates
-     *
-     * This function pointer can be NULL. If it is set, it may return NULL.
-     *
-     * Otherwise, it must return a 2x3 matrix, as an array of 6 floats in
-     * column-major order.
-     *
-     * This transform matrix maps 2D homogeneous texture coordinates of the
-     * form (s, t, 1) with s and t in the inclusive range [0, 1] to the
-     * texture coordinate that should be used to sample that location from the
-     * texture.
-     *
-     * The returned pointer is owned by the converter module, and must not be
-     * freed before the module is closed.
-     *
-     * \param interop the OpenGL interop
-     * \return a 2x3 transformation matrix (possibly NULL)
-     */
-    const float *
-    (*get_transform_matrix)(const struct vlc_gl_interop *interop);
-
-    /**
-     * Called before the interop is destroyed
-     *
-     * This function pointer can be NULL.
-     *
-     * \param interop the OpenGL interop
-     */
-    void
-    (*close)(struct vlc_gl_interop *interop);
-};
-
-struct vlc_gl_interop {
-    vlc_object_t obj;
-    module_t *module;
-
-    vlc_gl_t *gl;
-    const struct vlc_gl_api *api;
-    const opengl_vtable_t *vt; /* for convenience, same as &api->vt */
-    GLenum tex_target;
-
-    /* Input format
-     *
-     * This is the format of the pictures received from the core.
-     *
-     * It can be modified from the module open function to request changes from
-     * the core.
-     */
-    video_format_t fmt_in;
-
-    /* Output format
-     *
-     * This is the format of the pictures exposed by the interop to the sampler.
-     *
-     * It may differ from the input format:
-     *  - the orientation may be vertically flipped
-     *  - the chroma contains the "software" chroma if the input chroma is opaque
-     *  - the chroma may also be changed internally to a fallback (see
-     *    opengl_interop_generic_init())
-     */
-    video_format_t fmt_out;
-
-    /* Pointer to decoder video context, set by the caller (can be NULL) */
-    vlc_video_context *vctx;
-
-    /* Set to true if textures are generated from pf_update() */
-    bool handle_texs_gen;
-
-    /* Initialized by the interop */
-    struct vlc_gl_tex_cfg {
-        /*
-         * Texture scale factor, cannot be 0.
-         * In 4:2:0, 1/1 for the Y texture and 1/2 for the UV texture(s)
-         */
-        vlc_rational_t w;
-        vlc_rational_t h;
-
-        GLint internal;
-        GLenum format;
-        GLenum type;
-    } texs[PICTURE_PLANE_MAX];
-    unsigned tex_count;
-
-    void *priv;
-    const struct vlc_gl_interop_ops *ops;
-
-    /* Set by the caller to opengl_interop_init_impl().
-     * This avoids each module to link against opengl_interop_init_impl()
-     * directly. */
-    int
-    (*init)(struct vlc_gl_interop *interop, GLenum tex_target,
-            vlc_fourcc_t chroma, video_color_space_t yuv_space);
-};
 
 struct vlc_gl_interop *
-vlc_gl_interop_New(struct vlc_gl_t *gl, const struct vlc_gl_api *api,
-                   vlc_video_context *context, const video_format_t *fmt);
+vlc_gl_interop_New(struct vlc_gl_t *gl, vlc_video_context *context,
+                   const video_format_t *fmt);
 
 struct vlc_gl_interop *
-vlc_gl_interop_NewForSubpictures(struct vlc_gl_t *gl,
-                                 const struct vlc_gl_api *api);
+vlc_gl_interop_NewForSubpictures(struct vlc_gl_t *gl);
 
 void
 vlc_gl_interop_Delete(struct vlc_gl_interop *interop);


=====================================
modules/video_output/opengl/interop_android.c
=====================================
@@ -40,6 +40,12 @@ struct priv
     bool stex_attached;
     struct vlc_asurfacetexture *previous_texture;
     picture_t *current_picture;
+
+    struct {
+        PFNGLACTIVETEXTUREPROC ActiveTexture;
+        PFNGLBINDTEXTUREPROC BindTexture;
+        PFNGLGENTEXTURESPROC GenTextures;
+    } gl;
 };
 
 static void
@@ -74,8 +80,8 @@ ReductMatrix(float *mtx_2x3, const float *mtx_4x4)
 }
 
 static int
-tc_anop_allocate_textures(const struct vlc_gl_interop *interop, GLuint *textures,
-                          const GLsizei *tex_width, const GLsizei *tex_height)
+tc_anop_allocate_textures(const struct vlc_gl_interop *interop, uint32_t textures[],
+                          const int32_t tex_width[], const int32_t tex_height[])
 {
     (void) tex_width; (void) tex_height;
     struct priv *priv = interop->priv;
@@ -85,8 +91,8 @@ tc_anop_allocate_textures(const struct vlc_gl_interop *interop, GLuint *textures
 }
 
 static int
-tc_anop_update(struct vlc_gl_interop *interop, GLuint *textures,
-               const GLsizei *tex_width, const GLsizei *tex_height,
+tc_anop_update(struct vlc_gl_interop *interop, uint32_t textures[],
+               const int32_t tex_width[], const int32_t tex_height[],
                picture_t *pic, const size_t *plane_offset)
 {
     struct priv *priv = interop->priv;
@@ -119,7 +125,7 @@ tc_anop_update(struct vlc_gl_interop *interop, GLuint *textures,
             SurfaceTexture_detachFromGLContext(previous_texture);
             /* SurfaceTexture_detachFromGLContext will destroy the previous
              * texture name, so we need to regenerate it. */
-            interop->api->vt.GenTextures(1, &textures[0]);
+            priv->gl.GenTextures(1, &textures[0]);
         }
 
         if (SurfaceTexture_attachToGLContext(texture, textures[0]) != 0)
@@ -147,8 +153,8 @@ tc_anop_update(struct vlc_gl_interop *interop, GLuint *textures,
     ReductMatrix(priv->mtx_2x3, mtx_4x4);
     priv->transform_mtx = priv->mtx_2x3;
 
-    interop->vt->ActiveTexture(GL_TEXTURE0);
-    interop->vt->BindTexture(interop->tex_target, textures[0]);
+    priv->gl.ActiveTexture(GL_TEXTURE0);
+    priv->gl.BindTexture(interop->tex_target, textures[0]);
 
 success:
     if (previous_picture)
@@ -191,8 +197,7 @@ Open(vlc_object_t *obj)
      || !interop->vctx)
         return VLC_EGENERIC;
 
-    const char *extensions = interop->api->vt.GetString(GL_EXTENSIONS);
-    if (!vlc_gl_StrHasToken(extensions, "GL_OES_EGL_image_external"))
+    if (!vlc_gl_HasExtension(interop->gl, "GL_OES_EGL_image_external"))
     {
         msg_Warn(&interop->obj, "GL_OES_EGL_image_external is not available,"
                 " disabling android interop.");
@@ -215,6 +220,14 @@ Open(vlc_object_t *obj)
     priv->previous_texture = NULL;
     priv->stex_attached = false;
 
+#define LOAD_SYMBOL(name) \
+    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name); \
+    assert(priv->gl.name != NULL);
+
+    LOAD_SYMBOL(ActiveTexture);
+    LOAD_SYMBOL(BindTexture);
+    LOAD_SYMBOL(GenTextures);
+
     static const struct vlc_gl_interop_ops ops = {
         .allocate_textures = tc_anop_allocate_textures,
         .update_textures = tc_anop_update,


=====================================
modules/video_output/opengl/interop_cvpx.m
=====================================
@@ -45,13 +45,20 @@ struct priv
     CGLContextObj gl_ctx;
 #endif
     picture_t *last_pic;
+
+    struct {
+        PFNGLACTIVETEXTUREPROC ActiveTexture;
+        PFNGLBINDTEXTUREPROC BindTexture;
+        PFNGLTEXPARAMETERIPROC TexParameteri;
+        PFNGLTEXPARAMETERFPROC TexParameterf;
+    } gl;
 };
 
 #if TARGET_OS_IPHONE
 /* CVOpenGLESTextureCache version (ios) */
 static int
-tc_cvpx_update(const struct vlc_gl_interop *interop, GLuint *textures,
-               const GLsizei *tex_width, const GLsizei *tex_height,
+tc_cvpx_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+               const int32_t tex_width[], const int32_t tex_height[],
                picture_t *pic, const size_t *plane_offset)
 {
     (void) plane_offset;
@@ -93,12 +100,12 @@ tc_cvpx_update(const struct vlc_gl_interop *interop, GLuint *textures,
         }
 
         textures[i] = CVOpenGLESTextureGetName(cvtex);
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-        interop->vt->TexParameteri(interop->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        interop->vt->TexParameterf(interop->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        interop->vt->TexParameterf(interop->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-        interop->vt->BindTexture(interop->tex_target, 0);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        priv->gl.TexParameteri(interop->tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        priv->gl.TexParameterf(interop->tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        priv->gl.TexParameterf(interop->tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        priv->gl.BindTexture(interop->tex_target, 0);
         priv->last_cvtexs[i] = cvtex;
     }
 
@@ -108,8 +115,8 @@ tc_cvpx_update(const struct vlc_gl_interop *interop, GLuint *textures,
 #else
 /* IOSurface version (macos) */
 static int
-tc_cvpx_update(const struct vlc_gl_interop *interop, GLuint *textures,
-               const GLsizei *tex_width, const GLsizei *tex_height,
+tc_cvpx_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+               const int32_t tex_width[], const int32_t tex_height[],
                picture_t *pic, const size_t *plane_offset)
 {
     (void) plane_offset;
@@ -121,8 +128,8 @@ tc_cvpx_update(const struct vlc_gl_interop *interop, GLuint *textures,
 
     for (unsigned i = 0; i < interop->tex_count; ++i)
     {
-        interop->vt->ActiveTexture(GL_TEXTURE0 + i);
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.ActiveTexture(GL_TEXTURE0 + i);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
 
         CGLError err =
             CGLTexImageIOSurface2D(priv->gl_ctx, interop->tex_target,
@@ -184,6 +191,15 @@ Open(vlc_object_t *obj)
     if (unlikely(priv == NULL))
         return VLC_ENOMEM;
 
+#define LOAD_FUNCTION(name) \
+    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name); \
+    assert(priv->gl.name != NULL)
+
+    LOAD_FUNCTION(ActiveTexture);
+    LOAD_FUNCTION(BindTexture);
+    LOAD_FUNCTION(TexParameteri);
+    LOAD_FUNCTION(TexParameterf);
+
 #if TARGET_OS_IPHONE
     const GLenum tex_target = GL_TEXTURE_2D;
 


=====================================
modules/video_output/opengl/interop_dxva2.c
=====================================
@@ -97,8 +97,8 @@ struct glpriv
 };
 
 static int
-GLConvUpdate(const struct vlc_gl_interop *interop, GLuint *textures,
-             const GLsizei *tex_width, const GLsizei *tex_height,
+GLConvUpdate(const struct vlc_gl_interop *interop, uint32_t textures[],
+             const int32_t tex_width[], const int32_t tex_height[],
              picture_t *pic, const size_t *plane_offset)
 {
     VLC_UNUSED(textures); VLC_UNUSED(tex_width); VLC_UNUSED(tex_height); VLC_UNUSED(plane_offset);
@@ -163,8 +163,8 @@ GLConvUpdate(const struct vlc_gl_interop *interop, GLuint *textures,
 }
 
 static int
-GLConvAllocateTextures(const struct vlc_gl_interop *interop, GLuint *textures,
-                       const GLsizei *tex_width, const GLsizei *tex_height)
+GLConvAllocateTextures(const struct vlc_gl_interop *interop, uint32_t textures[],
+                       const int32_t tex_width[], const int32_t tex_height[])
 {
     VLC_UNUSED(tex_width); VLC_UNUSED(tex_height);
     struct glpriv *priv = interop->priv;


=====================================
modules/video_output/opengl/interop_sw.c
=====================================
@@ -48,6 +48,27 @@ struct priv
         picture_t *display_pics[PBO_DISPLAY_COUNT];
         size_t display_idx;
     } pbo;
+
+#define OPENGL_VTABLE_F(X) \
+        X(PFNGLGETERRORPROC,        GetError) \
+        X(PFNGLGETINTEGERVPROC,     GetIntegerv) \
+        X(PFNGLGETSTRINGPROC,       GetString) \
+        \
+        X(PFNGLACTIVETEXTUREPROC,   ActiveTexture) \
+        X(PFNGLBINDTEXTUREPROC,     BindTexture) \
+        X(PFNGLTEXIMAGE2DPROC,      TexImage2D) \
+        X(PFNGLTEXSUBIMAGE2DPROC,   TexSubImage2D) \
+        \
+        X(PFNGLBINDBUFFERPROC,      BindBuffer) \
+        X(PFNGLBUFFERDATAPROC,      BufferData) \
+        X(PFNGLBUFFERSUBDATAPROC,   BufferSubData) \
+        X(PFNGLDELETEBUFFERSPROC,   DeleteBuffers) \
+        X(PFNGLGENBUFFERSPROC,      GenBuffers) \
+        X(PFNGLPIXELSTOREIPROC,     PixelStorei)
+    struct {
+#define DECLARE_SYMBOL(type, name) type name;
+        OPENGL_VTABLE_F(DECLARE_SYMBOL)
+    } gl;
 };
 
 static void
@@ -63,6 +84,7 @@ pbo_picture_destroy(picture_t *pic)
 static picture_t *
 pbo_picture_create(const struct vlc_gl_interop *interop)
 {
+    const struct priv *priv = interop->priv;
     picture_sys_t *picsys = calloc(1, sizeof(*picsys));
     if (unlikely(picsys == NULL))
         return NULL;
@@ -78,8 +100,8 @@ pbo_picture_create(const struct vlc_gl_interop *interop)
         return NULL;
     }
 
-    interop->vt->GenBuffers(pic->i_planes, picsys->buffers);
-    picsys->DeleteBuffers = interop->vt->DeleteBuffers;
+    priv->gl.GenBuffers(pic->i_planes, picsys->buffers);
+    picsys->DeleteBuffers = priv->gl.DeleteBuffers;
 
     /* XXX: needed since picture_NewFromResource override pic planes */
     if (picture_Setup(pic, &interop->fmt_out))
@@ -109,20 +131,21 @@ pbo_picture_create(const struct vlc_gl_interop *interop)
 static int
 pbo_data_alloc(const struct vlc_gl_interop *interop, picture_t *pic)
 {
+    const struct priv *priv = interop->priv;
     picture_sys_t *picsys = pic->p_sys;
 
-    interop->vt->GetError();
+    priv->gl.GetError();
 
     for (int i = 0; i < pic->i_planes; ++i)
     {
-        interop->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER, picsys->buffers[i]);
-        interop->vt->BufferData(GL_PIXEL_UNPACK_BUFFER, picsys->bytes[i], NULL,
+        priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, picsys->buffers[i]);
+        priv->gl.BufferData(GL_PIXEL_UNPACK_BUFFER, picsys->bytes[i], NULL,
                                 GL_DYNAMIC_DRAW);
 
-        if (interop->vt->GetError() != GL_NO_ERROR)
+        if (priv->gl.GetError() != GL_NO_ERROR)
         {
             msg_Err(interop->gl, "could not alloc PBO buffers");
-            interop->vt->DeleteBuffers(i, picsys->buffers);
+            priv->gl.DeleteBuffers(i, picsys->buffers);
             return VLC_EGENERIC;
         }
     }
@@ -145,7 +168,7 @@ pbo_pics_alloc(const struct vlc_gl_interop *interop)
     }
 
     /* turn off pbo */
-    interop->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+    priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
     return VLC_SUCCESS;
 error:
@@ -155,8 +178,8 @@ error:
 }
 
 static int
-tc_pbo_update(const struct vlc_gl_interop *interop, GLuint *textures,
-              const GLsizei *tex_width, const GLsizei *tex_height,
+tc_pbo_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+              const int32_t tex_width[], const int32_t tex_height[],
               picture_t *pic, const size_t *plane_offset)
 {
     (void) plane_offset; assert(plane_offset == NULL);
@@ -170,35 +193,36 @@ tc_pbo_update(const struct vlc_gl_interop *interop, GLuint *textures,
     {
         GLsizeiptr size = pic->p[i].i_lines * pic->p[i].i_pitch;
         const GLvoid *data = pic->p[i].p_pixels;
-        interop->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER,
+        priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER,
                            p_sys->buffers[i]);
-        interop->vt->BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);
+        priv->gl.BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);
 
-        interop->vt->ActiveTexture(GL_TEXTURE0 + i);
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.ActiveTexture(GL_TEXTURE0 + i);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
 
-        interop->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch
+        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, pic->p[i].i_pitch
             * tex_width[i] / (pic->p[i].i_visible_pitch ? pic->p[i].i_visible_pitch : 1));
 
-        interop->vt->TexSubImage2D(interop->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
+        priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
                                    interop->texs[i].format, interop->texs[i].type, NULL);
-        interop->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
     }
 
     /* turn off pbo */
-    interop->vt->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+    priv->gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
     return VLC_SUCCESS;
 }
 
 static int
-tc_common_allocate_textures(const struct vlc_gl_interop *interop, GLuint *textures,
-                            const GLsizei *tex_width, const GLsizei *tex_height)
+tc_common_allocate_textures(const struct vlc_gl_interop *interop, uint32_t textures[],
+                            const int32_t tex_width[], const int32_t tex_height[])
 {
+    const struct priv *priv = interop->priv;
     for (unsigned i = 0; i < interop->tex_count; i++)
     {
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
-        interop->vt->TexImage2D(interop->tex_target, 0, interop->texs[i].internal,
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
+        priv->gl.TexImage2D(interop->tex_target, 0, interop->texs[i].internal,
                                 tex_width[i], tex_height[i], 0, interop->texs[i].format,
                                 interop->texs[i].type, NULL);
     }
@@ -207,7 +231,7 @@ tc_common_allocate_textures(const struct vlc_gl_interop *interop, GLuint *textur
 
 static int
 upload_plane(const struct vlc_gl_interop *interop, unsigned tex_idx,
-             GLsizei width, GLsizei height,
+             int32_t width, int32_t height,
              unsigned pitch, unsigned visible_pitch, const void *pixels)
 {
     struct priv *priv = interop->priv;
@@ -215,7 +239,7 @@ upload_plane(const struct vlc_gl_interop *interop, unsigned tex_idx,
     GLenum tex_type = interop->texs[tex_idx].type;
 
     /* This unpack alignment is the default, but setting it just in case. */
-    interop->vt->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
+    priv->gl.PixelStorei(GL_UNPACK_ALIGNMENT, 4);
 
     if (!priv->has_unpack_subimage)
     {
@@ -244,36 +268,37 @@ upload_plane(const struct vlc_gl_interop *interop, unsigned tex_idx,
                 source += pitch;
                 destination += visible_pitch;
             }
-            interop->vt->TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
+            priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                                        tex_format, tex_type, priv->texture_temp_buf);
         }
         else
         {
-            interop->vt->TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
+            priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                                        tex_format, tex_type, pixels);
         }
     }
     else
     {
-        interop->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, pitch * width / (visible_pitch ? visible_pitch : 1));
-        interop->vt->TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
+        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, pitch * width / (visible_pitch ? visible_pitch : 1));
+        priv->gl.TexSubImage2D(interop->tex_target, 0, 0, 0, width, height,
                                    tex_format, tex_type, pixels);
-        interop->vt->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+        priv->gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
     }
     return VLC_SUCCESS;
 }
 
 static int
-tc_common_update(const struct vlc_gl_interop *interop, GLuint *textures,
-                 const GLsizei *tex_width, const GLsizei *tex_height,
+tc_common_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+                 const int32_t tex_width[], const int32_t tex_height[],
                  picture_t *pic, const size_t *plane_offset)
 {
+    const struct priv *priv = interop->priv;
     int ret = VLC_SUCCESS;
     for (unsigned i = 0; i < interop->tex_count && ret == VLC_SUCCESS; i++)
     {
         assert(textures[i] != 0);
-        interop->vt->ActiveTexture(GL_TEXTURE0 + i);
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.ActiveTexture(GL_TEXTURE0 + i);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
         const void *pixels = plane_offset != NULL ?
                              &pic->p[i].p_pixels[plane_offset[i]] :
                              pic->p[i].p_pixels;
@@ -297,13 +322,24 @@ opengl_interop_generic_deinit(struct vlc_gl_interop *interop)
 int
 opengl_interop_generic_init(struct vlc_gl_interop *interop, bool allow_dr)
 {
+
+    struct priv *priv = calloc(1, sizeof(struct priv));
+    if (unlikely(priv == NULL))
+        return VLC_ENOMEM;
+
+#define LOAD_SYMBOL(type, name) \
+    priv->gl.name = vlc_gl_GetProcAddress(interop->gl, "gl" # name); \
+    assert(priv->gl.name != NULL);
+
+    OPENGL_VTABLE_F(LOAD_SYMBOL);
+
     video_color_space_t space;
     const vlc_fourcc_t *list;
 
     if (vlc_fourcc_IsYUV(interop->fmt_in.i_chroma))
     {
         GLint max_texture_units = 0;
-        interop->vt->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
+        priv->gl.GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units);
         if (max_texture_units < 3)
             return VLC_EGENERIC;
 
@@ -364,34 +400,31 @@ interop_init:
         video_format_FixRgb(&interop->fmt_out);
     }
 
-    struct priv *priv = interop->priv = calloc(1, sizeof(struct priv));
-    if (unlikely(priv == NULL))
-        return VLC_ENOMEM;
-
     static const struct vlc_gl_interop_ops ops = {
         .allocate_textures = tc_common_allocate_textures,
         .update_textures = tc_common_update,
         .close = opengl_interop_generic_deinit,
     };
+    interop->priv = priv;
     interop->ops = &ops;
     interop->fmt_in.i_chroma = i_chroma;
 
     /* OpenGL or OpenGL ES2 with GL_EXT_unpack_subimage ext */
-    priv->has_unpack_subimage =
-        !interop->api->is_gles || vlc_gl_StrHasToken(interop->api->extensions, "GL_EXT_unpack_subimage");
+    priv->has_unpack_subimage = interop->gl->api_type == VLC_OPENGL
+        || vlc_gl_HasExtension(interop->gl, "GL_EXT_unpack_subimage");
 
     if (allow_dr && priv->has_unpack_subimage)
     {
         /* Ensure we do direct rendering / PBO with OpenGL 3.0 or higher. */
-        const unsigned char *ogl_version = interop->vt->GetString(GL_VERSION);
+        const unsigned char *ogl_version = priv->gl.GetString(GL_VERSION);
         const bool glver_ok = strverscmp((const char *)ogl_version, "3.0") >= 0;
 
         const bool has_pbo = glver_ok &&
-            (vlc_gl_StrHasToken(interop->api->extensions, "GL_ARB_pixel_buffer_object") ||
-             vlc_gl_StrHasToken(interop->api->extensions, "GL_EXT_pixel_buffer_object"));
+            (vlc_gl_HasExtension(interop->gl, "GL_ARB_pixel_buffer_object") ||
+             vlc_gl_HasExtension(interop->gl, "GL_EXT_pixel_buffer_object"));
 
-        const bool supports_pbo = has_pbo && interop->vt->BufferData
-            && interop->vt->BufferSubData;
+        const bool supports_pbo = has_pbo && priv->gl.BufferData
+            && priv->gl.BufferSubData;
         if (supports_pbo && pbo_pics_alloc(interop) == VLC_SUCCESS)
         {
             static const struct vlc_gl_interop_ops pbo_ops = {


=====================================
modules/video_output/opengl/interop_vaapi.c
=====================================
@@ -71,6 +71,11 @@ struct priv
         void (*destroyImageKHR)(EGLDisplay, EGLImage image);
     } egl;
 
+    struct
+    {
+        PFNGLBINDTEXTUREPROC BindTexture;
+    } gl;
+
     unsigned fourcc;
     EGLint drm_fourccs[3];
 
@@ -184,8 +189,8 @@ vaegl_init_fourcc(struct priv *priv, unsigned va_fourcc)
 }
 
 static int
-tc_vaegl_update(const struct vlc_gl_interop *interop, GLuint *textures,
-                const GLsizei *tex_width, const GLsizei *tex_height,
+tc_vaegl_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+                const int32_t tex_width[], const int32_t tex_height[],
                 picture_t *pic, const size_t *plane_offset)
 {
     (void) plane_offset;
@@ -255,7 +260,7 @@ tc_vaegl_update(const struct vlc_gl_interop *interop, GLuint *textures,
         if (egl_images[i] == NULL)
             goto error;
 
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
 
         priv->glEGLImageTargetTexture2DOES(interop->tex_target, egl_images[i]);
     }
@@ -270,7 +275,7 @@ tc_vaegl_update(const struct vlc_gl_interop *interop, GLuint *textures,
         if (egl_images[i] == NULL)
             goto error;
 
-        interop->vt->BindTexture(interop->tex_target, textures[i]);
+        priv->gl.BindTexture(interop->tex_target, textures[i]);
 
         priv->glEGLImageTargetTexture2DOES(interop->tex_target, egl_images[i]);
     }
@@ -441,7 +446,7 @@ Open(vlc_object_t *obj)
         goto error;
     }
 
-    if (!vlc_gl_StrHasToken(interop->api->extensions, "GL_OES_EGL_image"))
+    if (!vlc_gl_HasExtension(interop->gl, "GL_OES_EGL_image"))
         goto error;
 
     priv = interop->priv = calloc(1, sizeof(struct priv));
@@ -500,6 +505,11 @@ Open(vlc_object_t *obj)
     if (priv->glEGLImageTargetTexture2DOES == NULL)
         goto error;
 
+    priv->gl.BindTexture =
+        vlc_gl_GetProcAddress(interop->gl, "glBindTexture");
+    if (priv->gl.BindTexture == NULL)
+        goto error;
+
     priv->vadpy = dec_device->opaque;
     assert(priv->vadpy != NULL);
 


=====================================
modules/video_output/opengl/interop_vdpau.c
=====================================
@@ -42,7 +42,7 @@
 #define INTEROP_CALL(fct, ...) \
     _##fct(__VA_ARGS__); \
     { \
-        GLenum ret = interop->vt->GetError(); \
+        GLenum ret = ((converter_sys_t*)interop->priv)->gl.GetError(); \
         if (ret != GL_NO_ERROR) \
         { \
             msg_Err(interop->gl, #fct " failed: 0x%x", ret); \
@@ -62,11 +62,14 @@ static PFNGLVDPAUUNMAPSURFACESNVPROC            _glVDPAUUnmapSurfacesNV;
 
 typedef struct {
     vlc_decoder_device *dec_device;
+    struct {
+        PFNGLGETERRORPROC GetError;
+    } gl;
 } converter_sys_t;
 
 static int
-tc_vdpau_gl_update(const struct vlc_gl_interop *interop, GLuint textures[],
-                   GLsizei const tex_widths[], GLsizei const tex_heights[],
+tc_vdpau_gl_update(const struct vlc_gl_interop *interop, uint32_t textures[],
+                   int32_t const tex_widths[], int32_t const tex_heights[],
                    picture_t *pic, size_t const plane_offsets[])
 {
     VLC_UNUSED(tex_widths);
@@ -107,8 +110,10 @@ tc_vdpau_gl_update(const struct vlc_gl_interop *interop, GLuint textures[],
 static void
 Close(struct vlc_gl_interop *interop)
 {
-    _glVDPAUFiniNV(); assert(interop->vt->GetError() == GL_NO_ERROR);
     converter_sys_t *sys = interop->priv;
+
+    _glVDPAUFiniNV();
+    assert(sys->gl.GetError() == GL_NO_ERROR);
     vlc_decoder_device *dec_device = sys->dec_device;
     vlc_decoder_device_Release(dec_device);
 }
@@ -124,8 +129,7 @@ Open(vlc_object_t *obj)
      || (interop->fmt_in.i_chroma != VLC_CODEC_VDPAU_VIDEO_420
       && interop->fmt_in.i_chroma != VLC_CODEC_VDPAU_VIDEO_422
       && interop->fmt_in.i_chroma != VLC_CODEC_VDPAU_VIDEO_444)
-     || !vlc_gl_StrHasToken(interop->api->extensions, "GL_NV_vdpau_interop")
-     || interop->gl->surface->type != VOUT_WINDOW_TYPE_XID)
+     || !vlc_gl_HasExtension(interop->gl, "GL_NV_vdpau_interop"))
     {
         vlc_decoder_device_Release(dec_device);
         return VLC_EGENERIC;
@@ -138,6 +142,8 @@ Open(vlc_object_t *obj)
         return VLC_ENOMEM;
     }
     sys->dec_device = dec_device;
+    sys->gl.GetError = vlc_gl_GetProcAddress(interop->gl, "glGetError");
+    assert(sys->gl.GetError != NULL);
 
     /* Request to change the input chroma to the core */
     interop->fmt_in.i_chroma = VLC_CODEC_VDPAU_OUTPUT;


=====================================
modules/video_output/opengl/vout_helper.c
=====================================
@@ -133,7 +133,7 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
         (GLint)fmt->i_height > max_tex_size)
         ResizeFormatToGLMaxTexSize(fmt, max_tex_size);
 
-    vgl->interop = vlc_gl_interop_New(gl, api, context, fmt);
+    vgl->interop = vlc_gl_interop_New(gl, context, fmt);
     if (!vgl->interop)
     {
         msg_Err(gl, "Could not create interop");
@@ -203,7 +203,7 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
         goto delete_filters;
     }
 
-    vgl->sub_interop = vlc_gl_interop_NewForSubpictures(gl, api);
+    vgl->sub_interop = vlc_gl_interop_NewForSubpictures(gl);
     if (!vgl->sub_interop)
     {
         msg_Err(gl, "Could not create sub interop");


=====================================
src/Makefile.am
=====================================
@@ -81,6 +81,7 @@ pluginsinclude_HEADERS = \
 	../include/vlc_network.h \
 	../include/vlc_objects.h \
 	../include/vlc_opengl.h \
+	../include/vlc_opengl_interop.h \
 	../include/vlc_picture.h \
 	../include/vlc_picture_fifo.h \
 	../include/vlc_picture_pool.h \


=====================================
src/libvlccore.sym
=====================================
@@ -710,6 +710,7 @@ vlc_gl_Create
 vlc_gl_CreateOffscreen
 vlc_gl_Release
 vlc_gl_Hold
+vlc_gl_HasExtension
 vlc_gl_surface_Create
 vlc_gl_surface_CheckSize
 vlc_gl_surface_Destroy


=====================================
src/video_output/opengl.c
=====================================
@@ -37,6 +37,13 @@ struct vlc_gl_priv_t
 {
     vlc_gl_t gl;
     vlc_atomic_rc_t rc;
+
+    struct {
+        const uint8_t* (*GetString)(uint32_t);
+        const uint8_t* (*GetStringi)(uint32_t, uint32_t);
+        void (*GetIntegerv)(uint32_t, int32_t *);
+        uint32_t (*GetError)();
+    } vt;
 };
 
 static int vlc_gl_start(void *func, bool forced, va_list ap)
@@ -84,6 +91,10 @@ vlc_gl_t *vlc_gl_Create(const struct vout_display_cfg *restrict cfg,
     gl->api_type = api_type;
     gl->surface = wnd;
     gl->device = NULL;
+    glpriv->vt.GetString = NULL;
+    glpriv->vt.GetStringi = NULL;
+    glpriv->vt.GetIntegerv = NULL;
+
     gl->module = vlc_module_load(gl, type, name, true, vlc_gl_start, gl,
                                  cfg->display.width, cfg->display.height);
     if (gl->module == NULL)
@@ -179,6 +190,69 @@ void vlc_gl_Release(vlc_gl_t *gl)
     vlc_object_delete(gl);
 }
 
+bool vlc_gl_HasExtension(vlc_gl_t *gl, const char *extension)
+{
+#define GL_NO_ERROR 0
+#define GL_VERSION 0x1F02
+#define GL_MAJOR_VERSION 0x821B
+#define GL_EXTENSIONS 0x1F03
+#define GL_NUM_EXTENSIONS 0x821D
+    struct vlc_gl_priv_t *glpriv = (struct vlc_gl_priv_t *)gl;
+    /* Cache the OpenGL function before checking. It's not done at OpenGL
+     * provider creation because the context might not be current, and it is
+     * not done at MakeCurrent because MakeCurrent is currently called at each
+     * frames, whereas vlc_gl_HasExtension can be called only during the
+     * client code initialization. */
+    if (glpriv->vt.GetString == NULL && glpriv->vt.GetStringi == NULL)
+    {
+        glpriv->vt.GetString = vlc_gl_GetProcAddress(gl, "glGetString");
+        glpriv->vt.GetIntegerv = vlc_gl_GetProcAddress(gl, "glGetIntegerv");
+        glpriv->vt.GetError = vlc_gl_GetProcAddress(gl, "glGetError");
+
+        int32_t version;
+        /* GL_MAJOR_VERSION is available in every OpenGL and GLES>=3.
+         * https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml
+         * https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glGet.xhtml
+         * It will return a GL_INVALID_ENUM error on GLES<3
+         */
+        glpriv->vt.GetIntegerv(GL_MAJOR_VERSION, &version);
+        uint32_t error = glpriv->vt.GetError();
+
+        if (error != GL_NO_ERROR)
+            version = 2;
+
+        /* Drain the errors before continuing. */
+        while (error != GL_NO_ERROR)
+            error = glpriv->vt.GetError();
+
+        /* glGetStringi is available in OpenGL>=3 and GLES>=3.
+         * https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetString.xhtml
+         * https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGetString.xhtml
+         */
+        if (version >= 3)
+            glpriv->vt.GetStringi = vlc_gl_GetProcAddress(gl, "glGetStringi");
+    }
+
+    /* Fallback to legacy checking mode. */
+    if (glpriv->vt.GetStringi == NULL)
+    {
+        const uint8_t *extensions = glpriv->vt.GetString(GL_EXTENSIONS);
+        return vlc_gl_StrHasToken((const char *)extensions, extension);
+    }
+
+    /* Unfortunately, no order is defined by the standard, so just loop over
+     * the different extensions linearily. */
+    int32_t count = 0;
+    glpriv->vt.GetIntegerv(GL_NUM_EXTENSIONS, &count);
+    for (int i = 0; i < count; ++i)
+    {
+        const uint8_t *name = glpriv->vt.GetStringi(GL_EXTENSIONS, i);
+        if (strcmp((const char *)name, extension) == 0)
+            return true;
+    }
+    return false;
+}
+
 #include <vlc_vout_window.h>
 
 typedef struct vlc_gl_surface



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/40d29a11ac40c349184b85eb9b9882eef705495e...5a010800c45e5d5467428a27134159995ad80cdf

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/40d29a11ac40c349184b85eb9b9882eef705495e...5a010800c45e5d5467428a27134159995ad80cdf
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list