浏览代码

Merge pull request #70519 from bruvzg/font_hash

[Font] Use key structure instead of raw hash for LRU cache to avoid collisions.
Rémi Verschelde 2 年之前
父节点
当前提交
7758151b6a
共有 2 个文件被更改,包括 64 次插入65 次删除
  1. 27 63
      scene/resources/font.cpp
  2. 37 2
      scene/resources/font.h

+ 27 - 63
scene/resources/font.cpp

@@ -270,24 +270,18 @@ void Font::set_cache_capacity(int p_single_line, int p_multi_line) {
 }
 
 Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
-		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	}
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
 
 	Ref<TextLine> buffer;
-	if (cache.has(hash)) {
-		buffer = cache.get(hash);
+	if (cache.has(key)) {
+		buffer = cache.get(key);
 	} else {
 		buffer.instantiate();
 		buffer->set_direction(p_direction);
 		buffer->set_orientation(p_orientation);
 		buffer->add_string(p_text, Ref<Font>(this), p_font_size);
-		cache.insert(hash, buffer);
+		cache.insert(key, buffer);
 	}
 
 	buffer->set_width(p_width);
@@ -300,17 +294,11 @@ Size2 Font::get_string_size(const String &p_text, HorizontalAlignment p_alignmen
 }
 
 Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
 
 	Ref<TextParagraph> lines_buffer;
-	if (cache_wrap.has(hash)) {
-		lines_buffer = cache_wrap.get(hash);
+	if (cache_wrap.has(key)) {
+		lines_buffer = cache_wrap.get(key);
 	} else {
 		lines_buffer.instantiate();
 		lines_buffer->set_direction(p_direction);
@@ -319,7 +307,7 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment
 		lines_buffer->set_width(p_width);
 		lines_buffer->set_break_flags(p_brk_flags);
 		lines_buffer->set_justification_flags(p_jst_flags);
-		cache_wrap.insert(hash, lines_buffer);
+		cache_wrap.insert(key, lines_buffer);
 	}
 
 	lines_buffer->set_alignment(p_alignment);
@@ -329,24 +317,18 @@ Size2 Font::get_multiline_string_size(const String &p_text, HorizontalAlignment
 }
 
 void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
-		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	}
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
 
 	Ref<TextLine> buffer;
-	if (cache.has(hash)) {
-		buffer = cache.get(hash);
+	if (cache.has(key)) {
+		buffer = cache.get(key);
 	} else {
 		buffer.instantiate();
 		buffer->set_direction(p_direction);
 		buffer->set_orientation(p_orientation);
 		buffer->add_string(p_text, Ref<Font>(this), p_font_size);
-		cache.insert(hash, buffer);
+		cache.insert(key, buffer);
 	}
 
 	Vector2 ofs = p_pos;
@@ -366,17 +348,11 @@ void Font::draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_t
 }
 
 void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
 
 	Ref<TextParagraph> lines_buffer;
-	if (cache_wrap.has(hash)) {
-		lines_buffer = cache_wrap.get(hash);
+	if (cache_wrap.has(key)) {
+		lines_buffer = cache_wrap.get(key);
 	} else {
 		lines_buffer.instantiate();
 		lines_buffer->set_direction(p_direction);
@@ -385,7 +361,7 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
 		lines_buffer->set_width(p_width);
 		lines_buffer->set_break_flags(p_brk_flags);
 		lines_buffer->set_justification_flags(p_jst_flags);
-		cache_wrap.insert(hash, lines_buffer);
+		cache_wrap.insert(key, lines_buffer);
 	}
 
 	Vector2 ofs = p_pos;
@@ -402,24 +378,18 @@ void Font::draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const S
 }
 
 void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	if (p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
-		hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-		hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	}
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	bool fill = (p_alignment == HORIZONTAL_ALIGNMENT_FILL);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, fill ? p_width : 0.0, fill ? p_jst_flags : TextServer::JUSTIFICATION_NONE, TextServer::BREAK_NONE, p_direction, p_orientation);
 
 	Ref<TextLine> buffer;
