[vlc-commits] opengl: add PBO support for old OpenGL versions

Thomas Guillem git at videolan.org
Fri Mar 10 18:50:19 CET 2017


vlc | branch: master | Thomas Guillem <thomas at gllm.fr> | Thu Mar  9 15:12:03 2017 +0100| [8663561d3f71595ebf116f17279a495b67cac713] | committer: Thomas Guillem

opengl: add PBO support for old OpenGL versions

Direct rendering is already present using PBO (GL_EXT_pixel_buffer_object) and
persistent buffers (GL_EXT_buffer_storage). But these extensions are available
only since OpenGL 4.4.

For older OpenGL versions (OpenGL 2.0 and OpenGL ES3 (or as an extension since
OpenGL ES2)), we can still improve quite a bit the performances by using a
non-mapped PBO and updating it via glBufferSubData. This won't be true direct
rendering since you must copy the data from the decoded picture to the GPU.

By using 2 PBOs (double buffering), we make sure that we can upload to the GPU
while the previous picture is beeing rendered.

Performances gains measured by playing 4K30fps video during 1 minute: 7 seconds
spent in vout->prepare() instead of 10 seconds. This result may vary with the
platform/device/drivers.

Thanks to sesse for the advices.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=8663561d3f71595ebf116f17279a495b67cac713
---

 modules/video_output/opengl/converters.c  | 148 +++++++++++++++++++++++++++++-
 modules/video_output/opengl/internal.h    |  10 ++
 modules/video_output/opengl/vout_helper.c |   3 +
 3 files changed, 157 insertions(+), 4 deletions(-)

diff --git a/modules/video_output/opengl/converters.c b/modules/video_output/opengl/converters.c
index 5e761ef..5c5d5ea 100644
--- a/modules/video_output/opengl/converters.c
+++ b/modules/video_output/opengl/converters.c
@@ -47,14 +47,17 @@
 #define NEED_GL_EXT_unpack_subimage
 #endif
 
-#ifdef VLCGL_HAS_MAP_PERSISTENT
+#ifdef VLCGL_HAS_PBO
+#define PBO_DISPLAY_COUNT 2 /* Double buffering */
 struct picture_sys_t
 {
     const opengl_tex_converter_t *tc;
     GLuint      buffers[PICTURE_PLANE_MAX];
     size_t      bytes[PICTURE_PLANE_MAX];
+#ifdef VLCGL_HAS_MAP_PERSISTENT
     GLsync      fence;
     unsigned    index;
+#endif
 };
 #endif
 
@@ -63,12 +66,18 @@ struct priv
     bool   has_unpack_subimage;
     void * texture_temp_buf;
     size_t texture_temp_buf_size;
+#ifdef VLCGL_HAS_PBO
+    struct {
+        picture_t *display_pics[PBO_DISPLAY_COUNT];
+        size_t display_idx;
+    } pbo;
 #ifdef VLCGL_HAS_MAP_PERSISTENT
     struct {
         picture_t *pics[VLCGL_PICTURE_MAX];
         unsigned long long list;
     } persistent;
 #endif
+#endif
 };
 
 #if !defined(USE_OPENGL_ES2)
@@ -491,7 +500,14 @@ opengl_fragment_shader_init(opengl_tex_converter_t *tc, GLenum tex_target,
     return fragment_shader;
 }
 
-#ifdef VLCGL_HAS_MAP_PERSISTENT
+#ifdef VLCGL_HAS_PBO
+# ifndef GL_PIXEL_UNPACK_BUFFER
+#  define GL_PIXEL_UNPACK_BUFFER 0x88EC
+# endif
+# ifndef GL_DYNAMIC_DRAW
+#  define GL_DYNAMIC_DRAW 0x88E8
+# endif
+
 static picture_t *
 pbo_picture_create(const opengl_tex_converter_t *tc, const video_format_t *fmt,
                    void (*pf_destroy)(picture_t *))
@@ -533,6 +549,113 @@ pbo_picture_create(const opengl_tex_converter_t *tc, const video_format_t *fmt,
 }
 
 static int
