Commit 8663561d authored by Thomas Guillem's avatar 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.
parent df65a321
......@@ -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 *))
......@@ -532,6 +548,113 @@ pbo_picture_create(const opengl_tex_converter_t *tc, const video_format_t *fmt,
return pic;
}
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)
{
......@@ -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;
......@@ -990,6 +1117,19 @@ generic_init(const video_format_t *fmt, opengl_tex_converter_t *tc,
tc->pf_release = tc_persistent_release;
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
}
......
......@@ -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;
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment