Skip to content

Commit 6fbb89d

Browse files
committed
Optimize CPU text shaping
1 parent 5950fca commit 6fbb89d

File tree

3 files changed

+133
-40
lines changed

3 files changed

+133
-40
lines changed

modules/text_server_adv/script_iterator.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
6161
}
6262

6363
int paren_size = PAREN_STACK_DEPTH;
64-
ParenStackEntry *paren_stack = static_cast<ParenStackEntry *>(memalloc(paren_size * sizeof(ParenStackEntry)));
64+
ParenStackEntry starter_stack[PAREN_STACK_DEPTH];
65+
ParenStackEntry *paren_stack = starter_stack;
6566

6667
int script_start;
6768
int script_end = p_start;
@@ -78,7 +79,9 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
7879
UChar32 n = (script_end + 1 < p_length) ? str[script_end + 1] : 0;
7980
UScriptCode sc = uscript_getScript(ch, &err);
8081
if (U_FAILURE(err)) {
81-
memfree(paren_stack);
82+
if (paren_stack != starter_stack) {
83+
memfree(paren_stack);
84+
}
8285
ERR_FAIL_MSG(u_errorName(err));
8386
}
8487
if (is_emoji(ch, n)) {
@@ -92,7 +95,11 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
9295
if (unlikely(paren_sp >= paren_size)) {
9396
// If the stack is full, allocate more space to handle deeply nested parentheses. This is unlikely to happen with any real text.
9497
paren_size += PAREN_STACK_DEPTH;
95-
paren_stack = static_cast<ParenStackEntry *>(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)));
98+
if (paren_stack == starter_stack) {
99+
paren_stack = static_cast<ParenStackEntry *>(memalloc(paren_size * sizeof(ParenStackEntry)));
100+
} else {
101+
paren_stack = static_cast<ParenStackEntry *>(memrealloc(paren_stack, paren_size * sizeof(ParenStackEntry)));
102+
}
96103
}
97104
paren_stack[paren_sp].pair_index = ch;
98105
paren_stack[paren_sp].script_code = script_code;
@@ -144,5 +151,7 @@ ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length
144151
script_ranges.push_back(rng);
145152
} while (script_end < p_length);
146153

147-
memfree(paren_stack);
154+
if (paren_stack != starter_stack) {
155+
memfree(paren_stack);
156+
}
148157
}

modules/text_server_adv/text_server_adv.cpp

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4275,7 +4275,13 @@ bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const
42754275
Vector2i size = _get_size(fd, 16);
42764276
FontForSizeAdvanced *ffsd = nullptr;
42774277
ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), false);
4278-
return fd->supported_scripts.has(hb_tag_from_string(p_script.ascii().get_data(), -1));
4278+
char ascii_script[] = { ' ', ' ', ' ', ' ' };
4279+
for (int i = 0; i < MIN(4, p_script.size()); i++) {
4280+
if (p_script[i] <= 0x7f) {
4281+
ascii_script[i] = p_script[i];
4282+
}
4283+
}
4284+
return fd->supported_scripts.has(hb_tag_from_string(ascii_script, -1));
42794285
}
42804286
}
42814287

@@ -5294,6 +5300,14 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
52945300
int32_t bidi_run_start = _convert_pos(p_sd, ov_start + start + _bidi_run_start);
52955301
int32_t bidi_run_end = _convert_pos(p_sd, ov_start + start + _bidi_run_start + _bidi_run_length);
52965302

