Add performance benchmark for shaping, get extents and draw

This commit is contained in:
Ebrahim Byagowi 2020-06-09 17:37:36 +04:30
parent 12a9d57292
commit 95b1081be4
11 changed files with 318 additions and 2 deletions

View File

@ -226,7 +226,9 @@ jobs:
# test a meson based dist
- run: meson dist -Cbuild && rm -rf build
# test experimental APIs
- run: meson build -Dexperimental_api=true && ninja -Cbuild test && rm -rf build
- run: meson build -Dexperimental_api=true -Dbenchmark=true -Dexperimental_api=true -Doptimization=2 && ninja -Cbuild test # or meson test -Cbuild
# run benchmarks
- run: build/perf/perf && meson test -Cbuild --benchmark && rm -rf build # or ninja -Cbuild benchmark
# mingw
- run: .ci/build-win32.sh
- store_artifacts:

View File

@ -17,7 +17,7 @@ jobs:
- name: install meson and fonttools
run: sudo pip3 install meson fonttools
- name: run
run: meson build -Db_coverage=true -Dglib=enabled -Dcairo=enabled -Dicu=enabled -Dgraphite=enabled -Dfreetype=enabled
run: meson build -Db_coverage=true -Dglib=enabled -Dcairo=enabled -Dicu=enabled -Dgraphite=enabled -Dfreetype=enabled -Dbenchmark=true -Doptimization=2
- name: ci
run: meson test --print-errorlogs -Cbuild
@ -30,6 +30,23 @@ jobs:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
REVISION: ${{ github.sha }}
# waiting for https://github.com/rhysd/github-action-benchmark/issues/36 to happen
# - name: benchmark
# run: build/perf/perf --benchmark_format=json > perf/result.json
# - name: store benchmark result
# uses: rhysd/github-action-benchmark@b2ee598
# if: github.event_name != 'pull_request'
# with:
# name: C++ Benchmark
# tool: 'googlecpp'
# output-file-path: perf/result.json
# gh-pages-branch: gh-pages
# github-token: ${{ secrets.PERSONAL_GITHUB_TOKEN }}
# auto-push: true
# alert-threshold: '150%'
# comment-on-alert: true
# fail-on-alert: true
- name: cov
run: ninja -Cbuild coverage
- uses: codecov/codecov-action@v1

View File

@ -345,6 +345,10 @@ if not get_option('tests').disabled()
subdir('test')
endif
if get_option('benchmark')
subdir('perf')
endif
if not get_option('gtk_doc').disabled()
subdir('docs')
endif

View File

