diff --git a/test/threads/Makefile.am b/test/threads/Makefile.am index c6879d776..f0347d492 100644 --- a/test/threads/Makefile.am +++ b/test/threads/Makefile.am @@ -7,6 +7,7 @@ SUBDIRS = EXTRA_DIST += \ meson.build \ hb-shape-threads.cc \ + hb-subset-threads.cc \ $(NULL) # Convenience targets: diff --git a/test/threads/hb-subset-threads.cc b/test/threads/hb-subset-threads.cc new file mode 100644 index 000000000..80bcd2b5c --- /dev/null +++ b/test/threads/hb-subset-threads.cc @@ -0,0 +1,180 @@ +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "hb-subset.h" + +enum operation_t +{ + subset_codepoints, + subset_glyphs +}; + +#define SUBSET_FONT_BASE_PATH "test/subset/data/fonts/" + +struct test_input_t +{ + const char *font_path; + const unsigned max_subset_size; +} default_tests[] = +{ + {SUBSET_FONT_BASE_PATH "Roboto-Regular.ttf", 4000}, + {SUBSET_FONT_BASE_PATH "Amiri-Regular.ttf", 4000}, + {SUBSET_FONT_BASE_PATH "NotoNastaliqUrdu-Regular.ttf", 1000}, + {SUBSET_FONT_BASE_PATH "NotoSansDevanagari-Regular.ttf", 1000}, + {SUBSET_FONT_BASE_PATH "Mplus1p-Regular.ttf", 10000}, + {SUBSET_FONT_BASE_PATH "SourceHanSans-Regular_subset.otf", 10000}, + {SUBSET_FONT_BASE_PATH "SourceSansPro-Regular.otf", 2000}, +}; + + +static test_input_t *tests = default_tests; +static unsigned num_tests = sizeof (default_tests) / sizeof (default_tests[0]); + + +// https://en.cppreference.com/w/cpp/thread/condition_variable/wait +static std::condition_variable cv; +static std::mutex cv_m; +static bool ready = false; + +static unsigned num_repetitions = 1; +static unsigned num_threads = 3; + +static void AddCodepoints(const hb_set_t* codepoints_in_font, + unsigned subset_size, + hb_subset_input_t* input) +{ + auto *unicodes = hb_subset_input_unicode_set (input); + hb_codepoint_t cp = HB_SET_VALUE_INVALID; + for (unsigned i = 0; i < subset_size; i++) { + if (!hb_set_next (codepoints_in_font, &cp)) return; + hb_set_add (unicodes, cp); + } +} + +static void AddGlyphs(unsigned num_glyphs_in_font, + unsigned subset_size, + hb_subset_input_t* input) +{ + auto *glyphs = hb_subset_input_glyph_set (input); + for (unsigned i = 0; i < subset_size && i < num_glyphs_in_font; i++) { + hb_set_add (glyphs, i); + } +} + +static void subset (operation_t operation, + const test_input_t &test_input, + hb_face_t *face) +{ + // Wait till all threads are ready. + { + std::unique_lock lk (cv_m); + cv.wait(lk, [] {return ready;}); + } + + unsigned subset_size = test_input.max_subset_size; + + hb_subset_input_t* input = hb_subset_input_create_or_fail (); + assert (input); + + switch (operation) + { + case subset_codepoints: + { + hb_set_t* all_codepoints = hb_set_create (); + hb_face_collect_unicodes (face, all_codepoints); + AddCodepoints(all_codepoints, subset_size, input); + hb_set_destroy (all_codepoints); + } + break; + + case subset_glyphs: + { + unsigned num_glyphs = hb_face_get_glyph_count (face); + AddGlyphs(num_glyphs, subset_size, input); + } + break; + } + + for (unsigned i = 0; i < num_repetitions; i++) + { + hb_face_t* subset = hb_subset_or_fail (face, input); + assert (subset); + hb_face_destroy (subset); + } + + hb_subset_input_destroy (input); +} + +static void test_operation (operation_t operation, + const char *operation_name, + const test_input_t &test_input) +{ + char name[1024] = "subset"; + const char *p; + strcat (name, "/"); + p = strrchr (test_input.font_path, '/'); + strcat (name, p ? p + 1 : test_input.font_path); + strcat (name, "/"); + strcat (name, operation_name); + + printf ("Testing %s\n", name); + + hb_face_t *face; + { + hb_blob_t *blob = hb_blob_create_from_file_or_fail (test_input.font_path); + assert (blob); + face = hb_face_create (blob, 0); + hb_blob_destroy (blob); + } + + std::vector threads; + for (unsigned i = 0; i < num_threads; i++) + threads.push_back (std::thread (subset, operation, test_input, face)); + + { + std::unique_lock lk (cv_m); + ready = true; + } + cv.notify_all(); + + for (unsigned i = 0; i < num_threads; i++) + threads[i].join (); + + hb_face_destroy (face); +} + +int main(int argc, char** argv) +{ + if (argc > 1) + num_threads = atoi (argv[1]); + if (argc > 2) + num_repetitions = atoi (argv[2]); + + if (argc > 4) + { + num_tests = (argc - 3) / 2; + tests = (test_input_t *) calloc (num_tests, sizeof (test_input_t)); + for (unsigned i = 0; i < num_tests; i++) + { + tests[i].font_path = argv[3 + i]; + } + } + + printf ("Num threads %u; num repetitions %u\n", num_threads, num_repetitions); + for (unsigned i = 0; i < num_tests; i++) + { + auto& test_input = tests[i]; + test_operation (subset_codepoints, "codepoints", test_input); + test_operation (subset_glyphs, "glyphs", test_input); + } + + if (tests != default_tests) + free (tests); +} diff --git a/test/threads/meson.build b/test/threads/meson.build index 4402f4897..1848a1c6e 100644 --- a/test/threads/meson.build +++ b/test/threads/meson.build @@ -11,3 +11,18 @@ test('shape_threads', executable('hb-shape-threads', 'hb-shape-threads.cc', timeout: 300, suite: ['threads', 'slow'], ) + + +test('subset_threads', executable('hb-subset-threads', 'hb-subset-threads.cc', + dependencies: [ + thread_dep + ], + cpp_args: [], + include_directories: [incconfig, incsrc], + link_with: [libharfbuzz, libharfbuzz_subset], + install: false, + ), + workdir: meson.current_source_dir() / '..' / '..', + timeout: 300, + suite: ['threads', 'slow'], +)