5303+
bool cache_valid = false;
5304+
int cached_font_size = -1;
5305+
RID cached_font_rid = RID();
5306+
double cached_font_ascent = 0;
5307+
double cached_font_descent = 0;
5308+
double cached_font_top_spacing = 0;
5309+
double cached_font_bottom_spacing = 0;
5310+
p_new_sd->glyphs.reserve(p_new_sd->glyphs.size() + MIN(sd_size, bidi_run_end - bidi_run_start));
52975311
for (int j = 0; j < sd_size; j++) {
52985312
int col_key_off = (sd_glyphs[j].start == sd_glyphs[j].end) ? 1 : 0;
52995313
if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end - col_key_off)) {
@@ -5346,20 +5360,32 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
53465360
} else {
53475361
if (gl.font_rid.is_valid()) {
53485362
if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
5349-
p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_TOP), -gl.y_off));
5350-
p_new_sd->descent = MAX(p_new_sd->descent, MAX(_font_get_descent(gl.font_rid, gl.font_size) + _font_get_spacing(gl.font_rid, SPACING_BOTTOM), gl.y_off));
5363+
if (!cache_valid || cached_font_rid != gl.font_rid || cached_font_size != gl.font_size) {
5364+
cache_valid = true;
5365+
cached_font_rid = gl.font_rid;
5366+
cached_font_size = gl.font_size;
5367+
cached_font_ascent = _font_get_ascent(gl.font_rid, gl.font_size);
5368+
cached_font_descent = _font_get_descent(gl.font_rid, gl.font_size);
5369+
cached_font_top_spacing = _font_get_spacing(gl.font_rid, SPACING_TOP);
5370+
cached_font_bottom_spacing = _font_get_spacing(gl.font_rid, SPACING_BOTTOM);
5371+
}
5372+
p_new_sd->ascent = MAX(p_new_sd->ascent, MAX(cached_font_ascent + cached_font_top_spacing, -gl.y_off));
5373+
p_new_sd->descent = MAX(p_new_sd->descent, MAX(cached_font_descent + cached_font_bottom_spacing, gl.y_off));
53515374
} else {
5352-
p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
5353-
p_new_sd->descent = MAX(p_new_sd->descent, Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
5375+
double glyph_advance = Math::round(_font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
5376+
p_new_sd->ascent = MAX(p_new_sd->ascent, glyph_advance);
5377+
p_new_sd->descent = MAX(p_new_sd->descent, glyph_advance);
53545378
}
53555379
} else if (p_new_sd->preserve_invalid || (p_new_sd->preserve_control && is_control(ch[gl.start - p_sd->start]))) {
53565380
// Glyph not found, replace with hex code box.
53575381
if (p_new_sd->orientation == ORIENTATION_HORIZONTAL) {
5358-
p_new_sd->ascent = MAX(p_new_sd->ascent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.85);
5359-
p_new_sd->descent = MAX(p_new_sd->descent, get_hex_code_box_size(gl.font_size, gl.index).y * 0.15);
5382+
double box_size = get_hex_code_box_size(gl.font_size, gl.index).y;
5383+
p_new_sd->ascent = MAX(p_new_sd->ascent, box_size * 0.85);
5384+
p_new_sd->descent = MAX(p_new_sd->descent, box_size * 0.15);
53605385
} else {
5361-
p_new_sd->ascent = MAX(p_new_sd->ascent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));
5362-
p_new_sd->descent = MAX(p_new_sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5));
5386+
double box_size = Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5);
5387+
p_new_sd->ascent = MAX(p_new_sd->ascent, box_size);
5388+
p_new_sd->descent = MAX(p_new_sd->descent, box_size);
53635389
}
53645390
}
53655391
p_new_sd->width += gl.advance * gl.repeat;
@@ -6649,10 +6675,9 @@ UBreakIterator *TextServerAdvanced::_create_line_break_iterator_for_locale(const
66496675
return ubrk_clone(bi, r_err);
66506676
}
66516677

6652-
void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) {
6678+
void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) {
66536679
RID f;
66546680
int fs = p_sd->spans[p_span].font_size;
6655-
66566681
if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) {
66576682
// Try font from list.
66586683
f = p_fonts[p_fb_index];
@@ -6697,7 +6722,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
66976722
}
66986723