+pbo_data_alloc(const opengl_tex_converter_t *tc, picture_t *pic)
+{
+    picture_sys_t *picsys = pic->p_sys;
+
+    glGetError();
+    tc->api->GenBuffers(pic->i_planes, picsys->buffers);
+
+    for (int i = 0; i < pic->i_planes; ++i)
+    {
+        tc->api->BindBuffer(GL_PIXEL_UNPACK_BUFFER, picsys->buffers[i]);
+        tc->api->BufferData(GL_PIXEL_UNPACK_BUFFER, picsys->bytes[i], NULL,
+                            GL_DYNAMIC_DRAW);
+
+        if (glGetError() != GL_NO_ERROR)
+        {
+            msg_Err(tc->gl, "could not alloc PBO buffers");
+            tc->api->DeleteBuffers(i, picsys->buffers);
+            return VLC_EGENERIC;
+        }
+    }
+    return VLC_SUCCESS;
+}
+
+static void
+picture_pbo_destroy_cb(picture_t *pic)
+{
+    picture_sys_t *picsys = pic->p_sys;
+    const opengl_tex_converter_t *tc = picsys->tc;
+
+    if (picsys->buffers[0] != 0)
+        tc->api->DeleteBuffers(pic->i_planes, picsys->buffers);
+    free(picsys);
+    free(pic);
+}
+
+static int
+pbo_pics_alloc(const opengl_tex_converter_t *tc, const video_format_t *fmt)
+{
+    struct priv *priv = tc->priv;
+    for (size_t i = 0; i < PBO_DISPLAY_COUNT; ++i)
+    {
+        picture_t *pic = priv->pbo.display_pics[i] =
+            pbo_picture_create(tc, fmt, picture_pbo_destroy_cb);
+        if (pic == NULL)
+            goto error;
+
+        if (pbo_data_alloc(tc, pic) != VLC_SUCCESS)
+            goto error;
+    }
+
+    /* turn off pbo */
+    tc->api->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    return VLC_SUCCESS;
+error:
+    for (size_t i = 0; i < PBO_DISPLAY_COUNT && priv->pbo.display_pics[i]; ++i)
+        picture_Release(priv->pbo.display_pics[i]);
+    return VLC_EGENERIC;
+}
+
+static int
+tc_pbo_update(const opengl_tex_converter_t *tc, GLuint *textures,
+              const GLsizei *tex_width, const GLsizei *tex_height,
+              picture_t *pic, const size_t *plane_offset)
+{
+    (void) plane_offset; assert(plane_offset == NULL);
+    struct priv *priv = tc->priv;
+
+    picture_t *display_pic = priv->pbo.display_pics[priv->pbo.display_idx];
+    priv->pbo.display_idx = (priv->pbo.display_idx + 1) % PBO_DISPLAY_COUNT;
+
+    for (int i = 0; i < pic->i_planes; i++)
+    {
+        GLsizeiptr size = pic->p[i].i_visible_lines * pic->p[i].i_visible_pitch;
+        const GLvoid *data = pic->p[i].p_pixels;
+        tc->api->BindBuffer(GL_PIXEL_UNPACK_BUFFER,
+                            display_pic->p_sys->buffers[i]);
+        tc->api->BufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, size, data);
+
+        glActiveTexture(GL_TEXTURE0 + i);
+        glClientActiveTexture(GL_TEXTURE0 + i);
+        glBindTexture(tc->tex_target, textures[i]);
+
+        glPixelStorei(GL_UNPACK_ROW_LENGTH,
+                      pic->p[i].i_pitch / pic->p[i].i_pixel_pitch);
+
+        glTexSubImage2D(tc->tex_target, 0, 0, 0, tex_width[i], tex_height[i],
+                        tc->texs[i].format, tc->texs[i].type, NULL);
+    }
+
+    /* turn off pbo */
+    tc->api->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+
+    return VLC_SUCCESS;
+}
+
+static void
+tc_pbo_release(const opengl_tex_converter_t *tc)
+{
+    struct priv *priv = tc->priv;
+    for (size_t i = 0; i < PBO_DISPLAY_COUNT && priv->pbo.display_pics[i]; ++i)
+        picture_Release(priv->pbo.display_pics[i]);
+    free(tc->priv);
+}
+
+#ifdef VLCGL_HAS_MAP_PERSISTENT
+static int
 persistent_map(const opengl_tex_converter_t *tc, picture_t *pic)
 {
     picture_sys_t *picsys = pic->p_sys;
@@ -743,6 +866,7 @@ error:
     return NULL;
 }
 #endif /* VLCGL_HAS_MAP_PERSISTENT */
