Merge pull request #5040 from harfbuzz/aat-set

[aat] Speed up subchain skipping using exact sets
This commit is contained in:
Behdad Esfahbod 2025-02-04 22:48:58 +00:00 committed by GitHub
commit b30c65e271
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 75 additions and 33 deletions

View file

@ -63,10 +63,10 @@ struct hb_aat_apply_context_t :
const ankr *ankr_table;
const OT::GDEF *gdef_table;
const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
hb_set_digest_t buffer_digest = hb_set_digest_t::full ();
hb_set_t buffer_digest;
const hb_set_t *left_set = nullptr;
const hb_set_t *right_set = nullptr;
hb_set_digest_t machine_glyph_set = hb_set_digest_t::full ();
const hb_set_t *machine_glyph_set = nullptr;
hb_aat_class_cache_t *machine_class_cache = nullptr;
hb_mask_t subtable_flags = 0;

View file

@ -172,7 +172,7 @@ struct RearrangementSubtable
StateTableDriver<Types, EntryData> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.intersects (c->machine_glyph_set))
!c->buffer_digest.may_intersect (*c->machine_glyph_set))
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
@ -340,7 +340,7 @@ struct ContextualSubtable
StateTableDriver<Types, EntryData> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.intersects (c->machine_glyph_set))
!c->buffer_digest.may_intersect (*c->machine_glyph_set))
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
@ -606,7 +606,7 @@ struct LigatureSubtable
StateTableDriver<Types, EntryData> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.intersects (c->machine_glyph_set))
!c->buffer_digest.may_intersect (*c->machine_glyph_set))
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
@ -885,7 +885,7 @@ struct InsertionSubtable
StateTableDriver<Types, EntryData> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!c->buffer_digest.intersects (c->machine_glyph_set))
!c->buffer_digest.may_intersect (*c->machine_glyph_set))
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
@ -947,19 +947,21 @@ struct hb_accelerate_subtables_context_t :
friend struct hb_aat_layout_lookup_accelerator_t;
public:
hb_set_digest_t digest;
hb_set_t glyph_set;
mutable hb_aat_class_cache_t class_cache;
template <typename T>
auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
(
obj_.machine.collect_glyphs (this->digest, num_glyphs)
)
((
glyph_set.init (),
obj_.machine.collect_glyphs (glyph_set, num_glyphs)
))
template <typename T>
void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
{
digest = digest.full ();
glyph_set.init ();
glyph_set.invert ();
}
template <typename T>
@ -972,6 +974,7 @@ struct hb_accelerate_subtables_context_t :
void
fini ()
{
glyph_set.fini ();
}
};
@ -1185,7 +1188,7 @@ struct Chain
hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
goto skip;
c->subtable_flags = subtable_flags;
c->machine_glyph_set = accel ? accel->subtables[i].digest : hb_set_digest_t::full ();
c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_set_t);
c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
@ -1416,7 +1419,7 @@ struct mortmorx
c->buffer->unsafe_to_concat ();
c->buffer_digest = c->buffer->digest ();
c->buffer_digest = c->buffer->set ();
c->set_lookup_index (0);
const Chain<Types> *chain = &firstChain;

View file