-	if (cache.has(hash)) {
-		buffer = cache.get(hash);
+	if (cache.has(key)) {
+		buffer = cache.get(key);
 	} else {
 		buffer.instantiate();
 		buffer->set_direction(p_direction);
 		buffer->set_orientation(p_orientation);
 		buffer->add_string(p_text, Ref<Font>(this), p_font_size);
-		cache.insert(hash, buffer);
+		cache.insert(key, buffer);
 	}
 
 	Vector2 ofs = p_pos;
@@ -439,17 +409,11 @@ void Font::draw_string_outline(RID p_canvas_item, const Point2 &p_pos, const Str
 }
 
 void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
-	uint64_t hash = p_text.hash64();
-	hash = hash_djb2_one_64(p_font_size, hash);
-	hash = hash_djb2_one_64(hash_murmur3_one_float(p_width), hash);
-	hash = hash_djb2_one_64(p_brk_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_jst_flags.operator int64_t(), hash);
-	hash = hash_djb2_one_64(p_direction, hash);
-	hash = hash_djb2_one_64(p_orientation, hash);
+	ShapedTextKey key = ShapedTextKey(p_text, p_font_size, p_width, p_jst_flags, p_brk_flags, p_direction, p_orientation);
 
 	Ref<TextParagraph> lines_buffer;
-	if (cache_wrap.has(hash)) {
-		lines_buffer = cache_wrap.get(hash);
+	if (cache_wrap.has(key)) {
+		lines_buffer = cache_wrap.get(key);
 	} else {
 		lines_buffer.instantiate();
 		lines_buffer->set_direction(p_direction);
@@ -458,7 +422,7 @@ void Font::draw_multiline_string_outline(RID p_canvas_item, const Point2 &p_pos,
 		lines_buffer->set_width(p_width);
 		lines_buffer->set_break_flags(p_brk_flags);
 		lines_buffer->set_justification_flags(p_jst_flags);
-		cache_wrap.insert(hash, lines_buffer);
+		cache_wrap.insert(key, lines_buffer);
 	}
 
 	Vector2 ofs = p_pos;

+ 37 - 2
scene/resources/font.h

@@ -47,9 +47,44 @@ class TextParagraph;
 class Font : public Resource {
 	GDCLASS(Font, Resource);
 
+	struct ShapedTextKey {
+		String text;
+		int font_size = 14;
+		float width = 0.f;
+		BitField<TextServer::JustificationFlag> jst_flags = TextServer::JUSTIFICATION_NONE;
+		BitField<TextServer::LineBreakFlag> brk_flags = TextServer::BREAK_MANDATORY;
+		TextServer::Direction direction = TextServer::DIRECTION_AUTO;
+		TextServer::Orientation orientation = TextServer::ORIENTATION_HORIZONTAL;
+
+		bool operator==(const ShapedTextKey &p_b) const {
+			return (font_size == p_b.font_size) && (width == p_b.width) && (jst_flags == p_b.jst_flags) && (brk_flags == p_b.brk_flags) && (direction == p_b.direction) && (orientation == p_b.orientation) && (text == p_b.text);
+		}
+
+		ShapedTextKey() {}
+		ShapedTextKey(const String &p_text, int p_font_size, float p_width, BitField<TextServer::JustificationFlag> p_jst_flags, BitField<TextServer::LineBreakFlag> p_brk_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+			text = p_text;
+			font_size = p_font_size;
+			width = p_width;
+			jst_flags = p_jst_flags;
+			brk_flags = p_brk_flags;
+			direction = p_direction;
+			orientation = p_orientation;
+		}
+	};
+
+	struct ShapedTextKeyHasher {
+		_FORCE_INLINE_ static uint32_t hash(const ShapedTextKey &p_a) {
+			uint32_t hash = p_a.text.hash();
+			hash = hash_murmur3_one_32(p_a.font_size, hash);
+			hash = hash_murmur3_one_float(p_a.width, hash);
+			hash = hash_murmur3_one_32(p_a.brk_flags | (p_a.jst_flags << 6) | (p_a.direction << 12) | (p_a.orientation << 15), hash);
+			return hash_fmix32(hash);
+		}
+	};
+
 	// Shaped string cache.
-	mutable LRUCache<uint64_t, Ref<TextLine>> cache;
-	mutable LRUCache<uint64_t, Ref<TextParagraph>> cache_wrap;
+	mutable LRUCache<ShapedTextKey, Ref<TextLine>, ShapedTextKeyHasher> cache;
+	mutable LRUCache<ShapedTextKey, Ref<TextParagraph>, ShapedTextKeyHasher> cache_wrap;
 
 protected:
 	// Output.