Browse Source

Reuse text geometry if the mesh didn't change, see #727

In some cases the text geometry can be regenerated and result in the same mesh as previously, e.g. if the layout is changed without visually affecting a given text element. By reusing the previous geometry, we avoid compiling the geometry again which can be expensive on some backends.
Michael Ragazzon 6 months ago
parent
commit
0c96c0eed2

+ 2 - 0
Include/RmlUi/Core/Geometry.h

@@ -54,6 +54,8 @@ public:
 
 
 	Mesh Release(ReleaseMode mode = ReleaseMode::ReturnMesh);
 	Mesh Release(ReleaseMode mode = ReleaseMode::ReturnMesh);
 
 
+	const Mesh& GetMesh() const;
+
 private:
 private:
 	Geometry(RenderManager* render_manager, StableVectorIndex resource_handle);
 	Geometry(RenderManager* render_manager, StableVectorIndex resource_handle);
 	friend class RenderManager;
 	friend class RenderManager;

+ 4 - 2
Include/RmlUi/Core/Mesh.h

@@ -35,14 +35,16 @@
 
 
 namespace Rml {
 namespace Rml {
 
 
-struct Mesh {
+struct RMLUICORE_API Mesh {
 	Vector<Vertex> vertices;
 	Vector<Vertex> vertices;
 	Vector<int> indices;
 	Vector<int> indices;
 
 
 	explicit operator bool() const { return !indices.empty(); }
 	explicit operator bool() const { return !indices.empty(); }
+	friend bool operator==(const Mesh& lhs, const Mesh& rhs) { return lhs.vertices == rhs.vertices && lhs.indices == rhs.indices; }
+	friend bool operator!=(const Mesh& lhs, const Mesh& rhs) { return !(lhs == rhs); }
 };
 };
 
 
-struct TexturedMesh {
+struct RMLUICORE_API TexturedMesh {
 	Mesh mesh;
 	Mesh mesh;
 	Texture texture;
 	Texture texture;
 };
 };

+ 1 - 0
Include/RmlUi/Core/RenderManager.h

@@ -122,6 +122,7 @@ private:
 	void Render(const Geometry& geometry, Vector2f translation, Texture texture, const CompiledShader& shader);
 	void Render(const Geometry& geometry, Vector2f translation, Texture texture, const CompiledShader& shader);
 
 
 	void GetTextureSourceList(StringList& source_list) const;
 	void GetTextureSourceList(StringList& source_list) const;
+	const Mesh& GetMesh(const Geometry& geometry) const;
 
 
 	bool ReleaseTexture(const String& texture_source);
 	bool ReleaseTexture(const String& texture_source);
 	void ReleaseAllTextures();
 	void ReleaseAllTextures();

+ 6 - 0
Include/RmlUi/Core/Vertex.h

@@ -47,6 +47,12 @@ struct RMLUICORE_API Vertex {
 	ColourbPremultiplied colour;
 	ColourbPremultiplied colour;
 	/// Texture coordinate for any associated texture.
 	/// Texture coordinate for any associated texture.
 	Vector2f tex_coord;
 	Vector2f tex_coord;
+
+	friend bool operator==(const Vertex& lhs, const Vertex& rhs)
+	{
+		return lhs.position == rhs.position && lhs.colour == rhs.colour && lhs.tex_coord == rhs.tex_coord;
+	}
+	friend bool operator!=(const Vertex& lhs, const Vertex& rhs) { return !(lhs == rhs); }
 };
 };
 
 
 } // namespace Rml
 } // namespace Rml

+ 8 - 10
Source/Core/ElementText.cpp

@@ -308,7 +308,6 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
 void ElementText::ClearLines()
 void ElementText::ClearLines()
 {
 {
 	RMLUI_ZoneScoped;
 	RMLUI_ZoneScoped;
-	geometry.clear();
 	lines.clear();
 	lines.clear();
 	generated_decoration = Style::TextDecoration::None;
 	generated_decoration = Style::TextDecoration::None;
 }
 }
@@ -367,8 +366,6 @@ void ElementText::OnPropertyChange(const PropertyIdSet& changed_properties)
 		changed_properties.Contains(PropertyId::RmlUi_Direction))
 		changed_properties.Contains(PropertyId::RmlUi_Direction))
 	{
 	{
 		font_face_changed = true;
 		font_face_changed = true;
-
-		geometry.clear();
 		geometry_dirty = true;
 		geometry_dirty = true;
 
 
 		font_effects_handle = 0;
 		font_effects_handle = 0;
@@ -456,10 +453,8 @@ void ElementText::GenerateGeometry(RenderManager& render_manager, const FontFace
 	const auto& computed = GetComputedValues();
 	const auto& computed = GetComputedValues();
 	const TextShapingContext text_shaping_context{computed.language(), computed.direction(), computed.letter_spacing()};
 	const TextShapingContext text_shaping_context{computed.language(), computed.direction(), computed.letter_spacing()};
 
 
-	// Release the old geometry, and reuse the mesh buffers.
-	TexturedMeshList mesh_list(geometry.size());
-	for (size_t i = 0; i < geometry.size(); i++)
-		mesh_list[i].mesh = geometry[i].geometry.Release(Geometry::ReleaseMode::ClearMesh);
+	TexturedMeshList mesh_list;
+	mesh_list.reserve(geometry.size());
 
 
 	// Generate the new geometry, one line at a time.
 	// Generate the new geometry, one line at a time.
 	for (size_t i = 0; i < lines.size(); ++i)
 	for (size_t i = 0; i < lines.size(); ++i)
@@ -468,11 +463,14 @@ void ElementText::GenerateGeometry(RenderManager& render_manager, const FontFace
 			lines[i].position, colour, opacity, text_shaping_context, mesh_list);
 			lines[i].position, colour, opacity, text_shaping_context, mesh_list);
 	}
 	}
 
 
