[layout] Build lookup accelerators lazily on-demand

Reduces memory consumption for large multi-script fonts
drastically.
This commit is contained in:
Behdad Esfahbod 2023-01-31 14:59:39 -07:00
parent 83353f13f4
commit 7a4bd97e4a
6 changed files with 76 additions and 57 deletions

View File

@ -121,7 +121,6 @@ _hb_ot_font_destroy (void *font_data)
hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data; hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data;
auto *cache = ot_font->advance_cache.get_relaxed (); auto *cache = ot_font->advance_cache.get_relaxed ();
if (cache)
hb_free (cache); hb_free (cache);
hb_free (ot_font); hb_free (ot_font);

View File

@ -64,11 +64,8 @@ inline bool PosLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_apply
c->set_lookup_props (l.get_props ()); c->set_lookup_props (l.get_props ());
bool ret = false; bool ret = false;
if (lookup_index < gpos->lookup_count) auto *accel = gpos->get_accel (lookup_index);
{ ret = accel && accel->apply (c, l.get_subtable_count (), false);
auto &accel = gpos->accels[lookup_index];
ret = accel.apply (c, l.get_subtable_count (), false);
}
c->set_lookup_index (saved_lookup_index); c->set_lookup_index (saved_lookup_index);
c->set_lookup_props (saved_lookup_props); c->set_lookup_props (saved_lookup_props);

View File

@ -77,11 +77,8 @@ inline bool SubstLookup::dispatch_recurse_func<hb_ot_apply_context_t> (hb_ot_app
c->set_lookup_props (l.get_props ()); c->set_lookup_props (l.get_props ());
bool ret = false; bool ret = false;
if (lookup_index < gsub->lookup_count) auto *accel = gsub->get_accel (lookup_index);
{ ret = accel && accel->apply (c, l.get_subtable_count (), false);
auto &accel = gsub->accels[lookup_index];
ret = accel.apply (c, l.get_subtable_count (), false);
}
c->set_lookup_index (saved_lookup_index); c->set_lookup_index (saved_lookup_index);
c->set_lookup_props (saved_lookup_props); c->set_lookup_props (saved_lookup_props);

View File

@ -4013,37 +4013,40 @@ struct Extension
struct hb_ot_layout_lookup_accelerator_t struct hb_ot_layout_lookup_accelerator_t
{ {
template <typename TLookup> template <typename TLookup>
void init (const TLookup &lookup) static hb_ot_layout_lookup_accelerator_t *create (const TLookup &lookup)
{ {
unsigned count = lookup.get_subtable_count (); unsigned count = lookup.get_subtable_count ();
subtables = (hb_accelerate_subtables_context_t::hb_applicable_t *)
hb_calloc (count, sizeof (hb_accelerate_subtables_context_t::hb_applicable_t));
if (unlikely (!subtables))
return;
hb_accelerate_subtables_context_t c_accelerate_subtables (subtables); unsigned size = sizeof (hb_ot_layout_lookup_accelerator_t) -
HB_VAR_ARRAY * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t) +
count * sizeof (hb_accelerate_subtables_context_t::hb_applicable_t);
auto *thiz = (hb_ot_layout_lookup_accelerator_t *) hb_calloc (1, size);
if (unlikely (!thiz))
return nullptr;
hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables);
lookup.dispatch (&c_accelerate_subtables); lookup.dispatch (&c_accelerate_subtables);
digest.init (); thiz->digest.init ();
for (auto& subtable : hb_iter (subtables, count)) for (auto& subtable : hb_iter (thiz->subtables, count))
digest.add (subtable.digest); thiz->digest.add (subtable.digest);
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
cache_user_idx = c_accelerate_subtables.cache_user_idx; thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
if (i != cache_user_idx) if (i != thiz->cache_user_idx)
subtables[i].apply_cached_func = subtables[i].apply_func; thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
#endif #endif
return thiz;
} }
void fini () { hb_free (subtables); }
bool may_have (hb_codepoint_t g) const bool may_have (hb_codepoint_t g) const
{ return digest.may_have (g); } { return digest.may_have (g); }
bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
{ {
if (unlikely (!subtables)) return false;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (use_cache) if (use_cache)
{ {
@ -4084,10 +4087,10 @@ struct hb_ot_layout_lookup_accelerator_t
hb_set_digest_t digest; hb_set_digest_t digest;
private: private:
hb_accelerate_subtables_context_t::hb_applicable_t *subtables;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
unsigned cache_user_idx = (unsigned) -1; unsigned cache_user_idx = (unsigned) -1;
#endif #endif
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
}; };
template <typename Types> template <typename Types>
@ -4450,28 +4453,47 @@ struct GSUBGPOS
this->lookup_count = table->get_lookup_count (); this->lookup_count = table->get_lookup_count ();
this->accels = (hb_ot_layout_lookup_accelerator_t *) hb_calloc (this->lookup_count, sizeof (hb_ot_layout_lookup_accelerator_t)); this->accels = (hb_atomic_ptr_t<hb_ot_layout_lookup_accelerator_t> *) hb_calloc (this->lookup_count, sizeof (*accels));
if (unlikely (!this->accels)) if (unlikely (!this->accels))
{ {
this->lookup_count = 0; this->lookup_count = 0;
this->table.destroy (); this->table.destroy ();
this->table = hb_blob_get_empty (); this->table = hb_blob_get_empty ();
} }
for (unsigned int i = 0; i < this->lookup_count; i++)
this->accels[i].init (table->get_lookup (i));
} }
~accelerator_t () ~accelerator_t ()
{ {
for (unsigned int i = 0; i < this->lookup_count; i++) for (unsigned int i = 0; i < this->lookup_count; i++)
this->accels[i].fini (); hb_free (this->accels[i]);
hb_free (this->accels); hb_free (this->accels);
this->table.destroy (); this->table.destroy ();
} }
hb_ot_layout_lookup_accelerator_t *get_accel (unsigned lookup_index) const
{
if (unlikely (lookup_index >= lookup_count)) return nullptr;
retry:
auto *accel = accels[lookup_index].get_acquire ();
if (unlikely (!accel))
{
accel = hb_ot_layout_lookup_accelerator_t::create (table->get_lookup (lookup_index));
if (unlikely (!accel))
return nullptr;
if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
{
hb_free (accel);
goto retry;
}
}
return accel;
}
hb_blob_ptr_t<T> table; hb_blob_ptr_t<T> table;
unsigned int lookup_count; unsigned int lookup_count;
hb_ot_layout_lookup_accelerator_t *accels; hb_atomic_ptr_t<hb_ot_layout_lookup_accelerator_t> *accels;
}; };
protected: protected:

View File

@ -1487,11 +1487,13 @@ hb_ot_layout_lookup_would_substitute (hb_face_t *face,
unsigned int glyphs_length, unsigned int glyphs_length,
hb_bool_t zero_context) hb_bool_t zero_context)
{ {
if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false; auto &gsub = face->table.GSUB;
if (unlikely (lookup_index >= gsub->lookup_count)) return false;
OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context);
const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index); const OT::SubstLookup& l = gsub->table->get_lookup (lookup_index);
return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]); auto *accel = gsub->get_accel (lookup_index);
return accel && l.would_apply (&c, accel);
} }
@ -1830,11 +1832,9 @@ struct GSUBProxy
typedef OT::SubstLookup Lookup; typedef OT::SubstLookup Lookup;
GSUBProxy (hb_face_t *face) : GSUBProxy (hb_face_t *face) :
table (*face->table.GSUB->table), accel (*face->table.GSUB) {}
accels (face->table.GSUB->accels) {}
const GSUB &table; const GSUB::accelerator_t &accel;
const OT::hb_ot_layout_lookup_accelerator_t *accels;
}; };
struct GPOSProxy struct GPOSProxy
@ -1844,11 +1844,9 @@ struct GPOSProxy
typedef OT::PosLookup Lookup; typedef OT::PosLookup Lookup;
GPOSProxy (hb_face_t *face) : GPOSProxy (hb_face_t *face) :
table (*face->table.GPOS->table), accel (*face->table.GPOS) {}
accels (face->table.GPOS->accels) {}
const GPOS &table; const GPOS::accelerator_t &accel;
const OT::hb_ot_layout_lookup_accelerator_t *accels;
}; };
@ -1911,12 +1909,13 @@ apply_string (OT::hb_ot_apply_context_t *c,
const typename Proxy::Lookup &lookup, const typename Proxy::Lookup &lookup,
const OT::hb_ot_layout_lookup_accelerator_t &accel) const OT::hb_ot_layout_lookup_accelerator_t &accel)
{ {
bool ret = false;
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
unsigned subtable_count = lookup.get_subtable_count (); unsigned subtable_count = lookup.get_subtable_count ();
if (unlikely (!buffer->len || !c->lookup_mask)) if (unlikely (!buffer->len || !c->lookup_mask))
return ret; return false;
bool ret = false;
c->set_lookup_props (lookup.get_props ()); c->set_lookup_props (lookup.get_props ());
@ -1962,6 +1961,10 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
auto &lookup = lookups[table_index][i]; auto &lookup = lookups[table_index][i];
unsigned int lookup_index = lookup.index; unsigned int lookup_index = lookup.index;
auto *accel = proxy.accel.get_accel (lookup_index);
if (unlikely (!accel)) continue;
if (buffer->messaging () && if (buffer->messaging () &&
!buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue; !buffer->message (font, "start lookup %u feature '%c%c%c%c'", lookup_index, HB_UNTAG (lookup.feature_tag))) continue;
@ -1969,7 +1972,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
* (plus some past glyphs). * (plus some past glyphs).
* *
* Only try applying the lookup if there is any overlap. */ * Only try applying the lookup if there is any overlap. */
if (proxy.accels[lookup_index].digest.may_have (c.digest)) if (accel->digest.may_have (c.digest))
{ {
c.set_lookup_index (lookup_index); c.set_lookup_index (lookup_index);
c.set_lookup_mask (lookup.mask); c.set_lookup_mask (lookup.mask);
@ -1979,8 +1982,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
c.set_per_syllable (lookup.per_syllable); c.set_per_syllable (lookup.per_syllable);
apply_string<Proxy> (&c, apply_string<Proxy> (&c,
proxy.table.get_lookup (lookup_index), proxy.accel.table->get_lookup (lookup_index),
proxy.accels[lookup_index]); *accel);
} }
else if (buffer->messaging ()) else if (buffer->messaging ())
(void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag)); (void) buffer->message (font, "skipped lookup %u feature '%c%c%c%c' because no glyph matches", lookup_index, HB_UNTAG (lookup.feature_tag));

View File

@ -228,7 +228,7 @@ struct arabic_fallback_plan_t
hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS];
OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS];
OT::hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; OT::hb_ot_layout_lookup_accelerator_t *accel_array[ARABIC_FALLBACK_MAX_LOOKUPS];
}; };
#if defined(_WIN32) && !defined(HB_NO_WIN1256) #if defined(_WIN32) && !defined(HB_NO_WIN1256)
@ -278,7 +278,7 @@ arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan HB_UNUS
fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset)); fallback_plan->lookup_array[j] = const_cast<OT::SubstLookup*> (&(&manifest+manifest[i].lookupOffset));
if (fallback_plan->lookup_array[j]) if (fallback_plan->lookup_array[j])
{ {
fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); fallback_plan->accel_array[j] = OT::hb_ot_layout_lookup_accelerator_t::create (*fallback_plan->lookup_array[j]);
j++; j++;
} }
} }
@ -308,7 +308,7 @@ arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan,
fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i);
if (fallback_plan->lookup_array[j]) if (fallback_plan->lookup_array[j])
{ {
fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); fallback_plan->accel_array[j] = OT::hb_ot_layout_lookup_accelerator_t::create (*fallback_plan->lookup_array[j]);
j++; j++;
} }
} }
@ -355,7 +355,7 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i]) if (fallback_plan->lookup_array[i])
{ {
fallback_plan->accel_array[i].fini (); hb_free (fallback_plan->accel_array[i]);
if (fallback_plan->free_lookups) if (fallback_plan->free_lookups)
hb_free (fallback_plan->lookup_array[i]); hb_free (fallback_plan->lookup_array[i]);
} }
@ -372,9 +372,10 @@ arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i]) { if (fallback_plan->lookup_array[i]) {
c.set_lookup_mask (fallback_plan->mask_array[i]); c.set_lookup_mask (fallback_plan->mask_array[i]);
if (fallback_plan->accel_array[i])
hb_ot_layout_substitute_lookup (&c, hb_ot_layout_substitute_lookup (&c,
*fallback_plan->lookup_array[i], *fallback_plan->lookup_array[i],
fallback_plan->accel_array[i]); *fallback_plan->accel_array[i]);
} }
} }