+#endif /* VLCGL_HAS_PBO */
 
 static int
 tc_common_allocate_textures(const opengl_tex_converter_t *tc, GLuint *textures,
@@ -972,14 +1096,17 @@ generic_init(const video_format_t *fmt, opengl_tex_converter_t *tc,
 
     if (allow_dr)
     {
-#ifdef VLCGL_HAS_MAP_PERSISTENT
+        bool supports_map_persistent = false;
+
         const bool has_pbo =
             HasExtension(tc->glexts, "GL_ARB_pixel_buffer_object") ||
             HasExtension(tc->glexts, "GL_EXT_pixel_buffer_object");
+
+#ifdef VLCGL_HAS_MAP_PERSISTENT
         const bool has_bs =
             HasExtension(tc->glexts, "GL_ARB_buffer_storage") ||
             HasExtension(tc->glexts, "GL_EXT_buffer_storage");
-        const bool supports_map_persistent = has_pbo && has_bs && tc->api->BufferStorage
+        supports_map_persistent = has_pbo && has_bs && tc->api->BufferStorage
             && tc->api->MapBufferRange && tc->api->FlushMappedBufferRange
             && tc->api->UnmapBuffer && tc->api->FenceSync && tc->api->DeleteSync
             && tc->api->ClientWaitSync;
@@ -991,6 +1118,19 @@ generic_init(const video_format_t *fmt, opengl_tex_converter_t *tc,
             msg_Dbg(tc->gl, "MAP_PERSISTENT support (direct rendering) enabled");
         }
 #endif
+#ifdef VLCGL_HAS_PBO
+        if (!supports_map_persistent)
+        {
+            const bool supports_pbo = has_pbo && tc->api->BufferData
+                && tc->api->BufferSubData;
+            if (supports_pbo && pbo_pics_alloc(tc, fmt) == VLC_SUCCESS)
+            {
+                tc->pf_update  = tc_pbo_update;
+                tc->pf_release = tc_pbo_release;
+                msg_Err(tc->gl, "PBO support enabled");
+            }
+        }
+#endif
     }
 
 #ifdef NEED_GL_EXT_unpack_subimage
diff --git a/modules/video_output/opengl/internal.h b/modules/video_output/opengl/internal.h
index 2353f25..4115cda 100644
--- a/modules/video_output/opengl/internal.h
+++ b/modules/video_output/opengl/internal.h
@@ -28,9 +28,13 @@
 #   define PRECISION "precision highp float;"
 #   define VLCGL_PICTURE_MAX 128
 #   define glClientActiveTexture(x)
+#   define VLCGL_HAS_PBO /* PBO present as an OpenGlES 2 extension */
 #else
 #   define GLSL_VERSION "120"
 #   define VLCGL_PICTURE_MAX 128
+#   ifdef GL_VERSION_2_0
+#       define VLCGL_HAS_PBO
+#   endif
 #   ifdef GL_VERSION_4_4
 #       define VLCGL_HAS_MAP_PERSISTENT
 #   endif
@@ -66,6 +70,9 @@
 #   define PFNGLGENBUFFERSPROC               typeof(glGenBuffers)*
 #   define PFNGLBINDBUFFERPROC               typeof(glBindBuffer)*
 #   define PFNGLBUFFERDATAPROC               typeof(glBufferData)*
+#   ifdef VLCGL_HAS_PBO
+#    define PFNGLBUFFERSUBDATAPROC           typeof(glBufferSubData)*
+#   endif
 #   ifdef VLCGL_HAS_MAP_PERSISTENT
 #    define PFNGLBUFFERSTORAGEPROC           typeof(glBufferStorage)*
 #    define PFNGLMAPBUFFERRANGEPROC          typeof(glMapBufferRange)*
@@ -119,6 +126,9 @@ typedef struct {
     PFNGLGENBUFFERSPROC    GenBuffers;
     PFNGLBINDBUFFERPROC    BindBuffer;
     PFNGLBUFFERDATAPROC    BufferData;
+#ifdef VLCGL_HAS_PBO
+    PFNGLBUFFERSUBDATAPROC          BufferSubData;
+#endif
 #ifdef VLCGL_HAS_MAP_PERSISTENT
     PFNGLBUFFERSTORAGEPROC          BufferStorage;
     PFNGLMAPBUFFERRANGEPROC         MapBufferRange;
diff --git a/modules/video_output/opengl/vout_helper.c b/modules/video_output/opengl/vout_helper.c
index ff50510..f345c5d 100644
--- a/modules/video_output/opengl/vout_helper.c
+++ b/modules/video_output/opengl/vout_helper.c
@@ -619,6 +619,9 @@ vout_display_opengl_t *vout_display_opengl_New(video_format_t *fmt,
     GET_PROC_ADDR(BufferData, true);
     GET_PROC_ADDR(DeleteBuffers, true);
 
+#ifdef VLCGL_HAS_PBO
+    GET_PROC_ADDR(BufferSubData, false);
+#endif
 #ifdef VLCGL_HAS_MAP_PERSISTENT
     GET_PROC_ADDR(BufferStorage, false);
     GET_PROC_ADDR(MapBufferRange, false);



More information about the vlc-commits mailing list