blob: ea2431a947993103f5a5c74b55ae426b0a91a6fc [file] [log] [blame]
Garret Rieger1584d3c2020-10-28 17:49:09 -07001/*
2 * Copyright © 2020 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 *
24 * Google Author(s): Garret Rieger
25 */
26
27#ifndef HB_REPACKER_HH
28#define HB_REPACKER_HH
29
30#include "hb-open-type.hh"
Garret Rieger00f393d2020-10-29 14:58:34 -070031#include "hb-map.hh"
Garret Rieger5d3511e2020-11-05 10:34:26 -080032#include "hb-priority-queue.hh"
Garret Rieger1584d3c2020-10-28 17:49:09 -070033#include "hb-serialize.hh"
34#include "hb-vector.hh"
Garret Rieger20b02a62022-06-24 18:58:17 +000035#include "graph/graph.hh"
Garret Rieger3f7a74f2022-07-19 21:50:13 +000036#include "graph/gsubgpos-graph.hh"
Garret Rieger20b02a62022-06-24 18:58:17 +000037#include "graph/serialize.hh"
38
39using graph::graph_t;
Garret Rieger1584d3c2020-10-28 17:49:09 -070040
Garret Rieger6bc64312021-10-12 13:13:32 -070041/*
42 * For a detailed writeup on the overflow resolution algorithm see:
Behdad Esfahboda7a36082021-10-12 16:11:25 -070043 * docs/repacker.md
Garret Rieger6bc64312021-10-12 13:13:32 -070044 */
Garret Rieger1584d3c2020-10-28 17:49:09 -070045
Qunxin Liua35757c2022-02-02 10:30:34 -080046static inline
Garret Riegerb1d38a62022-07-19 23:33:16 +000047void _make_extensions (hb_tag_t table_tag, graph_t& sorted_graph, hb_vector_t<char>& buffer)
Garret Rieger3f7a74f2022-07-19 21:50:13 +000048{
49 // TODO: Move this into graph or gsubgpos graph?
50 hb_hashmap_t<unsigned, graph::Lookup*> lookups;
51 find_lookups (sorted_graph, lookups);
52
53 for (auto p : lookups.iter ())
54 {
Garret Riegerb1d38a62022-07-19 23:33:16 +000055 p.second->make_extension (table_tag, sorted_graph, p.first, buffer);
Garret Rieger3f7a74f2022-07-19 21:50:13 +000056 }
57}
58
59static inline
Garret Rieger70785602022-06-24 19:20:20 +000060bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& overflows,
Qunxin Liua35757c2022-02-02 10:30:34 -080061 graph_t& sorted_graph)
Garret Rieger375a6c82021-09-29 18:14:57 -070062{
Garret Rieger02b12d72021-12-06 15:23:35 -080063 unsigned space = 0;
64 hb_set_t roots_to_isolate;
65
Garret Rieger375a6c82021-09-29 18:14:57 -070066 for (int i = overflows.length - 1; i >= 0; i--)
67 {
Garret Rieger70785602022-06-24 19:20:20 +000068 const graph::overflow_record_t& r = overflows[i];
Garret Rieger375a6c82021-09-29 18:14:57 -070069
Garret Rieger02b12d72021-12-06 15:23:35 -080070 unsigned root;
71 unsigned overflow_space = sorted_graph.space_for (r.parent, &root);
72 if (!overflow_space) continue;
Garret Rieger3e4a2502021-12-06 16:00:15 -080073 if (sorted_graph.num_roots_for_space (overflow_space) <= 1) continue;
Garret Rieger375a6c82021-09-29 18:14:57 -070074
Garret Rieger02b12d72021-12-06 15:23:35 -080075 if (!space) {
76 space = overflow_space;
77 }
78
79 if (space == overflow_space)
80 roots_to_isolate.add(root);
Garret Rieger375a6c82021-09-29 18:14:57 -070081 }
Garret Rieger02b12d72021-12-06 15:23:35 -080082
83 if (!roots_to_isolate) return false;
84
Garret Rieger3e4a2502021-12-06 16:00:15 -080085 unsigned maximum_to_move = hb_max ((sorted_graph.num_roots_for_space (space) / 2u), 1u);
86 if (roots_to_isolate.get_population () > maximum_to_move) {
87 // Only move at most half of the roots in a space at a time.
88 unsigned extra = roots_to_isolate.get_population () - maximum_to_move;
89 while (extra--) {
90 unsigned root = HB_SET_VALUE_INVALID;
91 roots_to_isolate.previous (&root);
92 roots_to_isolate.del (root);
93 }
94 }
95
Garret Rieger02b12d72021-12-06 15:23:35 -080096 DEBUG_MSG (SUBSET_REPACK, nullptr,
97 "Overflow in space %d (%d roots). Moving %d roots to space %d.",
98 space,
99 sorted_graph.num_roots_for_space (space),
100 roots_to_isolate.get_population (),
101 sorted_graph.next_space ());
102
103 sorted_graph.isolate_subgraph (roots_to_isolate);
104 sorted_graph.move_to_new_space (roots_to_isolate);
105
106 return true;
Garret Rieger375a6c82021-09-29 18:14:57 -0700107}
108
Qunxin Liua35757c2022-02-02 10:30:34 -0800109static inline
Garret Rieger70785602022-06-24 19:20:20 +0000110bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
Qunxin Liua35757c2022-02-02 10:30:34 -0800111 hb_set_t& priority_bumped_parents,
112 graph_t& sorted_graph)
Garret Rieger8d8b7452021-09-07 16:52:37 -0700113{
114 bool resolution_attempted = false;
115
116 // Try resolving the furthest overflows first.
117 for (int i = overflows.length - 1; i >= 0; i--)
118 {
Garret Rieger70785602022-06-24 19:20:20 +0000119 const graph::overflow_record_t& r = overflows[i];
Garret Rieger8d8b7452021-09-07 16:52:37 -0700120 const auto& child = sorted_graph.vertices_[r.child];
121 if (child.is_shared ())
122 {
123 // The child object is shared, we may be able to eliminate the overflow
124 // by duplicating it.
Garret Riegerfe155de2021-09-10 14:55:24 -0700125 if (!sorted_graph.duplicate (r.parent, r.child)) continue;
Garret Rieger8d8b7452021-09-07 16:52:37 -0700126 return true;
127 }
128
129 if (child.is_leaf () && !priority_bumped_parents.has (r.parent))
130 {
131 // This object is too far from it's parent, attempt to move it closer.
132 //
133 // TODO(garretrieger): initially limiting this to leaf's since they can be
134 // moved closer with fewer consequences. However, this can
135 // likely can be used for non-leafs as well.
Garret Rieger8d8b7452021-09-07 16:52:37 -0700136 // TODO(garretrieger): also try lowering priority of the parent. Make it
137 // get placed further up in the ordering, closer to it's children.
138 // this is probably preferable if the total size of the parent object
139 // is < then the total size of the children (and the parent can be moved).
140 // Since in that case moving the parent will cause a smaller increase in
141 // the length of other offsets.
Garret Riegerbe2c4882021-12-09 15:44:06 -0800142 if (sorted_graph.raise_childrens_priority (r.parent)) {
143 priority_bumped_parents.add (r.parent);
144 resolution_attempted = true;
145 }
Garret Rieger8d8b7452021-09-07 16:52:37 -0700146 continue;
147 }
148
149 // TODO(garretrieger): add additional offset resolution strategies
150 // - Promotion to extension lookups.
Garret Rieger8d8b7452021-09-07 16:52:37 -0700151 // - Table splitting.
152 }
153
154 return resolution_attempted;
155}
Garret Rieger1584d3c2020-10-28 17:49:09 -0700156
157/*
Garret Riegerd3e2ba72020-11-11 13:50:18 -0800158 * Attempts to modify the topological sorting of the provided object graph to
159 * eliminate offset overflows in the links between objects of the graph. If a
160 * non-overflowing ordering is found the updated graph is serialized it into the
161 * provided serialization context.
162 *
163 * If necessary the structure of the graph may be modified in ways that do not
164 * affect the functionality of the graph. For example shared objects may be
165 * duplicated.
Garret Rieger6bc64312021-10-12 13:13:32 -0700166 *
167 * For a detailed writeup describing how the algorithm operates see:
Behdad Esfahboda7a36082021-10-12 16:11:25 -0700168 * docs/repacker.md
Garret Rieger1584d3c2020-10-28 17:49:09 -0700169 */
Qunxin Liua35757c2022-02-02 10:30:34 -0800170template<typename T>
Garret Riegerfa966bc2021-12-06 12:54:19 -0800171inline hb_blob_t*
Qunxin Liua35757c2022-02-02 10:30:34 -0800172hb_resolve_overflows (const T& packed,
Garret Rieger41bbf282021-09-08 10:14:00 -0700173 hb_tag_t table_tag,
Garret Rieger9c251892022-07-13 22:55:58 +0000174 unsigned max_rounds = 20) {
Garret Rieger3f7a74f2022-07-19 21:50:13 +0000175 printf("Resolving overflows!\n");
Garret Rieger1584d3c2020-10-28 17:49:09 -0700176 graph_t sorted_graph (packed);
Garret Riegeraf74ab42022-06-16 18:12:09 +0000177 sorted_graph.sort_shortest_distance ();
178
Garret Rieger70785602022-06-24 19:20:20 +0000179 bool will_overflow = graph::will_overflow (sorted_graph);
180 if (!will_overflow)
Garret Riegerd3e2ba72020-11-11 13:50:18 -0800181 {
Garret Rieger20b02a62022-06-24 18:58:17 +0000182 return graph::serialize (sorted_graph);
Garret Riegerd3e2ba72020-11-11 13:50:18 -0800183 }
Garret Rieger75414e82020-11-05 16:39:23 -0800184
Garret Riegerb1d38a62022-07-19 23:33:16 +0000185 hb_vector_t<char> extension_buffer;
Garret Rieger41bbf282021-09-08 10:14:00 -0700186 if ((table_tag == HB_OT_TAG_GPOS
187 || table_tag == HB_OT_TAG_GSUB)
Garret Rieger70785602022-06-24 19:20:20 +0000188 && will_overflow)
Garret Rieger41bbf282021-09-08 10:14:00 -0700189 {
Garret Riegerb1d38a62022-07-19 23:33:16 +0000190 _make_extensions (table_tag, sorted_graph, extension_buffer);
Garret Rieger816c5302021-09-28 16:04:27 -0700191 DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs.");
Garret Rieger401066b2022-07-06 18:44:40 +0000192 if (sorted_graph.assign_spaces ())
Garret Rieger41bbf282021-09-08 10:14:00 -0700193 sorted_graph.sort_shortest_distance ();
Garret Rieger41bbf282021-09-08 10:14:00 -0700194 }
195
Garret Rieger75414e82020-11-05 16:39:23 -0800196 unsigned round = 0;
Garret Rieger70785602022-06-24 19:20:20 +0000197 hb_vector_t<graph::overflow_record_t> overflows;
Garret Rieger75414e82020-11-05 16:39:23 -0800198 // TODO(garretrieger): select a good limit for max rounds.
Garret Riegerbb5c80a2020-11-10 14:11:57 -0800199 while (!sorted_graph.in_error ()
Garret Rieger70785602022-06-24 19:20:20 +0000200 && graph::will_overflow (sorted_graph, &overflows)
Garret Rieger9c251892022-07-13 22:55:58 +0000201 && round < max_rounds) {
Garret Rieger41bbf282021-09-08 10:14:00 -0700202 DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Overflow resolution round %d ===", round);
Garret Rieger70785602022-06-24 19:20:20 +0000203 print_overflows (sorted_graph, overflows);
Garret Rieger75414e82020-11-05 16:39:23 -0800204
Garret Riegera7a86a62020-11-06 16:22:48 -0800205 hb_set_t priority_bumped_parents;
Garret Rieger375a6c82021-09-29 18:14:57 -0700206
207 if (!_try_isolating_subgraphs (overflows, sorted_graph))
Garret Rieger75414e82020-11-05 16:39:23 -0800208 {
Garret Rieger9c251892022-07-13 22:55:58 +0000209 // Don't count space isolation towards round limit. Only increment
210 // round counter if space isolation made no changes.
211 round++;
Garret Rieger375a6c82021-09-29 18:14:57 -0700212 if (!_process_overflows (overflows, priority_bumped_parents, sorted_graph))
213 {
214 DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :(");
215 break;
216 }
Garret Rieger75414e82020-11-05 16:39:23 -0800217 }
218
Garret Rieger8d8b7452021-09-07 16:52:37 -0700219 sorted_graph.sort_shortest_distance ();
Garret Rieger1584d3c2020-10-28 17:49:09 -0700220 }
221
Garret Riegerbb5c80a2020-11-10 14:11:57 -0800222 if (sorted_graph.in_error ())
223 {
Garret Riegerfa966bc2021-12-06 12:54:19 -0800224 DEBUG_MSG (SUBSET_REPACK, nullptr, "Sorted graph in error state.");
225 return nullptr;
Garret Riegerbb5c80a2020-11-10 14:11:57 -0800226 }
Garret Rieger02c4a512021-09-07 13:22:19 -0700227
Garret Rieger70785602022-06-24 19:20:20 +0000228 if (graph::will_overflow (sorted_graph))
Garret Rieger02c4a512021-09-07 13:22:19 -0700229 {
Garret Rieger02c4a512021-09-07 13:22:19 -0700230 DEBUG_MSG (SUBSET_REPACK, nullptr, "Offset overflow resolution failed.");
Garret Riegerfa966bc2021-12-06 12:54:19 -0800231 return nullptr;
Garret Rieger02c4a512021-09-07 13:22:19 -0700232 }
Garret Riegerfa966bc2021-12-06 12:54:19 -0800233
Garret Rieger20b02a62022-06-24 18:58:17 +0000234 return graph::serialize (sorted_graph);
Garret Rieger1584d3c2020-10-28 17:49:09 -0700235}
236
Garret Rieger1584d3c2020-10-28 17:49:09 -0700237#endif /* HB_REPACKER_HH */