Browse Source

Re-implement Rml::Core::ReleaseCompiledGeometries(). See #84.

Michael Ragazzon 5 years ago
parent
commit
97682a2dff

+ 2 - 0
CMake/FileList.cmake

@@ -35,6 +35,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectGlow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectGlow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectOutline.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.h
     ${PROJECT_SOURCE_DIR}/Source/Core/IdNameMap.h
     ${PROJECT_SOURCE_DIR}/Source/Core/IdNameMap.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.h
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.h
@@ -238,6 +239,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEngineInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Geometry.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Geometry.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBox.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/LayoutBlockBoxSpace.cpp

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

@@ -144,6 +144,8 @@ RMLUICORE_API EventId RegisterEventType(const String& type, bool interruptible,
 
 
 /// Forces all texture handles loaded and generated by RmlUi to be released.
 /// Forces all texture handles loaded and generated by RmlUi to be released.
 RMLUICORE_API void ReleaseTextures();
 RMLUICORE_API void ReleaseTextures();
+/// Forces all compiled geometry handles generated by RmlUi to be released.
+RMLUICORE_API void ReleaseCompiledGeometry();
 
 
 }
 }
 }
 }

+ 19 - 5
Include/RmlUi/Core/Geometry.h

@@ -31,6 +31,7 @@
 
 
 #include "Header.h"
 #include "Header.h"
 #include "Vertex.h"
 #include "Vertex.h"
+#include <stdint.h>
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
@@ -39,6 +40,7 @@ class Context;
 class Element;
 class Element;
 class RenderInterface;
 class RenderInterface;
 struct Texture;
 struct Texture;