66996724
bool found = false;
6700-
for (int j = 0; j <= p_fonts.size(); j++) {
6725+
for (uint32_t j = 0; j <= p_fonts.size(); j++) {
67016726
RID f_rid;
67026727
if (j == p_fonts.size()) {
67036728
f_rid = p_prev_font;
@@ -6848,6 +6873,11 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
68486873
}
68496874
}
68506875

6876+
bool cache_valid = false;
6877+
RID cached_font_rid = RID();
6878+
int cached_font_size = 0;
6879+
float cached_offset = 0;
6880+
68516881
double adv_rem = 0.0;
68526882
for (unsigned int i = 0; i < glyph_count; i++) {
68536883
if ((i > 0) && (last_cluster_id != glyph_info[i].cluster)) {
@@ -6933,10 +6963,16 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
69336963
adv_rem = full_adv + gl.advance;
69346964
}
69356965
}
6966+
if (!cache_valid || cached_font_rid != gl.font_rid || cached_font_size != gl.font_size) {
6967+
cache_valid = true;
6968+
cached_font_size = gl.font_size;
6969+
cached_font_rid = gl.font_rid;
6970+
cached_offset = _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size));
6971+
}
69366972
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
6937-
gl.y_off += _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size));
6973+
gl.y_off += cached_offset;
69386974
} else {
6939-
gl.x_off += _font_get_baseline_offset(gl.font_rid) * (double)(_font_get_ascent(gl.font_rid, gl.font_size) + _font_get_descent(gl.font_rid, gl.font_size));
6975+
gl.x_off += cached_offset;
69406976
}
69416977
}
69426978
if ((!last_run || i < last_non_zero_w) && !Math::is_zero_approx(gl.advance)) {
@@ -6978,6 +7014,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star
69787014
failed_subrun_start = p_end + 1;
69797015
failed_subrun_end = p_start;
69807016
}
7017+
p_sd->glyphs.reserve(p_sd->glyphs.size() + w[i].count);
69817018
for (int j = 0; j < w[i].count; j++) {
69827019
if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
69837020
p_sd->ascent = MAX(p_sd->ascent, -w[i + j].y_off);
@@ -7222,27 +7259,8 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
72227259
}
72237260
sd->glyphs.push_back(gl);
72247261
} else {
7225-
Array fonts;
7226-
Array fonts_scr_only;
7227-
Array fonts_no_match;
7228-
int font_count = span.fonts.size();
7229-
if (font_count > 0) {
7230-
fonts.push_back(sd->spans[k].fonts[0]);
7231-
}
7232-
for (int l = 1; l < font_count; l++) {
7233-
if (_font_is_script_supported(span.fonts[l], script_code)) {
7234-
if (_font_is_language_supported(span.fonts[l], span.language)) {
7235-
fonts.push_back(sd->spans[k].fonts[l]);
7236-
} else {
7237-
fonts_scr_only.push_back(sd->spans[k].fonts[l]);
7238-
}
7239-
} else {
7240-
fonts_no_match.push_back(sd->spans[k].fonts[l]);
7241-
}
7242-
}
7243-
fonts.append_array(fonts_scr_only);
7244-
fonts.append_array(fonts_no_match);
7245-
_shape_run(sd, MAX(sd->spans[k].start - sd->start, script_run_start), MIN(sd->spans[k].end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID());
7262+
FontPriorityList fonts(this, span.fonts, span.language, script_code);
7263+
_shape_run(sd, MAX(span.start - sd->start, script_run_start), MIN(span.end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID());
72467264
}
72477265
}
72487266
}

modules/text_server_adv/text_server_adv.h

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,73 @@ class TextServerAdvanced : public TextServerExtension {
705705
int64_t _convert_pos(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
706706
int64_t _convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int64_t p_pos) const;
707707
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_length) const;
708-
void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font);
708+
709+
struct FontPriorityList {
710+
friend class TextServerAdvanced;
711+
712+
const int MAX_PRIORITY = 2;
713+
int current_priority = 0;
714+
uint32_t current_index = 0;
715+
uint32_t font_count = 0;
716+
const String *language;
717+
const String *script_code;
718+
LocalVector<Pair<RID, int>> unprocessed_fonts;
719+
LocalVector<RID> fonts;
720+
const TextServerAdvanced *text_server;
721+
722+
FontPriorityList(const TextServerAdvanced *p_text_server, const Array &p_fonts, const String &p_language, const String &p_script_code) {
723+
text_server = p_text_server;
724+
language = &p_language;
725+
script_code = &p_script_code;
726+
font_count = p_fonts.size();
727+
728+
unprocessed_fonts.reserve(font_count);
729+
for (uint32_t i = 0; i < font_count; i++) {
730+
unprocessed_fonts.push_back(Pair<RID, int>(p_fonts[i], -1));
731+
}
732+
733+
fonts.reserve(font_count);
734+
if (font_count > 0) {
735+
fonts.push_back(p_fonts[0]);
736+
unprocessed_fonts[0].second = 0;
737+
current_index++;
738+
}
739+
}
740+
741+
_FORCE_INLINE_ uint32_t size() const {
742+
return font_count;
743+
}
744+
745+
_FORCE_INLINE_ int _get_priority(const RID &font) {
746+
return text_server->_font_is_script_supported(font, *script_code) ? (text_server->_font_is_language_supported(font, *language) ? 0 : 1) : 2;
747+
}
748+
749+
RID operator[](uint32_t index) {
750+
if (index < fonts.size()) {
751+
return fonts[index];
752+
}
753+
while (current_priority < MAX_PRIORITY || current_index < font_count) {
754+
if (current_index >= font_count) {
755+
current_priority++;
756+
current_index = 0;
757+
}
758+
const RID &font = unprocessed_fonts[current_index].first;
759+
int &priority = unprocessed_fonts[current_index].second;
760+
if (priority < 0) {
761+
priority = _get_priority(font);
762+
}
763+
if (priority == current_priority) {
764+
fonts.push_back(font);
765+
if (index < fonts.size()) {
766+
return fonts[index];
767+
}
768+
}
769+
current_index++;
770+
}
771+
return RID();
772+
}
773+
};
774+
void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font);
709775
Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size);
710776
_FORCE_INLINE_ RID _find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text);
711777

0 commit comments

Comments
 (0)