[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,8 +121,7 @@ _hb_ot_font_destroy (void *font_data)
hb_ot_font_t *ot_font = (hb_ot_font_t *) font_data;
auto *cache = ot_font->advance_cache.get_relaxed ();
if (cache)
hb_free (cache);
hb_free (cache);
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 ());
bool ret = false;
if (lookup_index < gpos->lookup_count)
{
auto &accel = gpos->accels[lookup_index];
ret = accel.apply (c, l.get_subtable_count (), false);
}
auto *accel = gpos->get_accel (lookup_index);
ret = accel && accel->apply (c, l.get_subtable_count (), false);
c->set_lookup_index (saved_lookup_index);
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 ());
bool ret = false;
if (lookup_index < gsub->lookup_count)
{
auto &accel = gsub->accels[lookup_index];
ret = accel.apply (c, l.get_subtable_count (), false);
}
auto *accel = gsub->get_accel (lookup_index);
ret = accel && accel->apply (c, l.get_subtable_count (), false);
c->set_lookup_index (saved_lookup_index);
c->set_lookup_props (saved_lookup_props);

View File

@ -4013,37 +4013,40 @@ struct Extension
struct hb_ot_layout_lookup_accelerator_t
{
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 ();
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);
digest.init ();
for (auto& subtable : hb_iter (subtables, count))
digest.add (subtable.digest);
thiz->digest.init ();
for (auto& subtable : hb_iter (thiz->subtables, count))
thiz->digest.add (subtable.digest);
#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++)
if (i != cache_user_idx)
subtables[i].apply_cached_func = subtables[i].apply_func;
if (i != thiz->cache_user_idx)
thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
#endif
return thiz;
}
void fini () { hb_free (subtables); }
bool may_have (hb_codepoint_t g) const
{ return digest.may_have (g); }
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
if (use_cache)
{
@ -4084,10 +4087,10 @@ struct hb_ot_layout_lookup_accelerator_t
hb_set_digest_t digest;
private:
hb_accelerate_subtables_context_t::hb_applicable_t *subtables;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
unsigned cache_user_idx = (unsigned) -1;
#endif
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
};
template <typename Types>
@ -4450,28 +4453,47 @@ struct GSUBGPOS
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))
{
this->lookup_count = 0;
this->table.destroy ();
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 ()
{
for (unsigned int i = 0; i < this->lookup_count; i++)
this->accels[i].fini ();
hb_free (this->accels[i]);
hb_free (this->accels);
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;
unsigned int lookup_count;
hb_ot_layout_lookup_accelerator_t *accels;
hb_atomic_ptr_t<hb_ot_layout_lookup_accelerator_t> *accels;
};
protected:

View File

@ -1487,11 +1487,13 @@ hb_ot_layout_lookup_would_substitute (hb_face_t *face,
unsigned int glyphs_length,
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);
const OT::SubstLookup& l = face->table.GSUB->table->get_lookup (lookup_index);
return l.would_apply (&c, &face->table.GSUB->accels[lookup_index]);
const OT::SubstLookup& l = gsub->table->get_lookup (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;
GSUBProxy (hb_face_t *face) :
table (*face->table.GSUB->table),
accels (face->table.GSUB->accels) {}
accel (*face->table.GSUB) {}
const GSUB &table;
const OT::hb_ot_layout_lookup_accelerator_t *accels;
const GSUB::accelerator_t &accel;
};
struct GPOSProxy
@ -1844,11 +1844,9 @@ struct GPOSProxy
typedef OT::PosLookup Lookup;
GPOSProxy (hb_face_t *face) :
table (*face->table.GPOS->table),
accels (face->table.GPOS->accels) {}
accel (*face->table.GPOS) {}
const GPOS &table;
const OT::hb_ot_layout_lookup_accelerator_t *accels;
const GPOS::accelerator_t &accel;
};
@ -1911,12 +1909,13 @@ apply_string (OT::hb_ot_apply_context_t *c,
const typename Proxy::Lookup &lookup,
const OT::hb_ot_layout_lookup_accelerator_t &accel)
{
bool ret = false;
hb_buffer_t *buffer = c->buffer;
unsigned subtable_count = lookup.get_subtable_count ();
if (unlikely (!buffer->len || !c->lookup_mask))
return ret;
return false;
bool ret = false;
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];
unsigned int lookup_index = lookup.index;
auto *accel = proxy.accel.get_accel (lookup_index);
if (unlikely (!accel)) continue;
if (buffer->messaging () &&
!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).
*
* 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_mask (lookup.mask);
@ -1979,8 +1982,8 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
c.set_per_syllable (lookup.per_syllable);
apply_string<Proxy> (&c,
proxy.table.get_lookup (lookup_index),
proxy.accels[lookup_index]);
proxy.accel.table->get_lookup (lookup_index),
*accel);
}
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));

View File

@ -228,7 +228,7 @@ struct arabic_fallback_plan_t
hb_mask_t mask_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)
@ -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));
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++;
}
}
@ -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);
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++;
}
}
@ -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++)
if (fallback_plan->lookup_array[i])
{
fallback_plan->accel_array[i].fini ();
hb_free (fallback_plan->accel_array[i]);
if (fallback_plan->free_lookups)
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++)
if (fallback_plan->lookup_array[i]) {
c.set_lookup_mask (fallback_plan->mask_array[i]);
hb_ot_layout_substitute_lookup (&c,
*fallback_plan->lookup_array[i],
fallback_plan->accel_array[i]);
if (fallback_plan->accel_array[i])
hb_ot_layout_substitute_lookup (&c,
*fallback_plan->lookup_array[i],
*fallback_plan->accel_array[i]);
}
}