[gl] Switch the glyph cache to using VBOs.

This is a major performance improvement for GL even on non-TNL hardware
(i915), as we get to emit one giant DrawArrays and make GL drivers love us.
Now we're actually faster than not having the glyph cache.

Before glyph cache (HP 945GM):
[  0]       gl             firefox-20090601  238.103  238.195   0.35%    5/6
After:
[  0]       gl             firefox-20090601   68.181   76.735   5.46%    6/6
This commit is contained in:
Eric Anholt 2009-07-24 14:08:55 -07:00
parent ee4138e587
commit 74f3351319

View file

@ -520,50 +520,91 @@ _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
glyph_private->owner = NULL;
}
static void
_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
cairo_gl_composite_setup_t *setup,
int x1, int y1,
int x2, int y2,
cairo_gl_glyph_private_t *glyph,
float *vertices, float *texcoord_src,
float *texcoord_mask)
typedef struct _cairo_gl_glyphs_setup
{
unsigned int vbo_size; /* units of floats */
unsigned int vb_offset; /* units of floats */
unsigned int vertex_size; /* units of floats */
unsigned int num_prims;
float *vb;
cairo_gl_composite_setup_t *composite;
} cairo_gl_glyphs_setup_t;
static void
_cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup)
{
double s, t;
int i;
cairo_surface_attributes_t *src_attributes;
src_attributes = &setup->src.operand.texture.attributes;
if (setup->vb != NULL) {
glUnmapBufferARB (GL_ARRAY_BUFFER_ARB);
setup->vb = NULL;
vertices[0] = x1;
vertices[1] = y1;
vertices[2] = x2;
vertices[3] = y1;
vertices[4] = x2;
vertices[5] = y2;
vertices[6] = x1;
vertices[7] = y2;
if (setup->src.type == OPERAND_TEXTURE) {
for (i = 0; i < 4; i++) {
s = vertices[i * 2];
t = vertices[i * 2 + 1];
cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
texcoord_src[i * 2] = s;
texcoord_src[i * 2 + 1] = t;
if (setup->num_prims != 0) {
glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
setup->num_prims = 0;
}
}
texcoord_mask[0] = glyph->p1.x;
texcoord_mask[1] = glyph->p1.y;
texcoord_mask[2] = glyph->p2.x;
texcoord_mask[3] = glyph->p1.y;
texcoord_mask[4] = glyph->p2.x;
texcoord_mask[5] = glyph->p2.y;
texcoord_mask[6] = glyph->p1.x;
texcoord_mask[7] = glyph->p2.y;
for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++) {
_rtree_unlock (&ctx->glyph_cache[i].rtree,
&ctx->glyph_cache[i].rtree.root);
}
}
glDrawArrays (GL_QUADS, 0, 4);
static void
_cairo_gl_glyphs_emit_vertex (cairo_gl_glyphs_setup_t *setup,
int x, int y, float glyph_x, float glyph_y)
{
int i = 0;
float *vb = &setup->vb[setup->vb_offset];
cairo_surface_attributes_t *src_attributes;
src_attributes = &setup->composite->src.operand.texture.attributes;
vb[i++] = x;
vb[i++] = y;
vb[i++] = glyph_x;
vb[i++] = glyph_y;
if (setup->composite->src.type == OPERAND_TEXTURE) {
double s = x;
double t = y;
cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
vb[i++] = s;
vb[i++] = t;
}
setup->vb_offset += setup->vertex_size;
}
static void
_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
cairo_gl_glyphs_setup_t *setup,
int x1, int y1,
int x2, int y2,
cairo_gl_glyph_private_t *glyph)
{
if (setup->vb != NULL &&
setup->vb_offset + 4 * setup->vertex_size > setup->vbo_size) {
_cairo_gl_flush_glyphs (ctx, setup);
}
if (setup->vb == NULL) {
glBufferDataARB (GL_ARRAY_BUFFER_ARB,
setup->vbo_size * sizeof (GLfloat),
NULL, GL_STREAM_DRAW_ARB);
setup->vb = glMapBufferARB (GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
setup->vb_offset = 0;
}
_cairo_gl_glyphs_emit_vertex (setup, x1, y1, glyph->p1.x, glyph->p1.y);
_cairo_gl_glyphs_emit_vertex (setup, x2, y1, glyph->p2.x, glyph->p1.y);
_cairo_gl_glyphs_emit_vertex (setup, x2, y2, glyph->p2.x, glyph->p2.y);
_cairo_gl_glyphs_emit_vertex (setup, x1, y2, glyph->p1.x, glyph->p2.y);
setup->num_prims++;
}
cairo_int_status_t
@ -583,10 +624,11 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
cairo_gl_glyph_cache_t *cache = NULL;
cairo_status_t status;
int i = 0;
cairo_gl_composite_setup_t setup;
float vertices[4 * 2], texcoord_src[4 * 2], texcoord_mask[4 * 2];
cairo_region_t *clip_region = NULL;
cairo_solid_pattern_t solid_pattern;
cairo_gl_glyphs_setup_t setup;
cairo_gl_composite_setup_t composite_setup;
GLuint vbo = 0;
/* Just let unbounded operators go through the fallback code
* instead of trying to do the fixups here */
@ -660,7 +702,7 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
if (unlikely (status))
return status;
status = _cairo_gl_operand_init (&setup.src, source, dst,
status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
extents.x, extents.y,
extents.x, extents.y,
extents.width, extents.height);
@ -693,7 +735,7 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
if (status != CAIRO_STATUS_SUCCESS)
goto CLEANUP_CONTEXT;
_cairo_gl_set_src_operand (ctx, &setup);
_cairo_gl_set_src_operand (ctx, &composite_setup);
_cairo_scaled_font_freeze_cache (scaled_font);
if (! _cairo_gl_surface_owns_font (dst, scaled_font))
@ -705,15 +747,36 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
scaled_font->surface_backend = &_cairo_gl_surface_backend;
}
glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
/* Create our VBO so that we can accumulate a bunch of glyph primitives
* into one giant DrawArrays.
*/
memset(&setup, 0, sizeof(setup));
setup.composite = &composite_setup;
setup.vertex_size = 4;
if (composite_setup.src.type == OPERAND_TEXTURE)
setup.vertex_size += 2;
setup.vbo_size = num_glyphs * 4 * setup.vertex_size;
if (setup.vbo_size > 4096)
setup.vbo_size = 4096;
glGenBuffersARB (1, &vbo);
glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
glVertexPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
(void *)(uintptr_t)(0));
glEnableClientState (GL_VERTEX_ARRAY);
if (setup.src.type == OPERAND_TEXTURE) {
if (composite_setup.src.type == OPERAND_TEXTURE) {
/* Note that we're packing texcoord 0 after texcoord 1, for
* convenience.
*/
glClientActiveTexture (GL_TEXTURE0);
glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_src);
glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
(void *)(uintptr_t)(4 * sizeof (GLfloat)));
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
}
glClientActiveTexture (GL_TEXTURE1);
glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_mask);
glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
(void *)(uintptr_t)(2 * sizeof (GLfloat)));
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
for (i = 0; i < num_glyphs; i++) {
@ -741,6 +804,9 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
}
if (scaled_glyph->surface->format != last_format) {
/* Switching textures, so flush any queued prims. */
_cairo_gl_flush_glyphs (ctx, &setup);
glActiveTexture (GL_TEXTURE1);
status = cairo_gl_context_get_glyph_cache (ctx,
scaled_glyph->surface->format,
@ -759,16 +825,8 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
goto FINISH;
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
int n;
/* If we were accumulating VBOs like sensible people, this is where
* we'd flush because the cache was full. But we're slackers.
*/
for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) {
_rtree_unlock (&ctx->glyph_cache[n].rtree,
&ctx->glyph_cache[n].rtree.root);
}
/* Cache is full, so flush existing prims and try again. */
_cairo_gl_flush_glyphs (ctx, &setup);
}
status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph);
@ -786,12 +844,12 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
_cairo_gl_emit_glyph_rectangle (ctx, &setup,
x1, y1, x2, y2,
_cairo_gl_glyph_cache_lock (cache, scaled_glyph),
vertices, texcoord_src, texcoord_mask);
_cairo_gl_glyph_cache_lock (cache, scaled_glyph));
}
status = CAIRO_STATUS_SUCCESS;
FINISH:
_cairo_gl_flush_glyphs (ctx, &setup);
_cairo_scaled_font_thaw_cache (scaled_font);
CLEANUP_CONTEXT:
@ -809,9 +867,14 @@ _cairo_gl_surface_show_glyphs (void *abstract_dst,
glActiveTexture (GL_TEXTURE1);
glDisable (GL_TEXTURE_2D);
if (vbo != 0) {
glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
glDeleteBuffersARB (1, &vbo);
}
_cairo_gl_context_release (ctx);
_cairo_gl_operand_destroy (&setup.src);
_cairo_gl_operand_destroy (&composite_setup.src);
*remaining_glyphs = num_glyphs - i;
return status;