Implement shape_plan caching
Should give us some performance boost.
This commit is contained in:
parent
e9eb9503e9
commit
2f87cebe10
4
TODO
4
TODO
@ -1,6 +1,10 @@
|
||||
General fixes:
|
||||
=============
|
||||
|
||||
- mask propagation (when ligation, "or" the masks).
|
||||
|
||||
- fail_shaper that always fails.
|
||||
|
||||
- Make map in setup_masks() const, etc.
|
||||
|
||||
- Warn at compile time (and runtime with HB_DEBUG?) if no Unicode / font
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user