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.
This commit is contained in:
Neil Roberts 2010-02-26 12:38:22 +00:00
parent 22ce8a7111
commit 9083fc8e1f
6 changed files with 338 additions and 94 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -26,12 +26,13 @@
#include <glib.h>
#include <cogl/cogl.h>
#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);

View File

@ -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
* <http://www.gnu.org/licenses/>.
*
*
*
* Authors:
* Neil Roberts <neil@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib.h>
#include <cogl/cogl.h>
#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);
}

View File

@ -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
* <http://www.gnu.org/licenses/>.
*
*
*
* Authors:
* Neil Roberts <neil@linux.intel.com>
*/
#ifndef __COGL_PANGO_PIPELINE_CACHE_H__
#define __COGL_PANGO_PIPELINE_CACHE_H__
#include <glib.h>
#include <cogl/cogl.h>
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__ */

View File

@ -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