[repacker] count subtable size in each group of consecutive layers for extension promotion decisions.

Enforce that the following groups are all <64k in size:
- LookupList + Lookups
- Lookups + SubTables
- SubTables + Descendants
This commit is contained in:
Garret Rieger 2022-07-25 20:46:49 +00:00
parent 0a5b69b255
commit e4fd5ff727
3 changed files with 40 additions and 14 deletions

View File

@ -484,15 +484,18 @@ struct graph_t
find_subgraph (link.objidx, subgraph); find_subgraph (link.objidx, subgraph);
} }
size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph) size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1)
{ {
if (subgraph.has (node_idx)) return 0; if (subgraph.has (node_idx)) return 0;
subgraph.add (node_idx); subgraph.add (node_idx);
const auto& o = vertices_[node_idx].obj; const auto& o = vertices_[node_idx].obj;
size_t size = o.tail - o.head; size_t size = o.tail - o.head;
if (max_depth == 0)
return size;
for (const auto& link : o.all_links ()) for (const auto& link : o.all_links ())
size += find_subgraph_size (link.objidx, subgraph); size += find_subgraph_size (link.objidx, subgraph, max_depth - 1);
return size; return size;
} }

View File

@ -73,7 +73,11 @@ bool _promote_extensions_if_needed (graph::make_extension_context_t& ext_context
// TODO(garretrieger): support extension demotion, then consider all lookups. Requires advanced algo. // TODO(garretrieger): support extension demotion, then consider all lookups. Requires advanced algo.
// TODO(garretrieger): also support extension promotion during iterative resolution phase, then // TODO(garretrieger): also support extension promotion during iterative resolution phase, then
// we can use a less conservative threshold here. // we can use a less conservative threshold here.
// TODO(grieger): skip this for the 24 bit case.
// TODO(grieger): sort by # subtables / size instead (high to low). Goal is to get as many subtables
// as possible into space 0 to minimize the number of extension subtables added.
// A fully optimal solution will require a backpack problem dynamic programming type
// solution.
if (!ext_context.lookups) return true; if (!ext_context.lookups) return true;
hb_vector_t<hb_pair_t<unsigned, size_t>> lookup_sizes; hb_vector_t<hb_pair_t<unsigned, size_t>> lookup_sizes;
@ -90,31 +94,48 @@ bool _promote_extensions_if_needed (graph::make_extension_context_t& ext_context
lookup_sizes.qsort (compare_sizes); lookup_sizes.qsort (compare_sizes);
size_t accumlated_bytes = size_t lookup_list_size = ext_context.graph.vertices_[ext_context.lookup_list_index].table_size ();
ext_context.graph.vertices_[ext_context.lookup_list_index].table_size (); size_t l2_l3_size = lookup_list_size; // Lookup List + Lookups
size_t l3_l4_size = 0; // Lookups + SubTables
size_t l4_plus_size = 0; // SubTables + their descendants
// For the size of 16bit space, first add the size of all ext lookup subtables (as if all lookups // Start by assuming all lookups are using extension subtables, this size will be removed later
// were extensions). // if it's decided to not make a lookup extension.
for (auto p : lookup_sizes) for (auto p : lookup_sizes)
{ {
unsigned lookup_index = p.first; unsigned lookup_index = p.first;
accumlated_bytes += ext_context.lookups.get(lookup_index)->number_of_subtables () * 8; unsigned subtables_size = ext_context.lookups.get(lookup_index)->number_of_subtables () * 8;
l3_l4_size += subtables_size;
l4_plus_size += subtables_size;
} }
bool layers_full = false;
for (auto p : lookup_sizes) for (auto p : lookup_sizes)
{ {
unsigned lookup_index = p.first; unsigned lookup_index = p.first;
const graph::Lookup* lookup = ext_context.lookups.get(lookup_index); const graph::Lookup* lookup = ext_context.lookups.get(lookup_index);
if (lookup->is_extension (ext_context.table_tag)) if (lookup->is_extension (ext_context.table_tag))
// already extension, size is counted by the loop above. // already an extension so size is counted by the loop above.
continue; continue;
size_t size = p.second; if (!layers_full)
accumlated_bytes += size; {
// will not be an extension lookup so subtract size counted in the above loop. size_t lookup_size = ext_context.graph.vertices_[lookup_index].table_size ();
accumlated_bytes -= lookup->number_of_subtables () * 8; hb_set_t visited;
size_t subtables_size = ext_context.graph.find_subgraph_size (lookup_index, visited, 1) - lookup_size;
size_t remaining_size = p.second - subtables_size - lookup_size;
if (accumlated_bytes < (1 << 16)) continue; // this lookup fits with 64k, which won't overflow. l2_l3_size += lookup_size;
l3_l4_size += lookup_size + subtables_size;
l3_l4_size -= lookup->number_of_subtables () * 8;
l4_plus_size += subtables_size + remaining_size;
if (l2_l3_size < (1 << 16)
&& l3_l4_size < (1 << 16)
&& l4_plus_size < (1 << 16)) continue; // this lookup fits within all layers groups
layers_full = true;
}
if (!ext_context.lookups.get(lookup_index)->make_extension (ext_context, lookup_index)) if (!ext_context.lookups.get(lookup_index)->make_extension (ext_context, lookup_index))
return false; return false;

View File

@ -1483,4 +1483,6 @@ main (int argc, char **argv)
test_virtual_link (); test_virtual_link ();
test_shared_node_with_virtual_links (); test_shared_node_with_virtual_links ();
test_resolve_with_extension_promotion (); test_resolve_with_extension_promotion ();
// TODO(grieger): test with extensions already mixed in as well.
// TODO(grieger): test two layer ext promotion setup.
} }