[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