0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00

vo_gl: use generic eosd code

This is an experiment.

The change probably doesn't matter much.

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.
On the other hand, the old code could be fixed by tuning the texture sizes
for "modern" work loads.

Also, the new code 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 (it's enlarged
  on demand, but restricted by driver texture size limitations)

It has been reported that this is faster on OSX with ATI GPUs than the old
code.
This commit is contained in:
wm4 2011-10-29 19:38:33 +02:00
parent 4010dd0b1a
commit adc85b0031
3 changed files with 129 additions and 150 deletions

View File

@ -360,6 +360,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")),

View File

@ -463,6 +463,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 *);

View File

@ -38,6 +38,7 @@
#include "osd.h"
#include "sub/font_load.h"
#include "sub/sub.h"
#include "eosd_packer.h"
#include "gl_common.h"
#include "aspect.h"
@ -49,19 +50,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;
@ -74,17 +73,17 @@ struct gl_priv {
//! Alpha textures for OSD
GLuint osdatex[MAX_OSD_PARTS];
#endif
GLuint *eosdtex;
GLuint largeeosdtex[2];
GLuint eosd_texture;
int eosd_texture_width, eosd_texture_height;
struct eosd_packer *eosd;
struct vertex_eosd *eosd_va;
//! Display lists that draw the OSD parts
GLuint osdDispList[MAX_OSD_PARTS];
#ifndef FAST_OSD
GLuint osdaDispList[MAX_OSD_PARTS];
#endif
GLuint eosdDispList;
//! How many parts the OSD currently consists of
int osdtexCnt;
int eosdtexCnt;
int osd_color;
int use_ycbcr;
@ -284,151 +283,112 @@ static void clearOSD(struct vo *vo)
p->osdtexCnt = 0;
}
/**
* \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;
bool need_repos, need_upload, need_allocate;
eosd_packer_generate(p->eosd, imgs, &need_repos, &need_upload,
&need_allocate);
if (imgs->changed == 0) // there are elements, but they are unchanged
if (!need_repos)
return;
if (img && imgs->changed == 1) // there are elements, but they just moved
goto skip_upload;
clearEOSD(vo);
if (!img)
if (!p->eosd_texture)
gl->GenTextures(1, &p->eosd_texture);
gl->BindTexture(p->target, p->eosd_texture);
if (need_allocate) {
texSize(vo, p->eosd->surface.w, p->eosd->surface.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,
GL_NEAREST, p->eosd_texture_width,
p->eosd_texture_height, 0);
}
// 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->targets_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;
for (int n = 0; n < p->eosd->targets_count; n++) {
struct eosd_target *target = &p->eosd->targets[n];
ASS_Image *i = target->ass_img;
if (need_upload) {
glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap,
i->stride, target->source.x0, target->source.y0,
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 = target->dest.x0;
float y0 = target->dest.y0;
float x1 = target->dest.x1;
float y1 = target->dest.y1;
float tx0 = target->source.x0 / eosd_w;
float ty0 = target->source.y0 / eosd_h;
float tx1 = target->source.x1 / eosd_w;
float ty1 = target->source.y1 / eosd_h;
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
struct vertex_eosd *va = &p->eosd_va[n * 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
}
gl->BindTexture(p->target, 0);
}
// 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->targets_count == 0)
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 (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++);
glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA,
GL_UNSIGNED_BYTE, scale_type, sx, sy, 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)
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);
}
gl->EndList();
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->targets_count * 6);
gl->DisableClientState(GL_VERTEX_ARRAY);
gl->DisableClientState(GL_TEXTURE_COORD_ARRAY);
gl->DisableClientState(GL_COLOR_ARRAY);
gl->BindTexture(p->target, 0);
}
@ -450,10 +410,10 @@ static void uninitGl(struct vo *vo)
gl->DeleteTextures(i, p->default_texs);
p->default_texs[0] = 0;
clearOSD(vo);
clearEOSD(vo);
if (p->largeeosdtex[0])
gl->DeleteTextures(2, p->largeeosdtex);
p->largeeosdtex[0] = 0;
if (p->eosd_texture)
gl->DeleteTextures(1, &p->eosd_texture);
eosd_packer_reinit(p->eosd, 0, 0);
p->eosd_texture = 0;
if (gl->DeleteBuffers && p->buffer)
gl->DeleteBuffers(1, &p->buffer);
p->buffer = 0;
@ -633,6 +593,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);
eosd_packer_reinit(p->eosd, max_texture_size, max_texture_size);
resize(vo, d_width, d_height);
gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@ -786,7 +750,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
@ -799,7 +763,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,
@ -1239,6 +1203,8 @@ static int preinit_internal(struct vo *vo, const char *arg, int allow_sw,
.osd_color = 0xffffff,
};
p->eosd = eosd_packer_create(vo);
//essentially unused; for legacy warnings only
int user_colorspace = 0;
int levelconv = -1;