@ -28,6 +28,8 @@ option('introspection', type: 'feature', value: 'auto', yield: true,
option('gtk_doc', type: 'feature', value: 'auto', yield: true,
description: 'Generate documentation with gtk-doc')
option('benchmark', type : 'boolean', value : false,
description: 'Enable benchmark tests')
option('icu_builtin', type: 'boolean', value: false,
description: 'Don\'t separate ICU support as harfbuzz-icu module')
option('experimental_api', type: 'boolean', value: false,

9
perf/meson.build Normal file
View File

@ -0,0 +1,9 @@
google_benchmark = subproject('google-benchmark')
google_benchmark_dep = google_benchmark.get_variable('google_benchmark_dep')
benchmark('perf', executable('perf', 'perf.cc',
dependencies : [google_benchmark_dep, freetype_dep],
include_directories: [incconfig, incsrc],
link_with: [libharfbuzz],
install: false,
), workdir: meson.current_source_dir() / '..', timeout: 100)

128
perf/perf-draw.hh Normal file
View File

@ -0,0 +1,128 @@
#include "benchmark/benchmark.h"
#include "hb.h"
#include "hb-ot.h"
#include "hb-ft.h"
#include FT_OUTLINE_H
#define HB_UNUSED __attribute__((unused))
static void
_hb_move_to (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
static void
_hb_line_to (hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED, void *user_data HB_UNUSED) {}
static void
_hb_quadratic_to (hb_position_t control_x HB_UNUSED, hb_position_t control_y HB_UNUSED,
hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
static void
_hb_cubic_to (hb_position_t control1_x HB_UNUSED, hb_position_t control1_y HB_UNUSED,
hb_position_t control2_x HB_UNUSED, hb_position_t control2_y HB_UNUSED,
hb_position_t to_x HB_UNUSED, hb_position_t to_y HB_UNUSED,
void *user_data HB_UNUSED) {}
static void
_hb_close_path (void *user_data HB_UNUSED) {}
static void
_ft_move_to (const FT_Vector* to HB_UNUSED, void* user HB_UNUSED) {}
static void
_ft_line_to (const FT_Vector* to HB_UNUSED, void* user HB_UNUSED) {}
static void
_ft_conic_to (const FT_Vector* control HB_UNUSED, const FT_Vector* to HB_UNUSED,
void* user HB_UNUSED) {}
static void
_ft_cubic_to (const FT_Vector* control1 HB_UNUSED, const FT_Vector* control2 HB_UNUSED,
const FT_Vector* to HB_UNUSED, void* user HB_UNUSED) {}
static void draw (benchmark::State &state, const char *font_path, bool is_var, bool is_ft)
{
hb_font_t *font;
unsigned num_glyphs;
{
hb_blob_t *blob = hb_blob_create_from_file (font_path);
assert (hb_blob_get_length (blob));
hb_face_t *face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
num_glyphs = hb_face_get_glyph_count (face);
font = hb_font_create (face);
hb_face_destroy (face);
}
if (is_var)
{
hb_variation_t wght = {HB_TAG ('w','g','h','t'), 500};
hb_font_set_variations (font, &wght, 1);
}
if (is_ft)
{
FT_Face ft_face = hb_ft_font_get_face (font);
hb_ft_font_set_load_flags (font, FT_LOAD_NO_HINTING | FT_LOAD_NO_SCALE);
FT_Outline_Funcs draw_funcs;
draw_funcs.move_to = (FT_Outline_MoveToFunc) _ft_move_to;
draw_funcs.line_to = (FT_Outline_LineToFunc) _ft_line_to;
draw_funcs.conic_to = (FT_Outline_ConicToFunc) _ft_conic_to;
draw_funcs.cubic_to = (FT_Outline_CubicToFunc) _ft_cubic_to;
draw_funcs.shift = 0;
draw_funcs.delta = 0;
for (auto _ : state)
for (unsigned gid = 0; gid < num_glyphs; ++gid)
{
FT_Load_Glyph (ft_face, gid, FT_LOAD_NO_HINTING | FT_LOAD_NO_SCALE);
FT_Outline_Decompose (&ft_face->glyph->outline, &draw_funcs, nullptr);
}
}
else
{
hb_draw_funcs_t *draw_funcs = hb_draw_funcs_create ();
hb_draw_funcs_set_move_to_func (draw_funcs, _hb_move_to);
hb_draw_funcs_set_line_to_func (draw_funcs, _hb_line_to);
hb_draw_funcs_set_quadratic_to_func (draw_funcs, _hb_quadratic_to);
hb_draw_funcs_set_cubic_to_func (draw_funcs, _hb_cubic_to);
hb_draw_funcs_set_close_path_func (draw_funcs, _hb_close_path);
for (auto _ : state)
for (unsigned gid = 0; gid < num_glyphs; ++gid)
hb_font_draw_glyph (font, gid, draw_funcs, nullptr);
hb_draw_funcs_destroy (draw_funcs);
}
hb_font_destroy (font);
}
#define FONT_BASE_PATH "test/subset/data/fonts/"
BENCHMARK_CAPTURE (draw, cff - ot - SourceSansPro, FONT_BASE_PATH "SourceSansPro-Regular.otf", false, false);
BENCHMARK_CAPTURE (draw, cff - ft - SourceSansPro, FONT_BASE_PATH "SourceSansPro-Regular.otf", false, true);
BENCHMARK_CAPTURE (draw, cff2 - ot - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", false, false);
BENCHMARK_CAPTURE (draw, cff2 - ft - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", false, true);
BENCHMARK_CAPTURE (draw, cff2/vf - ot - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", true, false);
BENCHMARK_CAPTURE (draw, cff2/vf - ft - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", true, true);
BENCHMARK_CAPTURE (draw, glyf - ot - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", false, false);
BENCHMARK_CAPTURE (draw, glyf - ft - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", false, true);
BENCHMARK_CAPTURE (draw, glyf/vf - ot - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", true, false);
BENCHMARK_CAPTURE (draw, glyf/vf - ft - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", true, true);
BENCHMARK_CAPTURE (draw, glyf - ot - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", false, false);
BENCHMARK_CAPTURE (draw, glyf - ft - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", false, true);
BENCHMARK_CAPTURE (draw, glyf/vf - ot - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", true, false);
BENCHMARK_CAPTURE (draw, glyf/vf - ft - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", true, true);
BENCHMARK_CAPTURE (draw, glyf - ot - Roboto, FONT_BASE_PATH "Roboto-Regular.ttf", false, false);
BENCHMARK_CAPTURE (draw, glyf - ft - Roboto, FONT_BASE_PATH "Roboto-Regular.ttf", false, true);

65
perf/perf-extents.hh Normal file
View File

@ -0,0 +1,65 @@
#include "benchmark/benchmark.h"
#include "hb.h"
#include "hb-ft.h"
#include "hb-ot.h"
static void extents (benchmark::State &state, const char *font_path, bool is_var, bool is_ft)
{
hb_font_t *font;
unsigned num_glyphs;
{
hb_blob_t *blob = hb_blob_create_from_file (font_path);
assert (hb_blob_get_length (blob));
hb_face_t *face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
num_glyphs = hb_face_get_glyph_count (face);
font = hb_font_create (face);
hb_face_destroy (face);
}
if (is_var)
{
hb_variation_t wght = {HB_TAG ('w','g','h','t'), 500};
hb_font_set_variations (font, &wght, 1);
}
if (is_ft)
{
hb_ft_font_set_funcs (font);
hb_ft_font_set_load_flags (font, FT_LOAD_NO_HINTING | FT_LOAD_NO_SCALE);
}
hb_glyph_extents_t extents;
for (auto _ : state)
for (unsigned gid = 0; gid < num_glyphs; ++gid)
hb_font_get_glyph_extents (font, gid, &extents);
hb_font_destroy (font);
}
#define FONT_BASE_PATH "test/subset/data/fonts/"
BENCHMARK_CAPTURE (extents, cff - ot - SourceSansPro, FONT_BASE_PATH "SourceSansPro-Regular.otf", false, false);
BENCHMARK_CAPTURE (extents, cff - ft - SourceSansPro, FONT_BASE_PATH "SourceSansPro-Regular.otf", false, true);
BENCHMARK_CAPTURE (extents, cff2 - ot - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", false, false);
BENCHMARK_CAPTURE (extents, cff2 - ft - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", false, true);
BENCHMARK_CAPTURE (extents, cff2/vf - ot - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", true, false);
BENCHMARK_CAPTURE (extents, cff2/vf - ft - AdobeVFPrototype, FONT_BASE_PATH "AdobeVFPrototype.otf", true, true);
BENCHMARK_CAPTURE (extents, glyf - ot - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", false, false);
BENCHMARK_CAPTURE (extents, glyf - ft - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", false, true);
BENCHMARK_CAPTURE (extents, glyf/vf - ot - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", true, false);
BENCHMARK_CAPTURE (extents, glyf/vf - ft - SourceSerifVariable, FONT_BASE_PATH "SourceSerifVariable-Roman.ttf", true, true);
BENCHMARK_CAPTURE (extents, glyf - ot - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", false, false);
BENCHMARK_CAPTURE (extents, glyf - ft - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", false, true);
BENCHMARK_CAPTURE (extents, glyf/vf - ot - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", true, false);
BENCHMARK_CAPTURE (extents, glyf/vf - ft - Comfortaa, FONT_BASE_PATH "Comfortaa-Regular-new.ttf", true, true);
BENCHMARK_CAPTURE (extents, glyf - ot - Roboto, FONT_BASE_PATH "Roboto-Regular.ttf", false, false);
BENCHMARK_CAPTURE (extents, glyf - ft - Roboto, FONT_BASE_PATH "Roboto-Regular.ttf", false, true);

65
perf/perf-shaping.hh Normal file
View File

@ -0,0 +1,65 @@
#include "benchmark/benchmark.h"
#include "hb.h"
static void shape (benchmark::State &state, const char *text_path,
hb_direction_t direction, hb_script_t script,
const char *font_path)
{
hb_font_t *font;
{
hb_blob_t *blob = hb_blob_create_from_file (font_path);
assert (hb_blob_get_length (blob));
hb_face_t *face = hb_face_create (blob, 0);
hb_blob_destroy (blob);
font = hb_font_create (face);
hb_face_destroy (face);
}
hb_blob_t *text_blob = hb_blob_create_from_file (text_path);
unsigned text_length;
const char *text = hb_blob_get_data (text_blob, &text_length);
assert (text_length);
hb_buffer_t *buf = hb_buffer_create ();
for (auto _ : state)
{
hb_buffer_add_utf8 (buf, text, text_length, 0, -1);
hb_buffer_set_direction (buf, direction);
hb_buffer_set_script (buf, script);
hb_shape (font, buf, nullptr, 0);
hb_buffer_clear_contents (buf);
}
hb_buffer_destroy (buf);
hb_blob_destroy (text_blob);
hb_font_destroy (font);
}
BENCHMARK_CAPTURE (shape, fa-thelittleprince.txt - Amiri,
"perf/texts/fa-thelittleprince.txt",
HB_DIRECTION_RTL, HB_SCRIPT_ARABIC,
"perf/fonts/Amiri-Regular.ttf");
BENCHMARK_CAPTURE (shape, fa-thelittleprince.txt - NotoNastaliqUrdu,
"perf/texts/fa-thelittleprince.txt",
HB_DIRECTION_RTL, HB_SCRIPT_ARABIC,
"perf/fonts/NotoNastaliqUrdu-Regular.ttf");
BENCHMARK_CAPTURE (shape, fa-monologue.txt - Amiri,
"perf/texts/fa-monologue.txt",
HB_DIRECTION_RTL, HB_SCRIPT_ARABIC,
"perf/fonts/Amiri-Regular.ttf");
BENCHMARK_CAPTURE (shape, fa-monologue.txt - NotoNastaliqUrdu,
"perf/texts/fa-monologue.txt",
HB_DIRECTION_RTL, HB_SCRIPT_ARABIC,
"perf/fonts/NotoNastaliqUrdu-Regular.ttf");
BENCHMARK_CAPTURE (shape, en-thelittleprince.txt - Roboto,
"perf/texts/en-thelittleprince.txt",
HB_DIRECTION_LTR, HB_SCRIPT_LATIN,
"perf/fonts/Roboto-Regular.ttf");
BENCHMARK_CAPTURE (shape, en-words.txt - Roboto,
"perf/texts/en-words.txt",
HB_DIRECTION_LTR, HB_SCRIPT_LATIN,
"perf/fonts/Roboto-Regular.ttf");

15
perf/perf.cc Normal file
View File

@ -0,0 +1,15 @@
#include "benchmark/benchmark.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "perf-shaping.hh"
#ifdef HAVE_FREETYPE
#include "perf-extents.hh"
#ifdef HB_EXPERIMENTAL_API
#include "perf-draw.hh"
#endif
#endif
BENCHMARK_MAIN ();

View File

@ -6,3 +6,4 @@
/proxy-libintl
/zlib
/packagecache
/google-benchmark

View File

@ -0,0 +1,8 @@
[wrap-file]
directory = benchmark-1.4.1
source_url = https://github.com/google/benchmark/archive/v1.4.1.zip
source_filename = benchmark-1.4.1.zip
source_hash = 61ae07eb5d4a0b02753419eb17a82b7d322786bb36ab62bd3df331a4d47c00a7
patch_url = https://wrapdb.mesonbuild.com/v1/projects/google-benchmark/1.4.1/1/get_zip
patch_filename = google-benchmark-1.4.1-1-wrap.zip
patch_hash = 4cc5fe02ebd4fc82e110919b7977d7463eb2a99e4ecb9feca920eab6fd911d67