@ -227,6 +227,13 @@ struct hb_bit_page_t
return false;
return true;
}
bool may_intersect (const hb_bit_page_t &other) const
{
for (unsigned i = 0; i < len (); i++)
if (v[i] & other.v[i])
return true;
return false;
}
bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
bool is_subset (const hb_bit_page_t &larger_page) const
{

View file

@ -139,6 +139,9 @@ struct hb_bit_set_invertible_t
hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_bit_set_invertible_t &other) const
{ return inverted || other.inverted || s.may_intersect (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{
hb_codepoint_t c = first - 1;

View file

@ -297,9 +297,9 @@ struct hb_bit_set_t
unsigned int write_index = 0;
for (unsigned int i = 0; i < page_map.length; i++)
{
int m = (int) page_map[i].major;
int m = (int) page_map.arrayZ[i].major;
if (m < ds || de < m)
page_map[write_index++] = page_map[i];
page_map.arrayZ[write_index++] = page_map.arrayZ[i];
}
compact (compact_workspace, write_index);
resize (write_index);
@ -358,6 +358,26 @@ struct hb_bit_set_t
hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_bit_set_t &other) const
{
unsigned int na = pages.length;
unsigned int nb = other.pages.length;
unsigned int a = 0, b = 0;
for (; a < na && b < nb; )
{
if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major &&
page_at (a).may_intersect (other.page_at (b)))
return true;
if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
a++;
else
b++;
}
return false;
}
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{
hb_codepoint_t c = first - 1;
@ -389,7 +409,7 @@ struct hb_bit_set_t
{
if (page_at (a).is_empty ()) { a++; continue; }
if (other.page_at (b).is_empty ()) { b++; continue; }
if (page_map[a].major != other.page_map[b].major ||
if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major ||
!page_at (a).is_equal (other.page_at (b)))
return false;
a++;
@ -412,8 +432,8 @@ struct hb_bit_set_t
uint32_t spi = 0;
for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
{
uint32_t spm = page_map[spi].major;
uint32_t lpm = larger_set.page_map[lpi].major;
uint32_t spm = page_map.arrayZ[spi].major;
uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
auto sp = page_at (spi);
if (spm < lpm && !sp.is_empty ())
@ -503,7 +523,7 @@ struct hb_bit_set_t
for (; a < na && b < nb; )
{
if (page_map[a].major == other.page_map[b].major)
if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
{
if (!passthru_left)
{
@ -512,7 +532,7 @@ struct hb_bit_set_t
// passthru_left is set since no left side pages will be removed
// in that case.
if (write_index < a)
page_map[write_index] = page_map[a];
page_map.arrayZ[write_index] = page_map.arrayZ[a];
write_index++;
}
@ -520,7 +540,7 @@ struct hb_bit_set_t
a++;
b++;
}
else if (page_map[a].major < other.page_map[b].major)
else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
{
if (passthru_left)
count++;
@ -765,8 +785,8 @@ struct hb_bit_set_t
unsigned int initial_size = size;
for (unsigned int i = start_page; i < page_map.length && size; i++)
{
uint32_t base = major_start (page_map[i].major);
unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size);
uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size);
out += n;
size -= n;
start_page_value = 0;
@ -814,8 +834,8 @@ struct hb_bit_set_t
hb_codepoint_t next_value = codepoint + 1;
for (unsigned int i=start_page; i<page_map.length && size; i++)
{
uint32_t base = major_start (page_map[i].major);
unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value);
uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map.arrayZ[i].index].write_inverted (base, start_page_value, out, size, &next_value);
out += n;
size -= n;
start_page_value = 0;
@ -846,8 +866,8 @@ struct hb_bit_set_t
unsigned count = pages.length;
for (unsigned i = 0; i < count; i++)
{
const auto& map = page_map[i];
const auto& page = pages[map.index];
const auto& map = page_map.arrayZ[i];
const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_min ();
@ -859,8 +879,8 @@ struct hb_bit_set_t
unsigned count = pages.length;
for (signed i = count - 1; i >= 0; i--)
{
const auto& map = page_map[(unsigned) i];
const auto& page = pages[map.index];
const auto& map = page_map.arrayZ[(unsigned) i];
const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_max ();
@ -961,7 +981,7 @@ struct hb_bit_set_t
return nullptr;
last_page_lookup = i;
return &pages.arrayZ[page_map[i].index];
return &pages.arrayZ[page_map.arrayZ[i].index];
}
page_t &page_at (unsigned int i)
{

View file

@ -32,6 +32,7 @@
#include "hb.hh"
#include "hb-unicode.hh"
#include "hb-set.hh"
#include "hb-set-digest.hh"
@ -194,7 +195,12 @@ struct hb_buffer_t
hb_set_digest_t digest () const
{
hb_set_digest_t d;
d.init ();
d.add_array (&info[0].codepoint, len, sizeof (info[0]));
return d;
}
hb_set_t set () const
{
hb_set_t d;
d.add_array (&info[0].codepoint, len, sizeof (info[0]));
return d;
}

View file

@ -2033,7 +2033,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 (accel->digest.intersects (c.digest))
if (accel->digest.may_intersect (c.digest))
{
c.set_lookup_index (lookup_index);
c.set_lookup_mask (lookup.mask, false);

View file

@ -158,7 +158,7 @@ struct hb_set_digest_t
return true;
}
bool intersects (const hb_set_digest_t &o) const
bool may_intersect (const hb_set_digest_t &o) const
{
for (unsigned i = 0; i < n; i++)
if (!(masks[i] & o.masks[i]))

View file

@ -120,6 +120,9 @@ struct hb_sparseset_t
hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_sparseset_t &other) const
{ return s.may_intersect (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{ return s.intersects (first, last); }