-	// Apply the new geometry and textures.
+	// Apply the new geometry and textures. Reuse the old geometry if the mesh matches, which can be relatively common
+	// where the layout is changed in a way that does not visually affect this element.
 	geometry.resize(mesh_list.size());
 	geometry.resize(mesh_list.size());
-	for (size_t i = 0; i < geometry.size(); i++)
+	for (size_t i = 0; i < mesh_list.size(); i++)
 	{
 	{
-		geometry[i].geometry = render_manager.MakeGeometry(std::move(mesh_list[i].mesh));
+		if (!geometry[i].geometry || geometry[i].geometry.GetMesh() != mesh_list[i].mesh)
+			geometry[i].geometry = render_manager.MakeGeometry(std::move(mesh_list[i].mesh));
+
 		geometry[i].texture = mesh_list[i].texture;
 		geometry[i].texture = mesh_list[i].texture;
 	}
 	}
 
 

+ 6 - 0
Source/Core/Geometry.cpp

@@ -58,4 +58,10 @@ Mesh Geometry::Release(ReleaseMode mode)
 	return mesh;
 	return mesh;
 }
 }
 
 
+const Mesh& Geometry::GetMesh() const
+{
+	RMLUI_ASSERT(resource_handle != StableVectorIndex::Invalid);
+	return RenderManagerAccess::GetMesh(render_manager, *this);
+}
+
 } // namespace Rml
 } // namespace Rml

+ 6 - 0
Source/Core/RenderManager.cpp

@@ -269,6 +269,12 @@ void RenderManager::GetTextureSourceList(StringList& source_list) const
 	texture_database->file_database.GetSourceList(source_list);
 	texture_database->file_database.GetSourceList(source_list);
 }
 }
 
 
+const Mesh& RenderManager::GetMesh(const Geometry& geometry) const
+{
+	RMLUI_ASSERT(geometry.render_manager == this && geometry.resource_handle != geometry.InvalidHandle());
+	return geometry_list[geometry.resource_handle].mesh;
+}
+
 bool RenderManager::ReleaseTexture(const String& texture_source)
 bool RenderManager::ReleaseTexture(const String& texture_source)
 {
 {
 	return texture_database->file_database.ReleaseTexture(render_interface, texture_source);
 	return texture_database->file_database.ReleaseTexture(render_interface, texture_source);

+ 5 - 0
Source/Core/RenderManagerAccess.cpp

@@ -53,6 +53,11 @@ void RenderManagerAccess::GetTextureSourceList(RenderManager* render_manager, St
 	render_manager->GetTextureSourceList(source_list);
 	render_manager->GetTextureSourceList(source_list);
 }
 }
 
 
+const Mesh& RenderManagerAccess::GetMesh(RenderManager* render_manager, const Geometry& geometry)
+{
+	return render_manager->GetMesh(geometry);
+}
+
 bool RenderManagerAccess::ReleaseTexture(RenderManager* render_manager, const String& texture_source)
 bool RenderManagerAccess::ReleaseTexture(RenderManager* render_manager, const String& texture_source)
 {
 {
 	return render_manager->ReleaseTexture(texture_source);
 	return render_manager->ReleaseTexture(texture_source);

+ 1 - 0
Source/Core/RenderManagerAccess.h

@@ -55,6 +55,7 @@ private:
 	static void Render(RenderManager* render_manager, const Geometry& geometry, Vector2f translation, Texture texture, const CompiledShader& shader);
 	static void Render(RenderManager* render_manager, const Geometry& geometry, Vector2f translation, Texture texture, const CompiledShader& shader);
 
 
 	static void GetTextureSourceList(RenderManager* render_manager, StringList& source_list);
 	static void GetTextureSourceList(RenderManager* render_manager, StringList& source_list);
+	static const Mesh& GetMesh(RenderManager* render_manager, const Geometry& geometry);
 
 
 	static bool ReleaseTexture(RenderManager* render_manager, const String& texture_source);
 	static bool ReleaseTexture(RenderManager* render_manager, const String& texture_source);
 	static void ReleaseAllTextures(RenderManager* render_manager);
 	static void ReleaseAllTextures(RenderManager* render_manager);