+using GeometryDatabaseHandle = uint32_t;
 
 
 /**
 /**
 	A helper object for holding an array of vertices and indices, and compiling it as necessary when rendered.
 	A helper object for holding an array of vertices and indices, and compiling it as necessary when rendered.
@@ -51,6 +53,13 @@ class RMLUICORE_API Geometry
 public:
 public:
 	Geometry(Element* host_element = nullptr);
 	Geometry(Element* host_element = nullptr);
 	Geometry(Context* host_context);
 	Geometry(Context* host_context);
+
+	Geometry(const Geometry&) = delete;
+	Geometry& operator=(const Geometry&) = delete;
+
+	Geometry(Geometry&& other);
+	Geometry& operator=(Geometry&& other);
+
 	~Geometry();
 	~Geometry();
 
 
 	/// Set the host element for this geometry; this should be passed in the constructor if possible.
 	/// Set the host element for this geometry; this should be passed in the constructor if possible.
@@ -79,18 +88,23 @@ public:
 	void Release(bool clear_buffers = false);
 	void Release(bool clear_buffers = false);
 
 
 private:
 private:
+	// Move members from another geometry.
+	void MoveFrom(Geometry& other);
+
 	// Returns the host context's render interface.
 	// Returns the host context's render interface.
 	RenderInterface* GetRenderInterface();
 	RenderInterface* GetRenderInterface();
 
 
-	Context* host_context;
-	Element* host_element;
+	Context* host_context = nullptr;
+	Element* host_element = nullptr;
 
 
 	std::vector< Vertex > vertices;
 	std::vector< Vertex > vertices;
 	std::vector< int > indices;
 	std::vector< int > indices;
-	const Texture* texture;
+	const Texture* texture = nullptr;
+
+	CompiledGeometryHandle compiled_geometry = 0;
+	bool compile_attempted = false;
 
 
-	CompiledGeometryHandle compiled_geometry;
-	bool compile_attempted;
+	GeometryDatabaseHandle database_handle;
 };
 };
 
 
 using GeometryList = std::vector< Geometry >;
 using GeometryList = std::vector< Geometry >;

+ 6 - 1
Source/Core/Core.cpp

@@ -39,6 +39,7 @@
 
 
 #include "EventSpecification.h"
 #include "EventSpecification.h"
 #include "FileInterfaceDefault.h"
 #include "FileInterfaceDefault.h"
+#include "GeometryDatabase.h"
 #include "PluginRegistry.h"
 #include "PluginRegistry.h"
 #include "StyleSheetFactory.h"
 #include "StyleSheetFactory.h"
 #include "TemplateCache.h"
 #include "TemplateCache.h"
@@ -322,11 +323,15 @@ EventId RegisterEventType(const String& type, bool interruptible, bool bubbles,
 	return EventSpecificationInterface::InsertOrReplaceCustom(type, interruptible, bubbles, default_action_phase);
 	return EventSpecificationInterface::InsertOrReplaceCustom(type, interruptible, bubbles, default_action_phase);
 }
 }
 
 
-// Forces all texture handles loaded and generated by RmlUi to be released.
 void ReleaseTextures()
 void ReleaseTextures()
 {
 {
 	TextureDatabase::ReleaseTextures();
 	TextureDatabase::ReleaseTextures();
 }
 }
 
 
+void ReleaseCompiledGeometry()
+{
+	return GeometryDatabase::ReleaseAll();
+}
+
 }
 }
 }
 }

+ 31 - 11
Source/Core/Geometry.cpp

@@ -32,34 +32,54 @@
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
 #include "../../Include/RmlUi/Core/RenderInterface.h"
 #include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "GeometryDatabase.h"
+#include <utility>
+
 
 
 namespace Rml {
 namespace Rml {
 namespace Core {
 namespace Core {
 
 
 Geometry::Geometry(Element* _host_element)
 Geometry::Geometry(Element* _host_element)
 {
 {
-	host_element = _host_element;
-	host_context = nullptr;
+	database_handle = GeometryDatabase::Insert(this);
+}
 
 
-	texture = nullptr;
+Geometry::Geometry(Context* _host_context)
+{
+	database_handle = GeometryDatabase::Insert(this);
+}
 
 
-	compile_attempted = false;
-	compiled_geometry = 0;
+Geometry::Geometry(Geometry&& other)
+{
+	MoveFrom(other);
+	database_handle = GeometryDatabase::Insert(this);
 }
 }
 
 
-Geometry::Geometry(Context* _host_context)
+Geometry& Geometry::operator=(Geometry&& other)
 {
 {
-	host_element = nullptr;
-	host_context = _host_context;
+	MoveFrom(other);
+	// Keep the database handles from construction unchanged, they are tied to the *this* pointer and should not change.
+	return *this;
+}
 
 
-	texture = nullptr;
+void Geometry::MoveFrom(Geometry& other)
+{
+	host_context = std::exchange(other.host_context, nullptr);
+	host_element = std::exchange(other.host_element, nullptr);
 
 
-	compile_attempted = false;
-	compiled_geometry = 0;
+	vertices = std::move(other.vertices);
+	indices = std::move(other.indices);
+
+	texture = std::exchange(other.texture, nullptr);
+
+	compiled_geometry = std::exchange(other.compiled_geometry, 0);
+	compile_attempted = std::exchange(other.compile_attempted, false);
 }
 }
 
 
 Geometry::~Geometry()
 Geometry::~Geometry()
 {
 {
+	GeometryDatabase::Erase(database_handle);
+
 	Release();
 	Release();
 }
 }
 
 

+ 184 - 0
Source/Core/GeometryDatabase.cpp

@@ -0,0 +1,184 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "GeometryDatabase.h"
+#include "../../Include/RmlUi/Core/Geometry.h"
+#include <algorithm>
+
+namespace Rml {
+namespace Core {
+namespace GeometryDatabase {
+
+class Database {
+public:
+	Database() {
+		constexpr size_t reserve_size = 512;
+		geometry_list.reserve(reserve_size);
+		free_list.reserve(reserve_size);
+	}
+
+	~Database() {
+#ifdef RMLUI_TESTS_ENABLED
+		RMLUI_ASSERT(geometry_list.size() == free_list.size());
+		std::sort(free_list.begin(), free_list.end());
+		for (size_t i = 0; i < free_list.size(); i++)
+		{
+			RMLUI_ASSERT(i == free_list[i]);
+		}
+#endif
+	}
+
+	GeometryDatabaseHandle insert(Geometry* value)
+	{
+		GeometryDatabaseHandle handle;
+		if (free_list.empty())
+		{
+			handle = GeometryDatabaseHandle(geometry_list.size());
+			geometry_list.push_back(value);
+		}
+		else
+		{
+			handle = free_list.back();
+			free_list.pop_back();
+			geometry_list[handle] = value;
+		}
+		return handle;
+	}
+
+	void erase(GeometryDatabaseHandle handle)
+	{
+		free_list.push_back(handle);
+	}
+
+	// Iterate over every item in the database, skipping free slots.
+	template<typename Func>
+	void for_each(Func func)
+	{
+		std::sort(free_list.begin(), free_list.end());
+
+		size_t i_begin_next = 0;
+		for (GeometryDatabaseHandle freelist_entry : free_list)
+		{
+			const size_t i_end = size_t(freelist_entry);
+			const size_t i_begin = i_begin_next;
+			i_begin_next = i_end + 1;
+
+			for (size_t i = i_begin; i < i_end; i++)
+				func(geometry_list[i]);
+		}
+
+		for (size_t i = i_begin_next; i < geometry_list.size(); i++)
+				func(geometry_list[i]);
+	}
+
+private:
+	// List of all active geometry, in addition to free slots.
+	// Free slots (as defined by the 'free_list') may contain dangling pointers and must not be dereferenced.
+	std::vector<Geometry*> geometry_list;
+	// Declares free slots in the 'geometry_list' as indices.
+	std::vector<GeometryDatabaseHandle> free_list;
+};
+
+
+static Database geometry_database;
+
+GeometryDatabaseHandle Insert(Geometry* geometry)
+{
+	return geometry_database.insert(geometry);
+}
+
+void Erase(GeometryDatabaseHandle handle)
+{
+	geometry_database.erase(handle);
+}
+
+void ReleaseAll()
+{
+	geometry_database.for_each([](Geometry* geometry) {
+		geometry->Release();
+	});
+}
+
+
+
+#ifdef RMLUI_TESTS_ENABLED
+
+static class TestGeometryDatabase {
+private:
+	std::vector<Geometry> geometry_list;
+
+	bool list_database_equivalent()
+	{
+		int i = 0;
+		bool result = true;
+		GetDatabase().for_each([this, &i, &result](Geometry* geometry) {
+			result &= (geometry == &geometry_list[i++]);
+		});
+		return result;
+	}
+
+public:
+	TestGeometryDatabase() : geometry_list(10)
+	{
+		bool result = true;
+
+		int i = 0;
+		for (auto& geometry : geometry_list)
+			geometry.GetIndices().push_back(i++);
+
+		result &= list_database_equivalent();
+
+		geometry_list.reserve(2000);
+		result &= list_database_equivalent();
+
+		geometry_list.erase(geometry_list.begin() + 5);
+		result &= list_database_equivalent();
+
+		std::swap(geometry_list.front(), geometry_list.back());
+		geometry_list.pop_back();
+		result &= list_database_equivalent();
+
+		std::swap(geometry_list.front(), geometry_list.back());
+		result &= list_database_equivalent();
+
+		geometry_list.emplace_back();
+		result &= list_database_equivalent();
+
+		geometry_list.clear();
+		result &= list_database_equivalent();
+
+		RMLUI_ASSERT(result);
+	}
+
+} test_geometry_database;
+
+#endif
+
+}
+}
+}

+ 61 - 0
Source/Core/GeometryDatabase.h

@@ -0,0 +1,61 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUICOREGEOMETRYDATABASE_H
+#define RMLUICOREGEOMETRYDATABASE_H
+
+#include <stdint.h>
+
+namespace Rml {
+namespace Core {
+
+class Geometry;
+using GeometryDatabaseHandle = uint32_t;
+
+/**
+    The geometry database stores a reference to all active geometry.
+
+    The intention is for the user to be able to re-compile all geometry in use.
+
+    It is expected that every Insert() call is followed (at some later time) by
+    exactly one Erase() call with the same handle value.
+*/
+
+namespace GeometryDatabase {
+
+    GeometryDatabaseHandle Insert(Geometry* geometry);
+    void Erase(GeometryDatabaseHandle handle);
+
+    void ReleaseAll();
+
+}
+
+}
+}
+
+#endif