blob: 9b51b794ce678e4ef153f656a5ca0820f00e55fa [file] [log] [blame]
Garret Riegerfbd183d2022-04-20 20:05:14 +00001#include "benchmark/benchmark.h"
Behdad Esfahbod33c990f2022-05-27 16:57:00 -06002#include <cassert>
Garret Rieger52d59bf2022-05-10 19:40:37 +00003#include <cstring>
Garret Riegerfbd183d2022-04-20 20:05:14 +00004
Qunxin Liu47094492022-08-31 12:01:32 -07005#ifdef HAVE_CONFIG_H
6#include "config.h"
7#endif
8
Garret Riegerfbd183d2022-04-20 20:05:14 +00009#include "hb-subset.h"
10
Garret Rieger178c6702022-04-20 21:19:54 +000011
Garret Riegerbc5129d2022-05-04 22:16:03 +000012enum operation_t
13{
14 subset_codepoints,
Qunxin Liu47094492022-08-31 12:01:32 -070015 subset_glyphs,
16 instance,
Garret Riegerbc5129d2022-05-04 22:16:03 +000017};
18
Qunxin Liu47094492022-08-31 12:01:32 -070019struct axis_location_t
20{
21 hb_tag_t axis_tag;
22 float axis_value;
23};
24
25static const axis_location_t
26_roboto_flex_instance_opts[] =
27{
28 {HB_TAG ('w', 'g', 'h', 't'), 600.f},
29 {HB_TAG ('w', 'd', 't', 'h'), 75.f},
30 {HB_TAG ('o', 'p', 's', 'z'), 90.f},
31 {HB_TAG ('G', 'R', 'A', 'D'), -100.f},
32 {HB_TAG ('s', 'l', 'n', 't'), -3.f},
33 {HB_TAG ('X', 'T', 'R', 'A'), 500.f},
34 {HB_TAG ('X', 'O', 'P', 'Q'), 150.f},
35 {HB_TAG ('Y', 'O', 'P', 'Q'), 100.f},
36 {HB_TAG ('Y', 'T', 'L', 'C'), 480.f},
37 {HB_TAG ('Y', 'T', 'U', 'C'), 600.f},
38 {HB_TAG ('Y', 'T', 'A', 'S'), 800.f},
39 {HB_TAG ('Y', 'T', 'D', 'E'), -50.f},
40 {HB_TAG ('Y', 'T', 'F', 'I'), 600.f},
41};
42
43static const axis_location_t
44_mplus_instance_opts[] =
45{
46 {HB_TAG ('w', 'g', 'h', 't'), 800.f},
47};
48
49template <typename Type, unsigned int n>
50static inline unsigned int ARRAY_LEN (const Type (&)[n]) { return n; }
51
Garret Rieger52d59bf2022-05-10 19:40:37 +000052#define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/"
53
54struct test_input_t
55{
56 const char *font_path;
Seigo Nonakac62d6f42023-03-01 19:52:57 +090057 unsigned max_subset_size;
Qunxin Liu47094492022-08-31 12:01:32 -070058 const axis_location_t *instance_opts;
Seigo Nonakac62d6f42023-03-01 19:52:57 +090059 unsigned num_instance_opts;
60} default_tests[] =
Garret Rieger52d59bf2022-05-10 19:40:37 +000061{
Seigo Nonakac62d6f42023-03-01 19:52:57 +090062 {SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf", 1000, nullptr, 0},
63 {SUBSET_FONT_BASE_PATH "Amiri-Regular.ttf", 4096, nullptr, 0},
64 {SUBSET_FONT_BASE_PATH "NotoNastaliqUrdu-Regular.ttf", 1400, nullptr, 0},
Qunxin Liu47094492022-08-31 12:01:32 -070065 {SUBSET_FONT_BASE_PATH "NotoSansDevanagari-Regular.ttf", 1000, nullptr, 0},
66 {SUBSET_FONT_BASE_PATH "Mplus1p-Regular.ttf", 10000, nullptr, 0},
67 {SUBSET_FONT_BASE_PATH "SourceHanSans-Regular_subset.otf", 10000, nullptr, 0},
68 {SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf", 2000, nullptr, 0},
Seigo Nonakac62d6f42023-03-01 19:52:57 +090069 {SUBSET_FONT_BASE_PATH "AdobeVFPrototype.otf", 300, nullptr, 0},
Qunxin Liu47094492022-08-31 12:01:32 -070070 {SUBSET_FONT_BASE_PATH "MPLUS1-Variable.ttf", 6000, _mplus_instance_opts, ARRAY_LEN (_mplus_instance_opts)},
71 {SUBSET_FONT_BASE_PATH "RobotoFlex-Variable.ttf", 900, _roboto_flex_instance_opts, ARRAY_LEN (_roboto_flex_instance_opts)},
Garret Rieger52d59bf2022-05-10 19:40:37 +000072#if 0
73 {"perf/fonts/NotoSansCJKsc-VF.ttf", 100000},
74#endif
75};
76
Seigo Nonakac62d6f42023-03-01 19:52:57 +090077static test_input_t *tests = default_tests;
78static unsigned num_tests = sizeof (default_tests) / sizeof (default_tests[0]);
79
80
Garret Riegerfbd183d2022-04-20 20:05:14 +000081void AddCodepoints(const hb_set_t* codepoints_in_font,
82 unsigned subset_size,
83 hb_subset_input_t* input)
84{
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -060085 auto *unicodes = hb_subset_input_unicode_set (input);
Garret Riegerfbd183d2022-04-20 20:05:14 +000086 hb_codepoint_t cp = HB_SET_VALUE_INVALID;
87 for (unsigned i = 0; i < subset_size; i++) {
88 // TODO(garretrieger): pick randomly.
Garret Riegerb3ce96d2022-04-20 21:51:20 +000089 if (!hb_set_next (codepoints_in_font, &cp)) return;
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -060090 hb_set_add (unicodes, cp);
Garret Riegerfbd183d2022-04-20 20:05:14 +000091 }
92}
93
Garret Rieger62128562022-05-04 22:16:03 +000094void AddGlyphs(unsigned num_glyphs_in_font,
95 unsigned subset_size,
96 hb_subset_input_t* input)
97{
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -060098 auto *glyphs = hb_subset_input_glyph_set (input);
Garret Rieger62128562022-05-04 22:16:03 +000099 for (unsigned i = 0; i < subset_size && i < num_glyphs_in_font; i++) {
100 // TODO(garretrieger): pick randomly.
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -0600101 hb_set_add (glyphs, i);
Garret Rieger62128562022-05-04 22:16:03 +0000102 }
103}
104
Garret Riegerf4903de2022-10-13 21:38:54 +0000105// Preprocess face and populate the subset accelerator on it to speed up
106// the subsetting operations.
107static hb_face_t* preprocess_face(hb_face_t* face)
108{
Garret Rieger515863e2022-10-13 23:42:00 +0000109 hb_face_t* new_face = hb_subset_preprocess(face);
110 hb_face_destroy(face);
111 return new_face;
Garret Riegerf4903de2022-10-13 21:38:54 +0000112}
113
Garret Riegerfbd183d2022-04-20 20:05:14 +0000114/* benchmark for subsetting a font */
Garret Riegerbc5129d2022-05-04 22:16:03 +0000115static void BM_subset (benchmark::State &state,
116 operation_t operation,
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900117 const test_input_t &test_input,
118 bool hinting)
Garret Riegerfbd183d2022-04-20 20:05:14 +0000119{
Garret Riegerb3ce96d2022-04-20 21:51:20 +0000120 unsigned subset_size = state.range(0);
121
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900122 hb_face_t *face = nullptr;
123
124 static hb_face_t *cached_face;
125 static const char *cached_font_path;
126
127 if (!cached_font_path || strcmp (cached_font_path, test_input.font_path))
Garret Riegerfbd183d2022-04-20 20:05:14 +0000128 {
Qunxin Liu47094492022-08-31 12:01:32 -0700129 hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path);
Garret Riegerfbd183d2022-04-20 20:05:14 +0000130 assert (blob);
131 face = hb_face_create (blob, 0);
132 hb_blob_destroy (blob);
Garret Riegerf4903de2022-10-13 21:38:54 +0000133
134 face = preprocess_face (face);
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900135
136 if (cached_face)
137 hb_face_destroy (cached_face);
138
139 cached_face = hb_face_reference (face);
140 cached_font_path = test_input.font_path;
Garret Riegerfbd183d2022-04-20 20:05:14 +0000141 }
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900142 else
143 face = hb_face_reference (cached_face);
Garret Riegerfbd183d2022-04-20 20:05:14 +0000144
145 hb_subset_input_t* input = hb_subset_input_create_or_fail ();
146 assert (input);
147
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900148 if (!hinting)
149 hb_subset_input_set_flags (input, HB_SUBSET_FLAGS_NO_HINTING);
150
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -0600151 switch (operation)
Garret Riegerfbd183d2022-04-20 20:05:14 +0000152 {
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -0600153 case subset_codepoints:
154 {
155 hb_set_t* all_codepoints = hb_set_create ();
156 hb_face_collect_unicodes (face, all_codepoints);
157 AddCodepoints(all_codepoints, subset_size, input);
158 hb_set_destroy (all_codepoints);
Garret Riegerbc5129d2022-05-04 22:16:03 +0000159 }
Behdad Esfahbod7edd54f2022-05-10 18:44:14 -0600160 break;
161
162 case subset_glyphs:
163 {
164 unsigned num_glyphs = hb_face_get_glyph_count (face);
165 AddGlyphs(num_glyphs, subset_size, input);
166 }
167 break;
Behdad Esfahbod238e7dd2022-09-01 13:24:01 -0600168
Qunxin Liu47094492022-08-31 12:01:32 -0700169 case instance:
170 {
171 hb_set_t* all_codepoints = hb_set_create ();
172 hb_face_collect_unicodes (face, all_codepoints);
173 AddCodepoints(all_codepoints, subset_size, input);
174 hb_set_destroy (all_codepoints);
175
176 for (unsigned i = 0; i < test_input.num_instance_opts; i++)
177 hb_subset_input_pin_axis_location (input, face,
178 test_input.instance_opts[i].axis_tag,
179 test_input.instance_opts[i].axis_value);
180 }
181 break;
Garret Riegerfbd183d2022-04-20 20:05:14 +0000182 }
183
184 for (auto _ : state)
185 {
186 hb_face_t* subset = hb_subset_or_fail (face, input);
187 assert (subset);
188 hb_face_destroy (subset);
189 }
190
191 hb_subset_input_destroy (input);
192 hb_face_destroy (face);
193}
194
Garret Rieger52d59bf2022-05-10 19:40:37 +0000195static void test_subset (operation_t op,
196 const char *op_name,
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900197 bool hinting,
Garret Rieger52d59bf2022-05-10 19:40:37 +0000198 benchmark::TimeUnit time_unit,
199 const test_input_t &test_input)
200{
Qunxin Liu47094492022-08-31 12:01:32 -0700201 if (op == instance && test_input.instance_opts == nullptr)
202 return;
203
Garret Rieger52d59bf2022-05-10 19:40:37 +0000204 char name[1024] = "BM_subset/";
205 strcat (name, op_name);
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900206 strcat (name, "/");
207 const char *p = strrchr (test_input.font_path, '/');
208 strcat (name, p ? p + 1 : test_input.font_path);
209 if (!hinting)
210 strcat (name, "/nohinting");
Garret Riegerfbd183d2022-04-20 20:05:14 +0000211
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900212 benchmark::RegisterBenchmark (name, BM_subset, op, test_input, hinting)
Garret Rieger52d59bf2022-05-10 19:40:37 +0000213 ->Range(10, test_input.max_subset_size)
214 ->Unit(time_unit);
215}
Garret Riegerbc5129d2022-05-04 22:16:03 +0000216
Garret Rieger52d59bf2022-05-10 19:40:37 +0000217static void test_operation (operation_t op,
218 const char *op_name,
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900219 const test_input_t *tests,
220 unsigned num_tests,
Garret Rieger52d59bf2022-05-10 19:40:37 +0000221 benchmark::TimeUnit time_unit)
222{
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900223 for (unsigned i = 0; i < num_tests; i++)
Garret Rieger52d59bf2022-05-10 19:40:37 +0000224 {
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900225 auto& test_input = tests[i];
226 test_subset (op, op_name, true, time_unit, test_input);
227 test_subset (op, op_name, false, time_unit, test_input);
Garret Rieger52d59bf2022-05-10 19:40:37 +0000228 }
229}
Garret Riegerfbd183d2022-04-20 20:05:14 +0000230
Garret Rieger52d59bf2022-05-10 19:40:37 +0000231int main(int argc, char** argv)
232{
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900233 benchmark::Initialize(&argc, argv);
234
235 if (argc > 1)
236 {
237 num_tests = (argc - 1) / 2;
238 tests = (test_input_t *) calloc (num_tests, sizeof (test_input_t));
239 for (unsigned i = 0; i < num_tests; i++)
240 {
241 tests[i].font_path = argv[1 + i * 2];
242 tests[i].max_subset_size = atoi (argv[2 + i * 2]);
243 }
244 }
245
246#define TEST_OPERATION(op, time_unit) test_operation (op, #op, tests, num_tests, time_unit)
Garret Riegerbc5129d2022-05-04 22:16:03 +0000247
Garret Rieger52d59bf2022-05-10 19:40:37 +0000248 TEST_OPERATION (subset_glyphs, benchmark::kMillisecond);
249 TEST_OPERATION (subset_codepoints, benchmark::kMillisecond);
Qunxin Liu47094492022-08-31 12:01:32 -0700250 TEST_OPERATION (instance, benchmark::kMillisecond);
Garret Riegerfbd183d2022-04-20 20:05:14 +0000251
Garret Rieger52d59bf2022-05-10 19:40:37 +0000252#undef TEST_OPERATION
Garret Riegerbc5129d2022-05-04 22:16:03 +0000253
Garret Rieger52d59bf2022-05-10 19:40:37 +0000254 benchmark::RunSpecifiedBenchmarks();
255 benchmark::Shutdown();
Seigo Nonakac62d6f42023-03-01 19:52:57 +0900256
257 if (tests != default_tests)
258 free (tests);
Garret Rieger52d59bf2022-05-10 19:40:37 +0000259}