From 674a1625f99ab6e8db988430e9285b3dac2ff5d1 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 29 Oct 2011 19:38:33 +0200 Subject: [PATCH] vo_gl: use a single texture for libass-rendered bitmaps Use the bitmap packer code split from vo_vdpau to pack all libass-generated subtitle bitmaps into one texture. This reportedly improves performance in a lot in some cases on OS X at least. One issue with the old code was that "large" images caused each sub-image to be created and rendered as a new texture. "Large" in this case means larger than 32x32 pixels, which actually isn't very large with screen sizes beyond 1500x1000 pixels. This means rendering a simple subtitle for a fullscreened video may allocate many small textures, one for each glyph. The new code also uses less deprecated OpenGL features and draws all sub-images in one batch. There are two possible issues the new code could cause: - Drivers could have performance issues with the larger texture sizes and the number of glTexSubImage2D calls on it. - There is only one EOSD texture, which could become full (the size is increased on demand, but that is restricted by driver texture size limitations). Modified by Uoti Urpala to work with a different bitmap packer interface than the commit was originally created against. --- libvo/gl_common.c | 6 ++ libvo/gl_common.h | 7 ++ libvo/vo_gl.c | 274 +++++++++++++++++++++++++----------------------------- 3 files changed, 140 insertions(+), 147 deletions(-) diff --git a/libvo/gl_common.c b/libvo/gl_common.c index 3b723493db..7f0c5f3587 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -361,6 +361,12 @@ static const extfunc_desc_t extfuncs[] = { DEF_FUNC_DESC(ColorMask), DEF_FUNC_DESC(ReadPixels), DEF_FUNC_DESC(ReadBuffer), + DEF_FUNC_DESC(VertexPointer), + DEF_FUNC_DESC(ColorPointer), + DEF_FUNC_DESC(TexCoordPointer), + DEF_FUNC_DESC(DrawArrays), + DEF_FUNC_DESC(EnableClientState), + DEF_FUNC_DESC(DisableClientState), DEF_EXT_DESC(GenBuffers, NULL, ("glGenBuffers", "glGenBuffersARB")), diff --git a/libvo/gl_common.h b/libvo/gl_common.h index 46a91e27d3..762e096cdf 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -469,6 +469,13 @@ struct GL { void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid *); void (GLAPIENTRY *ReadBuffer)(GLenum); + void (GLAPIENTRY *VertexPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *ColorPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *TexCoordPointer)(GLint, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *DrawArrays)(GLenum, GLint, GLsizei); + void (GLAPIENTRY *EnableClientState)(GLenum); + void (GLAPIENTRY *DisableClientState)(GLenum); + // OpenGL extension functions void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *); diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 163ce894cb..aa303ad410 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -37,6 +37,7 @@ #include "geometry.h" #include "osd.h" #include "sub/sub.h" +#include "bitmap_packer.h" #include "gl_common.h" #include "aspect.h" @@ -48,19 +49,17 @@ static int preinit_nosw(struct vo *vo, const char *arg); //! How many parts the OSD may consist of at most #define MAX_OSD_PARTS 20 -#define LARGE_EOSD_TEX_SIZE 512 -#define TINYTEX_SIZE 16 -#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE / TINYTEX_SIZE) -#define TINYTEX_MAX (TINYTEX_COLS * TINYTEX_COLS) -#define SMALLTEX_SIZE 32 -#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE / SMALLTEX_SIZE) -#define SMALLTEX_MAX (SMALLTEX_COLS * SMALLTEX_COLS) - //for gl_priv.use_yuv #define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE)) #define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS))) #define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT)) +struct vertex_eosd { + float x, y; + uint8_t color[4]; + float u, v; +}; + struct gl_priv { MPGLContext *glctx; GL *gl; @@ -71,17 +70,18 @@ struct gl_priv { GLuint osdtex[MAX_OSD_PARTS]; //! Alpha textures for OSD GLuint osdatex[MAX_OSD_PARTS]; - GLuint *eosdtex; - GLuint largeeosdtex[2]; + GLuint eosd_texture; + int eosd_texture_width, eosd_texture_height; + struct bitmap_packer *eosd; + struct vertex_eosd *eosd_va; + int eosd_render_count; + unsigned int bitmap_id; + unsigned int bitmap_pos_id; //! Display lists that draw the OSD parts GLuint osdDispList[MAX_OSD_PARTS]; GLuint osdaDispList[MAX_OSD_PARTS]; - GLuint eosdDispList; - unsigned int bitmap_id; - unsigned int bitmap_pos_id; //! How many parts the OSD currently consists of int osdtexCnt; - int eosdtexCnt; int osd_color; int use_ycbcr; @@ -277,154 +277,128 @@ static void clearOSD(struct vo *vo) } /** - * \brief remove textures, display list and free memory used by EOSD - */ -static void clearEOSD(struct vo *vo) -{ - struct gl_priv *p = vo->priv; - GL *gl = p->gl; - - if (p->eosdDispList) - gl->DeleteLists(p->eosdDispList, 1); - p->eosdDispList = 0; - if (p->eosdtexCnt) - gl->DeleteTextures(p->eosdtexCnt, p->eosdtex); - p->eosdtexCnt = 0; - free(p->eosdtex); - p->eosdtex = NULL; -} - -static inline int is_tinytex(ASS_Image *i, int tinytexcur) -{ - return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE - && tinytexcur < TINYTEX_MAX; -} - -static inline int is_smalltex(ASS_Image *i, int smalltexcur) -{ - return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE - && smalltexcur < SMALLTEX_MAX; -} - -static inline void tinytex_pos(int tinytexcur, int *x, int *y) -{ - *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE; - *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE; -} - -static inline void smalltex_pos(int smalltexcur, int *x, int *y) -{ - *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE; - *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE; -} - -/** * \brief construct display list from ass image list * \param img image list to create OSD from. - * A value of NULL has the same effect as clearEOSD() */ static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) { struct gl_priv *p = vo->priv; GL *gl = p->gl; - int sx, sy; - int tinytexcur = 0; - int smalltexcur = 0; - GLuint *curtex; - GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; - ASS_Image *img = imgs->imgs; - ASS_Image *i; - if (imgs->bitmap_pos_id == p->bitmap_pos_id) - return; // contents are exactly same as before - if (imgs->bitmap_id == p->bitmap_id) - goto skip_upload; // bitmaps are the same, just position changed - clearEOSD(vo); - if (!img) return; - if (!p->largeeosdtex[0]) { - gl->GenTextures(2, p->largeeosdtex); - for (int n = 0; n < 2; n++) { - gl->BindTexture(p->target, p->largeeosdtex[n]); - glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, - LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); - } - } - for (i = img; i; i = i->next) { - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - if (is_tinytex(i, tinytexcur)) - tinytexcur++; - else if (is_smalltex(i, smalltexcur)) - smalltexcur++; - else - p->eosdtexCnt++; - } - mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n", - tinytexcur, smalltexcur, p->eosdtexCnt); - if (p->eosdtexCnt) { - p->eosdtex = calloc(p->eosdtexCnt, sizeof(GLuint)); - gl->GenTextures(p->eosdtexCnt, p->eosdtex); - } - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - continue; + + if (!p->eosd_texture) + gl->GenTextures(1, &p->eosd_texture); + + gl->BindTexture(p->target, p->eosd_texture); + + p->eosd_render_count = 0; + bool need_upload = false; + + if (imgs->bitmap_id != p->bitmap_id) { + need_upload = true; + int res = packer_pack_from_assimg(p->eosd, imgs->imgs); + if (res < 0) { + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] subtitle bitmaps do not fit in maximum texture\n"); + return; } - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); + if (res == 1) { + mp_msg(MSGT_VO, MSGL_V, "[gl] Allocating a %dx%d texture for " + "subtitle bitmaps.\n", p->eosd->w, p->eosd->h); + texSize(vo, p->eosd->w, p->eosd->h, + &p->eosd_texture_width, &p->eosd_texture_height); + // xxx it doesn't need to be cleared, that's a waste of time glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, - GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); + GL_UNSIGNED_BYTE, GL_NEAREST, + p->eosd_texture_width, + p->eosd_texture_height, 0); } - glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, - i->stride, x, y, i->w, i->h, 0); } - p->eosdDispList = gl->GenLists(1); -skip_upload: - gl->NewList(p->eosdDispList, GL_COMPILE); - tinytexcur = smalltexcur = 0; - for (i = img, curtex = p->eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) + + // 2 triangles primitives per quad = 6 vertices per quad + // not using GL_QUADS, as it is deprecated in OpenGL 3.x and later + p->eosd_va = talloc_realloc_size(p->eosd, p->eosd_va, + p->eosd->count * + sizeof(struct vertex_eosd) * 6); + + float eosd_w = p->eosd_texture_width; + float eosd_h = p->eosd_texture_height; + + if (p->use_rectangle == 1) + eosd_w = eosd_h = 1.0f; + + ASS_Image *i = imgs->imgs; + struct pos *spos = p->eosd->result; + for (int n = 0; n < p->eosd->count; n++, i = i->next) { + if (i->w == 0 || i->h == 0) continue; - gl->Color4ub(i->color >> 24, (i->color >> 16) & 0xff, - (i->color >> 8) & 0xff, 255 - (i->color & 0xff)); - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - gl->BindTexture(p->target, p->largeeosdtex[1]); - smalltexcur++; - } else { - texSize(vo, i->w, i->h, &sx, &sy); - gl->BindTexture(p->target, *curtex++); - } - glDrawTex(gl, i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy, - p->use_rectangle == 1, 0, 0); + + if (need_upload) + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, + i->stride, spos[n].x, spos[n].y, i->w, i->h, 0); + + uint8_t color[4] = { i->color >> 24, (i->color >> 16) & 0xff, + (i->color >> 8) & 0xff, 255 - (i->color & 0xff) }; + + float x0 = i->dst_x; + float y0 = i->dst_y; + float x1 = i->dst_x + i->w; + float y1 = i->dst_y + i->h; + float tx0 = spos[n].x / eosd_w; + float ty0 = spos[n].y / eosd_h; + float tx1 = (spos[n].x + i->w) / eosd_w; + float ty1 = (spos[n].y + i->h) / eosd_h; + +#define COLOR_INIT {color[0], color[1], color[2], color[3]} + struct vertex_eosd *va = &p->eosd_va[p->eosd_render_count * 6]; + va[0] = (struct vertex_eosd) { x0, y0, COLOR_INIT, tx0, ty0 }; + va[1] = (struct vertex_eosd) { x0, y1, COLOR_INIT, tx0, ty1 }; + va[2] = (struct vertex_eosd) { x1, y0, COLOR_INIT, tx1, ty0 }; + va[3] = (struct vertex_eosd) { x1, y1, COLOR_INIT, tx1, ty1 }; + va[4] = va[2]; + va[5] = va[1]; +#undef COLOR_INIT + p->eosd_render_count++; } - gl->EndList(); + gl->BindTexture(p->target, 0); p->bitmap_id = imgs->bitmap_id; p->bitmap_pos_id = imgs->bitmap_pos_id; } +// Note: relies on state being setup, like projection matrix and blending +static void drawEOSD(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (p->eosd_render_count == 0) + return; + + gl->BindTexture(p->target, p->eosd_texture); + + struct vertex_eosd *va = p->eosd_va; + size_t stride = sizeof(struct vertex_eosd); + + gl->VertexPointer(2, GL_FLOAT, stride, &va[0].x); + gl->ColorPointer(4, GL_UNSIGNED_BYTE, stride, &va[0].color[0]); + gl->TexCoordPointer(2, GL_FLOAT, stride, &va[0].u); + + gl->EnableClientState(GL_VERTEX_ARRAY); + gl->EnableClientState(GL_TEXTURE_COORD_ARRAY); + gl->EnableClientState(GL_COLOR_ARRAY); + + gl->DrawArrays(GL_TRIANGLES, 0, p->eosd_render_count * 6); + + gl->DisableClientState(GL_VERTEX_ARRAY); + gl->DisableClientState(GL_TEXTURE_COORD_ARRAY); + gl->DisableClientState(GL_COLOR_ARRAY); + + gl->BindTexture(p->target, 0); +} + /** * \brief uninitialize OpenGL context, freeing textures, buffers etc. */ @@ -443,11 +417,11 @@ static void uninitGl(struct vo *vo) gl->DeleteTextures(i, p->default_texs); p->default_texs[0] = 0; clearOSD(vo); - clearEOSD(vo); + if (p->eosd_texture) + gl->DeleteTextures(1, &p->eosd_texture); p->bitmap_id = p->bitmap_pos_id = 0; - if (p->largeeosdtex[0]) - gl->DeleteTextures(2, p->largeeosdtex); - p->largeeosdtex[0] = 0; + p->eosd->w = p->eosd->h = 0; + p->eosd_texture = 0; if (gl->DeleteBuffers && p->buffer) gl->DeleteBuffers(1, &p->buffer); p->buffer = 0; @@ -627,6 +601,10 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) update_yuvconv(vo); } + GLint max_texture_size; + gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); + p->eosd->w_max = p->eosd->h_max = max_texture_size; + resize(vo, d_width, d_height); gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -776,7 +754,7 @@ static void do_render_osd(struct vo *vo, int type) GL *gl = p->gl; int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0; - int draw_eosd = (type & RENDER_EOSD) && p->eosdDispList; + int draw_eosd = (type & RENDER_EOSD); if (!draw_osd && !draw_eosd) return; // set special rendering parameters @@ -789,7 +767,7 @@ static void do_render_osd(struct vo *vo, int type) gl->Enable(GL_BLEND); if (draw_eosd) { gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl->CallList(p->eosdDispList); + drawEOSD(vo); } if (draw_osd) { gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, @@ -1227,6 +1205,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw, .osd_color = 0xffffff, }; + p->eosd = talloc_zero(vo, struct bitmap_packer); + //essentially unused; for legacy warnings only int user_colorspace = 0; int levelconv = -1; -- 2.11.4.GIT