From bb156970defbd7479d512ab5b5aeb78fd1fefd75 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 25 Mar 2009 14:18:00 +0000 Subject: [PATCH 1/4] Render pango layouts with vertex buffers When rendering a pango layout CoglPangoRenderer now records the operations into a list called a CoglPangoDisplayList. The entries in the list are either glyph renderings from a texture, rectangles or trapezoids. Multiple consecutive glyph renderings from the same glyph cache texture are combined into a single entry. Note the CoglPangoDisplayList has nothing to do with a GL display list. After the display list is built it is attached to the PangoLayout with g_object_set_qdata so that next time the layout is rendered it can bypass rebuilding it. The glyph rendering entries are drawn via a VBO. The VBO is attached to the display list so it can be used multiple times. This makes the common case of rendering a PangoLayout contained in a single texture subsequent times usually boil down to a single call to glDrawArrays with an already uploaded VBO. The VBOs are accessed via the cogl_vertex_buffer API so if VBOs are not available in GL it will resort to a fallback. Note this will fall apart if the pango layout is altered after the first render. I can't find a way to detect when the layout is altered. However this won't affect ClutterText because it creates a new PangoLayout whenever any properties are changed. --- clutter/pango/Makefile.am | 2 + clutter/pango/cogl-pango-display-list.c | 330 ++++++++++++++++++++++++ clutter/pango/cogl-pango-display-list.h | 68 +++++ clutter/pango/cogl-pango-render.c | 145 ++++++----- 4 files changed, 482 insertions(+), 63 deletions(-) create mode 100644 clutter/pango/cogl-pango-display-list.c create mode 100644 clutter/pango/cogl-pango-display-list.h diff --git a/clutter/pango/Makefile.am b/clutter/pango/Makefile.am index 1d2d22402..e567e3ba2 100644 --- a/clutter/pango/Makefile.am +++ b/clutter/pango/Makefile.am @@ -1,4 +1,5 @@ source_c = \ + cogl-pango-display-list.c \ cogl-pango-fontmap.c \ cogl-pango-render.c \ cogl-pango-glyph-cache.c @@ -6,6 +7,7 @@ source_c = \ source_h = cogl-pango.h source_h_priv = \ + cogl-pango-display-list.h \ cogl-pango-private.h \ cogl-pango-glyph-cache.h diff --git a/clutter/pango/cogl-pango-display-list.c b/clutter/pango/cogl-pango-display-list.c new file mode 100644 index 000000000..ecef23f58 --- /dev/null +++ b/clutter/pango/cogl-pango-display-list.c @@ -0,0 +1,330 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Neil Roberts + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "cogl-pango-display-list.h" + +typedef enum +{ + COGL_PANGO_DISPLAY_LIST_TEXTURE, + COGL_PANGO_DISPLAY_LIST_RECTANGLE, + COGL_PANGO_DISPLAY_LIST_TRAPEZOID +} CoglPangoDisplayListNodeType; + +typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode; +typedef struct _CoglPangoDisplayListVertex CoglPangoDisplayListVertex; + +struct _CoglPangoDisplayList +{ + CoglColor color; + GSList *nodes; + GSList *last_node; +}; + +struct _CoglPangoDisplayListNode +{ + CoglPangoDisplayListNodeType type; + + CoglColor color; + + union + { + struct + { + /* The texture to render these coords from */ + CoglHandle texture; + /* Array of vertex data to render out of this texture */ + GArray *verts; + /* A VBO representing those vertices */ + CoglHandle vertex_buffer; + } texture; + + struct + { + float x_1, y_1; + float x_2, y_2; + } rectangle; + + struct + { + float y_1; + float x_11; + float x_21; + float y_2; + float x_12; + float x_22; + } trapezoid; + } d; +}; + +struct _CoglPangoDisplayListVertex +{ + float x, y, t_x, t_y; +}; + +CoglPangoDisplayList * +_cogl_pango_display_list_new (void) +{ + return g_slice_new0 (CoglPangoDisplayList); +} + +static void +_cogl_pango_display_list_append_node (CoglPangoDisplayList *dl, + CoglPangoDisplayListNode *node) +{ + if (dl->last_node) + dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node); + else + dl->last_node = dl->nodes = g_slist_prepend (NULL, node); +} + +void +_cogl_pango_display_list_set_color (CoglPangoDisplayList *dl, + const CoglColor *color) +{ + dl->color = *color; +} + +void +_cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, + CoglHandle texture, + float x_1, float y_1, + float x_2, float y_2, + float tx_1, float ty_1, + float tx_2, float ty_2) +{ + CoglPangoDisplayListNode *node; + CoglPangoDisplayListVertex *verts; + + /* Add to the last node if it is a texture node with the same + target texture */ + if (dl->last_node + && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE + && node->d.texture.texture == texture + && !memcmp (&dl->color, &node->color, sizeof (CoglColor))) + { + /* Get rid of the vertex buffer so that it will be recreated */ + if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE) + { + cogl_vertex_buffer_unref (node->d.texture.vertex_buffer); + node->d.texture.vertex_buffer = COGL_INVALID_HANDLE; + } + } + else + { + /* Otherwise create a new node */ + node = g_slice_new (CoglPangoDisplayListNode); + + node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE; + node->color = dl->color; + node->d.texture.texture = cogl_texture_ref (texture); + node->d.texture.verts + = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListVertex)); + node->d.texture.vertex_buffer = COGL_INVALID_HANDLE; + + _cogl_pango_display_list_append_node (dl, node); + } + + g_array_set_size (node->d.texture.verts, + node->d.texture.verts->len + 4); + verts = &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, + node->d.texture.verts->len - 4); + + verts->x = x_1; + verts->y = y_1; + verts->t_x = tx_1; + verts->t_y = ty_1; + verts++; + verts->x = x_1; + verts->y = y_2; + verts->t_x = tx_1; + verts->t_y = ty_2; + verts++; + verts->x = x_2; + verts->y = y_2; + verts->t_x = tx_2; + verts->t_y = ty_2; + verts++; + verts->x = x_2; + verts->y = y_1; + verts->t_x = tx_2; + verts->t_y = ty_1; +} + +void +_cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, + float x_1, float y_1, + float x_2, float y_2) +{ + CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); + + node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE; + node->color = dl->color; + node->d.rectangle.x_1 = x_1; + node->d.rectangle.y_1 = y_1; + node->d.rectangle.x_2 = x_2; + node->d.rectangle.y_2 = y_2; + + _cogl_pango_display_list_append_node (dl, node); +} + +void +_cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, + float y_1, + float x_11, + float x_21, + float y_2, + float x_12, + float x_22) +{ + CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); + + node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID; + node->color = dl->color; + node->d.trapezoid.y_1 = y_1; + node->d.trapezoid.x_11 = x_11; + node->d.trapezoid.x_21 = x_21; + node->d.trapezoid.y_2 = y_2; + node->d.trapezoid.x_12 = x_12; + node->d.trapezoid.x_22 = x_22; + + _cogl_pango_display_list_append_node (dl, node); +} + +static void +_cogl_pango_display_list_render_texture (CoglHandle material, + CoglPangoDisplayListNode *node) +{ + cogl_material_set_layer (material, 0, node->d.texture.texture); + cogl_material_set_color (material, &node->color); + cogl_set_source (material); + + if (node->d.texture.vertex_buffer == COGL_INVALID_HANDLE) + { + CoglHandle vb = cogl_vertex_buffer_new (node->d.texture.verts->len); + + cogl_vertex_buffer_add (vb, "gl_Vertex", 2, GL_FLOAT, FALSE, + sizeof (CoglPangoDisplayListVertex), + &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, 0).x); + cogl_vertex_buffer_add (vb, "gl_MultiTexCoord0", 2, GL_FLOAT, FALSE, + sizeof (CoglPangoDisplayListVertex), + &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, + 0).t_x); + cogl_vertex_buffer_submit (vb); + + node->d.texture.vertex_buffer = vb; + } + + cogl_vertex_buffer_draw (node->d.texture.vertex_buffer, + GL_QUADS, 0, node->d.texture.verts->len); +} + +void +_cogl_pango_display_list_render (CoglPangoDisplayList *dl, + CoglHandle glyph_material, + CoglHandle solid_material) +{ + GSList *l; + + for (l = dl->nodes; l; l = l->next) + { + CoglPangoDisplayListNode *node = l->data; + + switch (node->type) + { + case COGL_PANGO_DISPLAY_LIST_TEXTURE: + _cogl_pango_display_list_render_texture (glyph_material, node); + break; + + case COGL_PANGO_DISPLAY_LIST_RECTANGLE: + cogl_material_set_color (solid_material, &node->color); + cogl_set_source (solid_material); + cogl_rectangle (node->d.rectangle.x_1, + node->d.rectangle.y_1, + node->d.rectangle.x_2, + node->d.rectangle.y_2); + break; + + case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: + { + float points[8]; + + points[0] = node->d.trapezoid.x_11; + points[1] = node->d.trapezoid.y_1; + points[2] = node->d.trapezoid.x_12; + points[3] = node->d.trapezoid.y_2; + points[4] = node->d.trapezoid.x_22; + points[5] = node->d.trapezoid.y_2; + points[6] = node->d.trapezoid.x_21; + points[7] = node->d.trapezoid.y_1; + + cogl_material_set_color (solid_material, &node->color); + cogl_set_source (solid_material); + cogl_path_polygon (points, 4); + cogl_path_fill (); + } + break; + } + } +} + +static void +_cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node) +{ + if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) + { + g_array_free (node->d.texture.verts, TRUE); + if (node->d.texture.texture != COGL_INVALID_HANDLE) + cogl_texture_unref (node->d.texture.texture); + if (node->d.texture.vertex_buffer != COGL_INVALID_HANDLE) + cogl_vertex_buffer_unref (node->d.texture.vertex_buffer); + } + + g_slice_free (CoglPangoDisplayListNode, node); +} + +void +_cogl_pango_display_list_clear (CoglPangoDisplayList *dl) +{ + g_slist_foreach (dl->nodes, (GFunc) _cogl_pango_display_list_node_free, NULL); + g_slist_free (dl->nodes); + dl->nodes = NULL; + dl->last_node = NULL; +} + +void +_cogl_pango_display_list_free (CoglPangoDisplayList *dl) +{ + _cogl_pango_display_list_clear (dl); + g_slice_free (CoglPangoDisplayList, dl); +} diff --git a/clutter/pango/cogl-pango-display-list.h b/clutter/pango/cogl-pango-display-list.h new file mode 100644 index 000000000..a2b123208 --- /dev/null +++ b/clutter/pango/cogl-pango-display-list.h @@ -0,0 +1,68 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Authored By Neil Roberts + * + * Copyright (C) 2009 Intel Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef __COGL_PANGO_DISPLAY_LIST_H__ +#define __COGL_PANGO_DISPLAY_LIST_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _CoglPangoDisplayList CoglPangoDisplayList; + +CoglPangoDisplayList *_cogl_pango_display_list_new (void); + +void _cogl_pango_display_list_set_color (CoglPangoDisplayList *dl, + const CoglColor *color); + +void _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, + CoglHandle texture, + float x_1, float y_1, + float x_2, float y_2, + float tx_1, float ty_1, + float tx_2, float ty_2); + +void _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, + float x_1, float y_1, + float x_2, float y_2); + +void _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, + float y_1, + float x_11, + float x_21, + float y_2, + float x_12, + float x_22); + +void _cogl_pango_display_list_render (CoglPangoDisplayList *dl, + CoglHandle glyph_material, + CoglHandle solid_material); + +void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl); + +void _cogl_pango_display_list_free (CoglPangoDisplayList *dl); + +G_END_DECLS + +#endif /* __COGL_PANGO_DISPLAY_LIST_H__ */ diff --git a/clutter/pango/cogl-pango-render.c b/clutter/pango/cogl-pango-render.c index ba9395b21..91bcbe9f1 100644 --- a/clutter/pango/cogl-pango-render.c +++ b/clutter/pango/cogl-pango-render.c @@ -36,6 +36,7 @@ #include "cogl-pango-private.h" #include "cogl-pango-glyph-cache.h" +#include "cogl-pango-display-list.h" struct _CoglPangoRenderer { @@ -55,9 +56,8 @@ struct _CoglPangoRenderer gboolean use_mipmapping; - /* Array of rectangles to draw from the current texture */ - GArray *glyph_rectangles; - CoglHandle glyph_texture; + /* The current display list that is being built */ + CoglPangoDisplayList *display_list; }; struct _CoglPangoRendererClass @@ -65,20 +65,6 @@ struct _CoglPangoRendererClass PangoRendererClass class_instance; }; -static void -cogl_pango_renderer_glyphs_end (CoglPangoRenderer *priv) -{ - if (priv->glyph_rectangles->len > 0) - { - float *rectangles = (float *) priv->glyph_rectangles->data; - cogl_material_set_layer (priv->glyph_material, 0, priv->glyph_texture); - cogl_set_source (priv->glyph_material); - cogl_rectangles_with_texture_coords (rectangles, - priv->glyph_rectangles->len / 8); - g_array_set_size (priv->glyph_rectangles, 0); - } -} - static void cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, CoglPangoGlyphCacheValue *cache_value, @@ -86,25 +72,19 @@ cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, float y1) { float x2, y2; - float *p; - if (priv->glyph_rectangles->len > 0 - && priv->glyph_texture != cache_value->texture) - cogl_pango_renderer_glyphs_end (priv); - - priv->glyph_texture = cache_value->texture; + g_return_if_fail (priv->display_list != NULL); x2 = x1 + (float) cache_value->draw_width; y2 = y1 + (float) cache_value->draw_height; - g_array_set_size (priv->glyph_rectangles, priv->glyph_rectangles->len + 8); - p = &g_array_index (priv->glyph_rectangles, float, - priv->glyph_rectangles->len - 8); - - *(p++) = x1; *(p++) = y1; - *(p++) = x2; *(p++) = y2; - *(p++) = cache_value->tx1; *(p++) = cache_value->ty1; - *(p++) = cache_value->tx2; *(p++) = cache_value->ty2; + _cogl_pango_display_list_add_texture (priv->display_list, + cache_value->texture, + x1, y1, x2, y2, + cache_value->tx1, + cache_value->ty1, + cache_value->tx2, + cache_value->ty2); } static void cogl_pango_renderer_finalize (GObject *object); @@ -168,7 +148,6 @@ cogl_pango_renderer_init (CoglPangoRenderer *priv) priv->glyph_cache = cogl_pango_glyph_cache_new (FALSE); priv->mipmapped_glyph_cache = cogl_pango_glyph_cache_new (TRUE); priv->use_mipmapping = FALSE; - priv->glyph_rectangles = g_array_new (FALSE, FALSE, sizeof (float)); } static void @@ -191,7 +170,6 @@ cogl_pango_renderer_finalize (GObject *object) cogl_pango_glyph_cache_free (priv->mipmapped_glyph_cache); cogl_pango_glyph_cache_free (priv->glyph_cache); - g_array_free (priv->glyph_rectangles, TRUE); G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); } @@ -213,6 +191,17 @@ cogl_pango_get_renderer_from_context (PangoContext *context) return COGL_PANGO_RENDERER (renderer); } +static GQuark +cogl_pango_render_get_display_list_key (void) +{ + static GQuark key = 0; + + if (G_UNLIKELY (key == 0)) + key = g_quark_from_static_string ("CoglPangoDisplayList"); + + return key; +} + /** * cogl_pango_render_layout_subpixel: * @layout: a #PangoLayout @@ -232,17 +221,37 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, const CoglColor *color, int flags) { - PangoContext *context; - CoglPangoRenderer *priv; + PangoContext *context; + CoglPangoRenderer *priv; + CoglPangoDisplayList *display_list; context = pango_layout_get_context (layout); priv = cogl_pango_get_renderer_from_context (context); if (G_UNLIKELY (!priv)) return; - priv->color = *color; + display_list = g_object_get_qdata (G_OBJECT (layout), + cogl_pango_render_get_display_list_key ()); - pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, x, y); + if (display_list == NULL) + { + priv->display_list = display_list =_cogl_pango_display_list_new (); + priv->color = *color; + g_object_set_qdata_full (G_OBJECT (layout), + cogl_pango_render_get_display_list_key (), + display_list, + (GDestroyNotify) _cogl_pango_display_list_free); + pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); + + priv->display_list = NULL; + } + + cogl_push_matrix (); + cogl_translate (x, y, 0); + _cogl_pango_display_list_render (display_list, + priv->glyph_material, + priv->solid_material); + cogl_pop_matrix (); } /** @@ -296,9 +305,19 @@ cogl_pango_render_layout_line (PangoLayoutLine *line, if (G_UNLIKELY (!priv)) return; + priv->display_list = _cogl_pango_display_list_new (); priv->color = *color; pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y); + + cogl_material_set_color (priv->glyph_material, color); + cogl_material_set_color (priv->solid_material, color); + _cogl_pango_display_list_render (priv->display_list, + priv->glyph_material, + priv->solid_material); + + _cogl_pango_display_list_free (priv->display_list); + priv->display_list = NULL; } void @@ -444,8 +463,7 @@ cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer, else color = priv->color; - cogl_material_set_color (priv->solid_material, &color); - cogl_material_set_color (priv->glyph_material, &color); + _cogl_pango_display_list_set_color (priv->display_list, &color); } static void @@ -457,12 +475,13 @@ cogl_pango_renderer_draw_box (PangoRenderer *renderer, { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); - cogl_set_source (priv->solid_material); - cogl_path_rectangle (x, - y - height, - x + width, - y); - cogl_path_stroke (); + g_return_if_fail (priv->display_list != NULL); + + _cogl_pango_display_list_add_rectangle (priv->display_list, + x, + y - height, + x + width, + y); } static void @@ -500,6 +519,8 @@ cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); float x1, x2, y1, y2; + g_return_if_fail (priv->display_list != NULL); + cogl_pango_renderer_set_color_for_part (renderer, part); cogl_pango_renderer_get_device_units (renderer, @@ -509,8 +530,8 @@ cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, x + width, y + height, &x2, &y2); - cogl_set_source (priv->solid_material); - cogl_rectangle (x1, y1, x2, y2); + _cogl_pango_display_list_add_rectangle (priv->display_list, + x1, y1, x2, y2); } static void @@ -526,6 +547,8 @@ cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); float points[8]; + g_return_if_fail (priv->display_list != NULL); + points[0] = (x11); points[1] = (y1); points[2] = (x12); @@ -537,9 +560,13 @@ cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, cogl_pango_renderer_set_color_for_part (renderer, part); - cogl_set_source (priv->solid_material); - cogl_path_polygon (points, 4); - cogl_path_fill (); + _cogl_pango_display_list_add_trapezoid (priv->display_list, + y1, + x11, + x21, + y2, + x12, + x22); } static void @@ -570,8 +597,6 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, { PangoFontMetrics *metrics; - cogl_pango_renderer_glyphs_end (priv); - if (font == NULL || (metrics = pango_font_get_metrics (font, NULL)) == NULL) { @@ -603,15 +628,11 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, gi->glyph); if (cache_value == NULL) - { - cogl_pango_renderer_glyphs_end (priv); - - cogl_pango_renderer_draw_box (renderer, - x, - y, - PANGO_UNKNOWN_GLYPH_WIDTH, - PANGO_UNKNOWN_GLYPH_HEIGHT); - } + cogl_pango_renderer_draw_box (renderer, + x, + y, + PANGO_UNKNOWN_GLYPH_WIDTH, + PANGO_UNKNOWN_GLYPH_HEIGHT); else { float width, height; @@ -628,6 +649,4 @@ cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, xi += gi->geometry.width; } - - cogl_pango_renderer_glyphs_end (priv); } From 085be4cebc6f021d8deb114e7984fdfc49665f40 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 21 May 2009 12:09:16 +0100 Subject: [PATCH 2/4] [cogl-pango-render] Fix the positioning when calling cogl_pango_render_layout When a position is given to cogl_pango_render_layout_subpixel it translates the GL matrix by the coordinates. However it was not dividing by PANGO_SCALE so the coordinates were completely wrong. --- clutter/pango/cogl-pango-render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clutter/pango/cogl-pango-render.c b/clutter/pango/cogl-pango-render.c index 91bcbe9f1..a3b5e2e39 100644 --- a/clutter/pango/cogl-pango-render.c +++ b/clutter/pango/cogl-pango-render.c @@ -247,7 +247,7 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, } cogl_push_matrix (); - cogl_translate (x, y, 0); + cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0); _cogl_pango_display_list_render (display_list, priv->glyph_material, priv->solid_material); From 3fc64dfaa799828c3e0743df60de4255dac58d60 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 21 May 2009 12:17:12 +0100 Subject: [PATCH 3/4] [pango-render] Keep a reference to the first line to detect layout changes In order to cope with the situation where an application renders with a PangoLayout, makes some changes and then renders again with the same layout, CoglPangoRenderer needs to detect that the changes have occured so that it can recreate the display list. This is acheived by keeping a reference to the first line of the layout. When the layout is changed Pango will clear the layout pointer in the first line and create a new line. So if the layout pointer in the line becomes NULL then we know the layout has changed. This trick was suggested by Behdad Esfahbod in this email: http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html --- clutter/pango/cogl-pango-render.c | 81 +++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 14 deletions(-) diff --git a/clutter/pango/cogl-pango-render.c b/clutter/pango/cogl-pango-render.c index a3b5e2e39..99dd40d3f 100644 --- a/clutter/pango/cogl-pango-render.c +++ b/clutter/pango/cogl-pango-render.c @@ -65,6 +65,19 @@ struct _CoglPangoRendererClass PangoRendererClass class_instance; }; +typedef struct _CoglPangoRendererQdata CoglPangoRendererQdata; + +/* An instance of this struct gets attached to each PangoLayout to + cache the VBO and to detect changes to the layout */ +struct _CoglPangoRendererQdata +{ + /* The cache of the geometry for the layout */ + CoglPangoDisplayList *display_list; + /* A reference to the first line of the layout. This is just used to + detect changes */ + PangoLayoutLine *first_line; +}; + static void cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, CoglPangoGlyphCacheValue *cache_value, @@ -192,7 +205,7 @@ cogl_pango_get_renderer_from_context (PangoContext *context) } static GQuark -cogl_pango_render_get_display_list_key (void) +cogl_pango_render_get_qdata_key (void) { static GQuark key = 0; @@ -202,6 +215,16 @@ cogl_pango_render_get_display_list_key (void) return key; } +static void +cogl_pango_render_qdata_destroy (CoglPangoRendererQdata *qdata) +{ + if (qdata->display_list) + _cogl_pango_display_list_free (qdata->display_list); + if (qdata->first_line) + pango_layout_line_unref (qdata->first_line); + g_slice_free (CoglPangoRendererQdata, qdata); +} + /** * cogl_pango_render_layout_subpixel: * @layout: a #PangoLayout @@ -221,37 +244,67 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, const CoglColor *color, int flags) { - PangoContext *context; - CoglPangoRenderer *priv; - CoglPangoDisplayList *display_list; + PangoContext *context; + CoglPangoRenderer *priv; + CoglPangoRendererQdata *qdata; context = pango_layout_get_context (layout); priv = cogl_pango_get_renderer_from_context (context); if (G_UNLIKELY (!priv)) return; - display_list = g_object_get_qdata (G_OBJECT (layout), - cogl_pango_render_get_display_list_key ()); + qdata = g_object_get_qdata (G_OBJECT (layout), + cogl_pango_render_get_qdata_key ()); - if (display_list == NULL) + if (qdata == NULL) { - priv->display_list = display_list =_cogl_pango_display_list_new (); - priv->color = *color; + qdata = g_slice_new0 (CoglPangoRendererQdata); g_object_set_qdata_full (G_OBJECT (layout), - cogl_pango_render_get_display_list_key (), - display_list, - (GDestroyNotify) _cogl_pango_display_list_free); - pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); + cogl_pango_render_get_qdata_key (), + qdata, + (GDestroyNotify) + cogl_pango_render_qdata_destroy); + } + /* Check if the layout has changed since the last build of the + display list. This trick was suggested by Behdad Esfahbod here: + http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */ + if (qdata->display_list && qdata->first_line + && qdata->first_line->layout != layout) + { + _cogl_pango_display_list_free (qdata->display_list); + qdata->display_list = NULL; + } + + if (qdata->display_list == NULL) + { + qdata->display_list = _cogl_pango_display_list_new (); + + priv->color = *color; + priv->display_list = qdata->display_list; + pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); priv->display_list = NULL; } cogl_push_matrix (); cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0); - _cogl_pango_display_list_render (display_list, + _cogl_pango_display_list_render (qdata->display_list, priv->glyph_material, priv->solid_material); cogl_pop_matrix (); + + /* Keep a reference to the first line of the layout so we can detect + changes */ + if (qdata->first_line) + { + pango_layout_line_unref (qdata->first_line); + qdata->first_line = NULL; + } + if (pango_layout_get_line_count (layout) > 0) + { + qdata->first_line = pango_layout_get_line (layout, 0); + pango_layout_line_ref (qdata->first_line); + } } /** From 7e18109f9bec0afba900534a9c96035ae1f32dd7 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 21 May 2009 15:49:14 +0100 Subject: [PATCH 4/4] [cogl-pango-display-list] Use GL_TRIANGLES under GLES GLES doesn't support GL_QUADS. This patch makes it use GL_TRIANGLES instead in that case. Unfortunately this means submitting two extra vertices per quad. It could be better to use indexed elements once CoglVertexBuffers gains support for that. --- clutter/pango/cogl-pango-display-list.c | 28 ++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/clutter/pango/cogl-pango-display-list.c b/clutter/pango/cogl-pango-display-list.c index ecef23f58..b87b609f9 100644 --- a/clutter/pango/cogl-pango-display-list.c +++ b/clutter/pango/cogl-pango-display-list.c @@ -41,6 +41,13 @@ typedef enum typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode; typedef struct _CoglPangoDisplayListVertex CoglPangoDisplayListVertex; +#ifdef HAVE_CLUTTER_GLX +#define COGL_PANGO_DISPLAY_LIST_DRAW_MODE GL_QUADS +#else +/* GLES doesn't support GL_QUADS so we use GL_TRIANGLES instead */ +#define COGL_PANGO_DISPLAY_LIST_DRAW_MODE GL_TRIANGLES +#endif + struct _CoglPangoDisplayList { CoglColor color; @@ -177,6 +184,24 @@ _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, verts->y = y_1; verts->t_x = tx_2; verts->t_y = ty_1; + +#ifndef HAVE_CLUTTER_GLX + + /* GLES doesn't support GL_QUADS so we use GL_TRIANGLES instead with + two extra vertices per quad. FIXME: It might be better to use + indexed elements here but cogl vertex buffers don't currently + support storing the indices */ + + g_array_set_size (node->d.texture.verts, + node->d.texture.verts->len + 2); + verts = &g_array_index (node->d.texture.verts, + CoglPangoDisplayListVertex, + node->d.texture.verts->len - 6); + + verts[4] = verts[0]; + verts[5] = verts[2]; + +#endif /* HAVE_CLUTTER_GLX */ } void @@ -246,7 +271,8 @@ _cogl_pango_display_list_render_texture (CoglHandle material, } cogl_vertex_buffer_draw (node->d.texture.vertex_buffer, - GL_QUADS, 0, node->d.texture.verts->len); + COGL_PANGO_DISPLAY_LIST_DRAW_MODE, + 0, node->d.texture.verts->len); } void