From cbf011dcd03f7e0fb385691d99b6ed633d646470 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Fri, 26 Feb 2010 12:38:22 +0000 Subject: [PATCH] cogl-pango: Use a seperate cache of pipelines Instead of creating just two materials (one for texturing and one for solid primitives) the pango renderer now maintains a cache of pipelines. The display list can request a pipeline for a texture from the cache. The same pipeline cache is used by all display lists so that the pipelines can be shared. This avoids changing the texture on the material during a paint run. --- pango/Makefile.am | 16 ++- pango/cogl-pango-display-list.c | 68 ++++++---- pango/cogl-pango-display-list.h | 7 +- pango/cogl-pango-pipeline-cache.c | 211 ++++++++++++++++++++++++++++++ pango/cogl-pango-pipeline-cache.h | 53 ++++++++ pango/cogl-pango-render.c | 77 ++++------- 6 files changed, 338 insertions(+), 94 deletions(-) create mode 100644 pango/cogl-pango-pipeline-cache.c create mode 100644 pango/cogl-pango-pipeline-cache.h diff --git a/pango/Makefile.am b/pango/Makefile.am index cacbd4f73..89d2faf6c 100644 --- a/pango/Makefile.am +++ b/pango/Makefile.am @@ -1,17 +1,19 @@ include $(top_srcdir)/build/autotools/Makefile.am.silent source_c = \ - cogl-pango-display-list.c \ - cogl-pango-fontmap.c \ - cogl-pango-render.c \ - cogl-pango-glyph-cache.c + cogl-pango-display-list.c \ + cogl-pango-fontmap.c \ + cogl-pango-render.c \ + cogl-pango-glyph-cache.c \ + cogl-pango-pipeline-cache.c source_h = cogl-pango.h source_h_priv = \ - cogl-pango-display-list.h \ - cogl-pango-private.h \ - cogl-pango-glyph-cache.h + cogl-pango-display-list.h \ + cogl-pango-private.h \ + cogl-pango-glyph-cache.h \ + cogl-pango-pipeline-cache.h if COGL_STANDALONE_BUILD lib_LTLIBRARIES = libcoglpango.la diff --git a/pango/cogl-pango-display-list.c b/pango/cogl-pango-display-list.c index 6226a81cd..a79cf0f1c 100644 --- a/pango/cogl-pango-display-list.c +++ b/pango/cogl-pango-display-list.c @@ -43,10 +43,11 @@ typedef struct _CoglPangoDisplayListVertex CoglPangoDisplayListVertex; struct _CoglPangoDisplayList { - gboolean color_override; - CoglColor color; - GSList *nodes; - GSList *last_node; + gboolean color_override; + CoglColor color; + GSList *nodes; + GSList *last_node; + CoglPangoPipelineCache *pipeline_cache; }; struct _CoglPangoDisplayListNode @@ -56,6 +57,8 @@ struct _CoglPangoDisplayListNode gboolean color_override; CoglColor color; + CoglPipeline *pipeline; + union { struct @@ -92,9 +95,13 @@ struct _CoglPangoDisplayListVertex }; CoglPangoDisplayList * -_cogl_pango_display_list_new (void) +_cogl_pango_display_list_new (CoglPangoPipelineCache *pipeline_cache) { - return g_slice_new0 (CoglPangoDisplayList); + CoglPangoDisplayList *dl = g_slice_new0 (CoglPangoDisplayList); + + dl->pipeline_cache = pipeline_cache; + + return dl; } static void @@ -156,6 +163,7 @@ _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE; node->color_override = dl->color_override; node->color = dl->color; + node->pipeline = NULL; node->d.texture.texture = cogl_handle_ref (texture); node->d.texture.verts = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListVertex)); @@ -205,6 +213,7 @@ _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, node->d.rectangle.y_1 = y_1; node->d.rectangle.x_2 = x_2; node->d.rectangle.y_2 = y_2; + node->pipeline = NULL; _cogl_pango_display_list_append_node (dl, node); } @@ -229,6 +238,7 @@ _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, node->d.trapezoid.y_2 = y_2; node->d.trapezoid.x_12 = x_12; node->d.trapezoid.x_22 = x_22; + node->pipeline = NULL; _cogl_pango_display_list_append_node (dl, node); } @@ -309,17 +319,8 @@ emit_vertex_buffer_geometry (CoglPangoDisplayListNode *node) } static void -_cogl_pango_display_list_render_texture (CoglMaterial *material, - const CoglColor *color, - CoglPangoDisplayListNode *node) +_cogl_pango_display_list_render_texture (CoglPangoDisplayListNode *node) { - CoglColor premult_color = *color; - - cogl_material_set_layer (material, 0, node->d.texture.texture); - cogl_material_set_color (material, &premult_color); - - cogl_push_source (material); - /* For small runs of text like icon labels, we can get better performance * going through the Cogl journal since text may then be batched together * with other geometry. */ @@ -329,15 +330,11 @@ _cogl_pango_display_list_render_texture (CoglMaterial *material, emit_rectangles_through_journal (node); else emit_vertex_buffer_geometry (node); - - cogl_pop_source (); } void _cogl_pango_display_list_render (CoglPangoDisplayList *dl, - const CoglColor *color, - CoglMaterial *glyph_material, - CoglMaterial *solid_material) + const CoglColor *color) { GSList *l; @@ -346,6 +343,18 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl, CoglPangoDisplayListNode *node = l->data; CoglColor draw_color; + if (node->pipeline == NULL) + { + if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) + node->pipeline = + _cogl_pango_pipeline_cache_get (dl->pipeline_cache, + node->d.texture.texture); + else + node->pipeline = + _cogl_pango_pipeline_cache_get (dl->pipeline_cache, + NULL); + } + if (node->color_override) /* Use the override color but preserve the alpha from the draw color */ @@ -358,21 +367,20 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl, draw_color = *color; cogl_color_premultiply (&draw_color); + cogl_pipeline_set_color (node->pipeline, &draw_color); + cogl_push_source (node->pipeline); + switch (node->type) { case COGL_PANGO_DISPLAY_LIST_TEXTURE: - _cogl_pango_display_list_render_texture (glyph_material, - &draw_color, node); + _cogl_pango_display_list_render_texture (node); break; case COGL_PANGO_DISPLAY_LIST_RECTANGLE: - cogl_material_set_color (solid_material, &draw_color); - cogl_push_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); - cogl_pop_source (); break; case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: @@ -389,16 +397,15 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl, points[6] = node->d.trapezoid.x_21; points[7] = node->d.trapezoid.y_1; - cogl_material_set_color (solid_material, &draw_color); - cogl_push_source (solid_material); path = cogl_path_new (); cogl_path_polygon (path, points, 4); cogl_path_fill (path); cogl_object_unref (path); - cogl_pop_source (); } break; } + + cogl_pop_source (); } } @@ -414,6 +421,9 @@ _cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node) cogl_handle_unref (node->d.texture.vertex_buffer); } + if (node->pipeline) + cogl_object_unref (node->pipeline); + g_slice_free (CoglPangoDisplayListNode, node); } diff --git a/pango/cogl-pango-display-list.h b/pango/cogl-pango-display-list.h index 3079bf137..dd14edd42 100644 --- a/pango/cogl-pango-display-list.h +++ b/pango/cogl-pango-display-list.h @@ -26,12 +26,13 @@ #include #include +#include "cogl-pango-pipeline-cache.h" G_BEGIN_DECLS typedef struct _CoglPangoDisplayList CoglPangoDisplayList; -CoglPangoDisplayList *_cogl_pango_display_list_new (void); +CoglPangoDisplayList *_cogl_pango_display_list_new (CoglPangoPipelineCache *); void _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl, const CoglColor *color); @@ -57,9 +58,7 @@ void _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, float x_22); void _cogl_pango_display_list_render (CoglPangoDisplayList *dl, - const CoglColor *color, - CoglMaterial *glyph_material, - CoglMaterial *solid_material); + const CoglColor *color); void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl); diff --git a/pango/cogl-pango-pipeline-cache.c b/pango/cogl-pango-pipeline-cache.c new file mode 100644 index 000000000..ae1397c2a --- /dev/null +++ b/pango/cogl-pango-pipeline-cache.c @@ -0,0 +1,211 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2011 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 + * . + * + * + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "cogl-pango-pipeline-cache.h" + +typedef struct _CoglPangoPipelineCacheEntry CoglPangoPipelineCacheEntry; + +struct _CoglPangoPipelineCache +{ + GHashTable *hash_table; + + CoglPipeline *base_texture_pipeline; + + gboolean use_mipmapping; +}; + +struct _CoglPangoPipelineCacheEntry +{ + /* This will take a reference or it can be NULL to represent the + pipeline used to render colors */ + CoglHandle texture; + + /* This will only take a weak reference */ + CoglHandle pipeline; +}; + +static void +_cogl_pango_pipeline_cache_key_destroy (gpointer data) +{ + if (data) + cogl_object_unref (data); +} + +static void +_cogl_pango_pipeline_cache_value_destroy (gpointer data) +{ + CoglPangoPipelineCacheEntry *cache_entry = data; + + if (cache_entry->texture) + cogl_object_unref (cache_entry->texture); + + /* We don't need to unref the pipeline because it only takes a weak + reference */ + + g_slice_free (CoglPangoPipelineCacheEntry, cache_entry); +} + +CoglPangoPipelineCache * +_cogl_pango_pipeline_cache_new (gboolean use_mipmapping) +{ + CoglPangoPipelineCache *cache = g_new (CoglPangoPipelineCache, 1); + + /* The key is the pipeline pointer. A reference is taken when the + pipeline is used as a key so we should unref it again in the + destroy function */ + cache->hash_table = + g_hash_table_new_full (g_direct_hash, + g_direct_equal, + _cogl_pango_pipeline_cache_key_destroy, + _cogl_pango_pipeline_cache_value_destroy); + + cache->base_texture_pipeline = NULL; + + cache->use_mipmapping = use_mipmapping; + + return cache; +} + +static CoglPipeline * +get_base_texture_pipeline (CoglPangoPipelineCache *cache) +{ + if (cache->base_texture_pipeline == NULL) + { + CoglPipeline *pipeline; + + pipeline = cache->base_texture_pipeline = cogl_pipeline_new (); + + /* The default combine mode of materials is to modulate (A x B) + * the texture RGBA channels with the RGBA channels of the + * previous layer (which in our case is just the font color) + * + * Since the RGB for an alpha texture is defined as 0, this gives us: + * + * result.rgb = color.rgb * 0 + * result.a = color.a * texture.a + * + * What we want is premultiplied rgba values: + * + * result.rgba = color.rgb * texture.a + * result.a = color.a * texture.a + */ + cogl_pipeline_set_layer_combine (pipeline, 0, /* layer */ + "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", + NULL); + cogl_pipeline_set_layer_wrap_mode (pipeline, 0, + COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); + + if (cache->use_mipmapping) + cogl_pipeline_set_layer_filters + (pipeline, 0, + COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, + COGL_PIPELINE_FILTER_LINEAR); + } + + return cache->base_texture_pipeline; +} + +typedef struct +{ + CoglPangoPipelineCache *cache; + CoglHandle texture; +} PipelineDestroyNotifyData; + +static void +pipeline_destroy_notify_cb (void *user_data) +{ + PipelineDestroyNotifyData *data = user_data; + + g_hash_table_remove (data->cache->hash_table, data->texture); + g_slice_free (PipelineDestroyNotifyData, data); +} + +CoglPipeline * +_cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, + CoglHandle texture) +{ + CoglPangoPipelineCacheEntry *entry; + PipelineDestroyNotifyData *destroy_data; + static CoglUserDataKey pipeline_destroy_notify_key; + + /* Look for an existing entry */ + entry = g_hash_table_lookup (cache->hash_table, texture); + + if (entry) + return cogl_object_ref (entry->pipeline); + + /* No existing pipeline was found so let's create another */ + entry = g_slice_new (CoglPangoPipelineCacheEntry); + + if (texture) + { + entry->texture = cogl_handle_ref (texture); + entry->pipeline = cogl_pipeline_copy (get_base_texture_pipeline (cache)); + + cogl_pipeline_set_layer_texture (entry->pipeline, 0 /* layer */, texture); + } + else + { + entry->texture = NULL; + entry->pipeline = cogl_pipeline_new (); + } + + /* Add a weak reference to the pipeline so we can remove it from the + hash table when it is destroyed */ + destroy_data = g_slice_new (PipelineDestroyNotifyData); + destroy_data->cache = cache; + destroy_data->texture = texture; + cogl_object_set_user_data (entry->pipeline, + &pipeline_destroy_notify_key, + destroy_data, + pipeline_destroy_notify_cb); + + g_hash_table_insert (cache->hash_table, + texture ? cogl_handle_ref (texture) : NULL, + entry); + + /* This doesn't take a reference on the pipeline so that it will use + the newly created reference */ + return entry->pipeline; +} + +void +_cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache) +{ + if (cache->base_texture_pipeline) + cogl_object_unref (cache->base_texture_pipeline); + + g_hash_table_destroy (cache->hash_table); + + g_free (cache); +} diff --git a/pango/cogl-pango-pipeline-cache.h b/pango/cogl-pango-pipeline-cache.h new file mode 100644 index 000000000..d095c584f --- /dev/null +++ b/pango/cogl-pango-pipeline-cache.h @@ -0,0 +1,53 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2011 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 + * . + * + * + * + * Authors: + * Neil Roberts + */ + +#ifndef __COGL_PANGO_PIPELINE_CACHE_H__ +#define __COGL_PANGO_PIPELINE_CACHE_H__ + +#include +#include + +G_BEGIN_DECLS + +typedef struct _CoglPangoPipelineCache CoglPangoPipelineCache; + +CoglPangoPipelineCache * +_cogl_pango_pipeline_cache_new (gboolean use_mipmapping); + +/* Returns a pipeline that can be used to render glyphs in the given + texture. The pipeline has a new reference so it is up to the caller + to unref it */ +CoglPipeline * +_cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, + CoglHandle texture); + +void +_cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache); + +G_END_DECLS + +#endif /* __COGL_PANGO_PIPELINE_CACHE_H__ */ diff --git a/pango/cogl-pango-render.c b/pango/cogl-pango-render.c index 597f7898c..7fa7c5784 100644 --- a/pango/cogl-pango-render.c +++ b/pango/cogl-pango-render.c @@ -44,16 +44,14 @@ struct _CoglPangoRenderer { PangoRenderer parent_instance; - /* The material used to texture from the glyph cache with */ - CoglMaterial *glyph_material; - /* The material used for solid fills. (boxes, rectangles + trapezoids) */ - CoglMaterial *solid_material; - /* Two caches of glyphs as textures, one with mipmapped textures and one without */ CoglPangoGlyphCache *glyph_cache; CoglPangoGlyphCache *mipmapped_glyph_cache; + CoglPangoPipelineCache *pipeline_cache; + CoglPangoPipelineCache *mipmapped_pipeline_cache; + gboolean use_mipmapping; /* The current display list that is being built */ @@ -173,30 +171,8 @@ G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER); static void cogl_pango_renderer_init (CoglPangoRenderer *priv) { - priv->glyph_material = cogl_material_new (); - - /* The default combine mode of materials is to modulate (A x B) the texture - * RGBA channels with the RGBA channels of the previous layer (which in our - * case is just the font color) - * - * Since the RGB for an alpha texture is defined as 0, this gives us: - * - * result.rgb = color.rgb * 0 - * result.a = color.a * texture.a - * - * What we want is premultiplied rgba values: - * - * result.rgba = color.rgb * texture.a - * result.a = color.a * texture.a - */ - cogl_material_set_layer_combine (priv->glyph_material, 0, /* layer */ - "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", - NULL); - cogl_material_set_layer_wrap_mode (priv->glyph_material, 0, - COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE); - - priv->solid_material = cogl_material_new (); - + priv->pipeline_cache = _cogl_pango_pipeline_cache_new (FALSE); + priv->mipmapped_pipeline_cache = _cogl_pango_pipeline_cache_new (TRUE); priv->glyph_cache = cogl_pango_glyph_cache_new (FALSE); priv->mipmapped_glyph_cache = cogl_pango_glyph_cache_new (TRUE); priv->use_mipmapping = TRUE; @@ -224,6 +200,9 @@ cogl_pango_renderer_finalize (GObject *object) cogl_pango_glyph_cache_free (priv->mipmapped_glyph_cache); cogl_pango_glyph_cache_free (priv->glyph_cache); + _cogl_pango_pipeline_cache_free (priv->pipeline_cache); + _cogl_pango_pipeline_cache_free (priv->mipmapped_pipeline_cache); + G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); } @@ -333,9 +312,13 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, if (qdata->display_list == NULL) { + CoglPangoPipelineCache *pipeline_cache = + (priv->use_mipmapping ? + priv->mipmapped_pipeline_cache : priv->pipeline_cache); + cogl_pango_ensure_glyph_cache_for_layout (layout); - qdata->display_list = _cogl_pango_display_list_new (); + qdata->display_list = _cogl_pango_display_list_new (pipeline_cache); /* Register for notification of when the glyph cache changes so we can rebuild the display list */ @@ -354,9 +337,7 @@ cogl_pango_render_layout_subpixel (PangoLayout *layout, cogl_push_matrix (); cogl_translate (x / (gfloat) PANGO_SCALE, y / (gfloat) PANGO_SCALE, 0); _cogl_pango_display_list_render (qdata->display_list, - color, - priv->glyph_material, - priv->solid_material); + color); cogl_pop_matrix (); /* Keep a reference to the first line of the layout so we can detect @@ -416,24 +397,26 @@ cogl_pango_render_layout_line (PangoLayoutLine *line, int y, const CoglColor *color) { - PangoContext *context; - CoglPangoRenderer *priv; + PangoContext *context; + CoglPangoRenderer *priv; + CoglPangoPipelineCache *pipeline_cache; context = pango_layout_get_context (line->layout); priv = cogl_pango_get_renderer_from_context (context); if (G_UNLIKELY (!priv)) return; - priv->display_list = _cogl_pango_display_list_new (); + pipeline_cache = (priv->use_mipmapping ? + priv->mipmapped_pipeline_cache : priv->pipeline_cache); + + priv->display_list = _cogl_pango_display_list_new (pipeline_cache); _cogl_pango_ensure_glyph_cache_for_layout_line (line); pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, x, y); _cogl_pango_display_list_render (priv->display_list, - color, - priv->glyph_material, - priv->solid_material); + color); _cogl_pango_display_list_free (priv->display_list); priv->display_list = NULL; @@ -450,21 +433,7 @@ void _cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer, gboolean value) { - if (renderer->use_mipmapping != value) - { - CoglMaterialFilter min_filter; - - renderer->use_mipmapping = value; - - if (value) - min_filter = COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR; - else - min_filter = COGL_MATERIAL_FILTER_LINEAR; - - cogl_material_set_layer_filters (renderer->glyph_material, 0, - min_filter, - COGL_MATERIAL_FILTER_LINEAR); - } + renderer->use_mipmapping = value; } gboolean