From 74f33513194ec3d8e69a21aa71aa7fc854415e83 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 24 Jul 2009 14:08:55 -0700 Subject: [PATCH] [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 --- src/cairo-gl-glyphs.c | 177 ++++++++++++++++++++++++++++-------------- 1 file changed, 120 insertions(+), 57 deletions(-) diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index 7fe0123ce..4ab1a03fe 100644 --- a/src/cairo-gl-glyphs.c +++ b/src/cairo-gl-glyphs.c @@ -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;