diff options
author | Behdad Esfahbod <behdad@behdad.org> | 2012-07-27 04:02:38 -0400 |
---|---|---|
committer | Behdad Esfahbod <behdad@behdad.org> | 2012-07-27 04:20:39 -0400 |
commit | 2f87cebe1062c7007021ebd05c1664e60da80825 (patch) | |
tree | f84b8d260e41b45ebeb7c47dfcde2befed6df1c9 /src | |
parent | e9eb9503e97044222f843daacfa47e26e51312b7 (diff) |
Implement shape_plan caching
Should give us some performance boost.
Diffstat (limited to 'src')
-rw-r--r-- | src/hb-buffer-private.hh | 30 | ||||
-rw-r--r-- | src/hb-font-private.hh | 6 | ||||
-rw-r--r-- | src/hb-font.cc | 15 | ||||
-rw-r--r-- | src/hb-shape-plan.cc | 100 | ||||
-rw-r--r-- | src/hb-shape-plan.h | 7 | ||||
-rw-r--r-- | src/hb-shape.cc | 2 |
6 files changed, 158 insertions, 2 deletions
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh index b800aebb..d6189d24 100644 --- a/src/hb-buffer-private.hh +++ b/src/hb-buffer-private.hh @@ -40,14 +40,44 @@ ASSERT_STATIC (sizeof (hb_glyph_info_t) == 20); ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t)); + +/* + * hb_segment_properties_t + */ + typedef struct hb_segment_properties_t { hb_direction_t direction; hb_script_t script; hb_language_t language; ASSERT_POD (); } hb_segment_properties_t; + #define _HB_BUFFER_PROPS_DEFAULT { HB_DIRECTION_INVALID, HB_SCRIPT_INVALID, HB_LANGUAGE_INVALID } +static inline hb_bool_t +hb_segment_properties_equal (const hb_segment_properties_t *a, + const hb_segment_properties_t *b) +{ + return a->direction == b->direction && + a->script == b->script && + a->language == b->language; +} + + +static inline long +hb_segment_properties_hash (const hb_segment_properties_t *p) +{ + /* TODO improve */ + return (long) p->direction + + (long) p->script + + (long) p->language; +} + + + +/* + * hb_buffer_t + */ struct hb_buffer_t { hb_object_header_t header; diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh index f1b43c0e..892fd7c1 100644 --- a/src/hb-font-private.hh +++ b/src/hb-font-private.hh @@ -34,6 +34,7 @@ #include "hb-font.h" #include "hb-object-private.hh" #include "hb-shaper-private.hh" +#include "hb-shape-plan-private.hh" @@ -101,6 +102,11 @@ struct hb_face_t { unsigned int upem; struct hb_shaper_data_t shaper_data; + + struct plan_node_t { + hb_shape_plan_t *shape_plan; + plan_node_t *next; + } *shape_plans; }; #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS diff --git a/src/hb-font.cc b/src/hb-font.cc index 5a3219a1..83c349ed 100644 --- a/src/hb-font.cc +++ b/src/hb-font.cc @@ -596,7 +596,9 @@ static const hb_face_t _hb_face_nil = { #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, #include "hb-shaper-list.hh" #undef HB_SHAPER_IMPLEMENT - } + }, + + NULL, /* shape_plans */ }; @@ -709,6 +711,17 @@ hb_face_destroy (hb_face_t *face) { if (!hb_object_destroy (face)) return; + /* The cached shape_plans think they have a reference on us, and + * try to release it. Make sure that doesn't mess up. */ + face->header.ref_count.ref_count = HB_REFERENCE_COUNT_INVALID_VALUE; + for (hb_face_t::plan_node_t *node = face->shape_plans; node; ) + { + hb_face_t::plan_node_t *next = node->next; + hb_shape_plan_destroy (node->shape_plan); + free (node); + node = next; + } + #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, face); #include "hb-shaper-list.hh" #undef HB_SHAPER_IMPLEMENT diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc index 663cb348..af3c18d2 100644 --- a/src/hb-shape-plan.cc +++ b/src/hb-shape-plan.cc @@ -179,3 +179,103 @@ hb_shape_plan_execute (hb_shape_plan *shape_plan, return false; } + + +/* + * caching + */ + +#if 0 +static long +hb_shape_plan_hash (const hb_shape_plan_t *shape_plan) +{ + return hb_segment_properties_hash (&shape_plan->props) + + shape_plan->default_shaper_list ? 0 : (long) shape_plan->shaper_func; +} +#endif + +/* TODO no user-feature caching for now. */ +struct hb_shape_plan_proposal_t +{ + const hb_segment_properties_t props; + const char * const *shaper_list; + hb_shape_func_t *shaper_func; +}; + +static hb_bool_t +hb_shape_plan_matches (const hb_shape_plan_t *shape_plan, + const hb_shape_plan_proposal_t *proposal) +{ + return hb_segment_properties_equal (&shape_plan->props, &proposal->props) && + ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) || + (shape_plan->shaper_func == proposal->shaper_func)); +} + +hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list) +{ + if (num_user_features) + return hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list); + + hb_shape_plan_proposal_t proposal = { + *props, + shaper_list, + NULL + }; + + if (shaper_list) { + /* Choose shaper. Adapted from hb_shape_plan_plan(). */ +#define HB_SHAPER_PLAN(shaper) \ + HB_STMT_START { \ + if (hb_##shaper##_shaper_face_data_ensure (face)) \ + proposal.shaper_func = _hb_##shaper##_shape; \ + } HB_STMT_END + + for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++) + if (0) + ; +#define HB_SHAPER_IMPLEMENT(shaper) \ + else if (0 == strcmp (*shaper_item, #shaper)) \ + HB_SHAPER_PLAN (shaper); +#include "hb-shaper-list.hh" +#undef HB_SHAPER_IMPLEMENT + +#undef HB_SHAPER_PLAN + + if (unlikely (!proposal.shaper_list)) + return hb_shape_plan_get_empty (); + } + + +retry: + hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans); + for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next) + if (hb_shape_plan_matches (node->shape_plan, &proposal)) + return hb_shape_plan_reference (node->shape_plan); + + /* Not found. */ + + hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list); + + hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t)); + if (unlikely (!node)) + return shape_plan; + + node->shape_plan = shape_plan; + node->next = cached_plan_nodes; + + if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) { + hb_shape_plan_destroy (shape_plan); + free (node); + goto retry; + } + + /* Release our reference on face. */ + hb_face_destroy (face); + + return hb_shape_plan_reference (shape_plan); +} diff --git a/src/hb-shape-plan.h b/src/hb-shape-plan.h index a4786a82..fbce5ddf 100644 --- a/src/hb-shape-plan.h +++ b/src/hb-shape-plan.h @@ -47,6 +47,13 @@ hb_shape_plan_create (hb_face_t *face, unsigned int num_user_features, const char * const *shaper_list); +hb_shape_plan_t * +hb_shape_plan_create_cached (hb_face_t *face, + const hb_segment_properties_t *props, + const hb_feature_t *user_features, + unsigned int num_user_features, + const char * const *shaper_list); + HB_INTERNAL hb_shape_plan_t * hb_shape_plan_get_empty (void); diff --git a/src/hb-shape.cc b/src/hb-shape.cc index fde9cf2a..5aa587bc 100644 --- a/src/hb-shape.cc +++ b/src/hb-shape.cc @@ -87,7 +87,7 @@ hb_shape_full (hb_font_t *font, buffer->guess_properties (); - hb_shape_plan_t *shape_plan = hb_shape_plan_create (font->face, &buffer->props, features, num_features, shaper_list); + hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list); hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features); hb_shape_plan_destroy (shape_plan); return res; |