Browse Source

Merge pull request #102129 from Ivorforce/optimize-text-server-adv-break-iter

Optimize text rendering by caching `UBreakIterator` instances.
Thaddeus Crews 5 months ago
parent
commit
90509e6402

+ 15 - 3
modules/text_server_adv/text_server_adv.cpp

@@ -5664,8 +5664,12 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
 				i++;
 				i++;
 			}
 			}
 			int r_end = sd->spans[i].end;
 			int r_end = sd->spans[i].end;
-			UBreakIterator *bi = ubrk_open(UBRK_LINE, (language.is_empty()) ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
-			if (U_FAILURE(err)) {
+			UBreakIterator *bi = sd->_get_break_iterator_for_locale(language, &err);
+			if (!U_FAILURE(err) && bi) {
+				ubrk_setText(bi, data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+			}
+
+			if (U_FAILURE(err) || !bi) {
 				// No data loaded - use fallback.
 				// No data loaded - use fallback.
 				for (int j = r_start; j < r_end; j++) {
 				for (int j = r_start; j < r_end; j++) {
 					char32_t c = sd->text[j - sd->start];
 					char32_t c = sd->text[j - sd->start];
@@ -5694,7 +5698,6 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
 					}
 					}
 				}
 				}
 			}
 			}
-			ubrk_close(bi);
 			i++;
 			i++;
 		}
 		}
 		sd->break_ops_valid = true;
 		sd->break_ops_valid = true;
@@ -6142,6 +6145,15 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
 	}
 	}
 }
 }
 
 
+UBreakIterator *TextServerAdvanced::ShapedTextDataAdvanced::_get_break_iterator_for_locale(const String &p_language, UErrorCode *r_err) {
+	HashMap<String, UBreakIterator *>::Iterator key_value = line_break_iterators_per_language.find(p_language);
+	if (key_value) {
+		return key_value->value;
+	}
+	UBreakIterator *brk = ubrk_open(UBRK_LINE, p_language.is_empty() ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : p_language.ascii().get_data(), nullptr, 0, r_err);
+	return line_break_iterators_per_language.insert(p_language, brk)->value;
+}
+
 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) {
 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) {
 	RID f;
 	RID f;
 	int fs = p_sd->spans[p_span].font_size;
 	int fs = p_sd->spans[p_span].font_size;

+ 8 - 0
modules/text_server_adv/text_server_adv.h

@@ -545,6 +545,11 @@ class TextServerAdvanced : public TextServerExtension {
 		bool js_ops_valid = false;
 		bool js_ops_valid = false;
 		bool chars_valid = false;
 		bool chars_valid = false;
 
 
+		HashMap<String, UBreakIterator *> line_break_iterators_per_language;
+
+		// Creating UBreakIterator is surprisingly costly. To improve efficiency, we cache them.
+		UBreakIterator *_get_break_iterator_for_locale(const String &p_language, UErrorCode *r_err);
+
 		~ShapedTextDataAdvanced() {
 		~ShapedTextDataAdvanced() {
 			for (int i = 0; i < bidi_iter.size(); i++) {
 			for (int i = 0; i < bidi_iter.size(); i++) {
 				if (bidi_iter[i]) {
 				if (bidi_iter[i]) {
@@ -557,6 +562,9 @@ class TextServerAdvanced : public TextServerExtension {
 			if (hb_buffer) {
 			if (hb_buffer) {
 				hb_buffer_destroy(hb_buffer);
 				hb_buffer_destroy(hb_buffer);
 			}
 			}
+			for (const KeyValue<String, UBreakIterator *> &bi : line_break_iterators_per_language) {
+				ubrk_close(bi.value);
+			}
 		}
 		}
 	};
 	};