ソースを参照

Only allow a single render interface, remove the ability to set per-context render interfaces [breaking change]

Now, only a single render interface can be set, and it must be set globally before the call to Rml::Initialise. Furthermore, the render interface cannot be changed while the library is initialised, and must live until after the call to Rml::Shutdown. If these conditions are not met, the library will submit error messages to the user in most cases.

This change is motivated by several reasons:

- The feature added considerable complexity, since several types of render state needed to store their state per render interface.
- This complexity would have become more pronounced with upcoming changes for advanced render effects.
- It was easy to make bugs where render resources (e.g. textures) would be freed in the wrong render interface.
- It was difficult to test for the presence of the above bugs.
- No tests existed already, it is very likely that bugs were already present.
Michael Ragazzon 2 年 前
コミット
0bbd0193cb
42 ファイル変更204 行追加397 行削除
  1. 0 7
      Include/RmlUi/Core/Context.h
  2. 3 9
      Include/RmlUi/Core/Core.h
  3. 0 5
      Include/RmlUi/Core/Element.h
  4. 3 5
      Include/RmlUi/Core/ElementUtilities.h
  5. 1 13
      Include/RmlUi/Core/Geometry.h
  6. 0 11
      Include/RmlUi/Core/RenderInterface.h
  7. 6 8
      Include/RmlUi/Core/Texture.h
  8. 3 2
      Samples/invaders/src/DecoratorDefender.cpp
  9. 0 4
      Samples/invaders/src/DecoratorStarfield.cpp
  10. 1 1
      Samples/invaders/src/Game.cpp
  11. 3 2
      Samples/luainvaders/src/DecoratorDefender.cpp
  12. 0 4
      Samples/luainvaders/src/DecoratorStarfield.cpp
  13. 1 1
      Samples/luainvaders/src/Game.cpp
  14. 1 18
      Source/Core/Context.cpp
  15. 15 17
      Source/Core/Core.cpp
  16. 1 1
      Source/Core/DecoratorGradient.cpp
  17. 2 3
      Source/Core/DecoratorNinePatch.cpp
  18. 20 31
      Source/Core/DecoratorTiled.cpp
  19. 6 7
      Source/Core/DecoratorTiled.h
  20. 13 17
      Source/Core/DecoratorTiledBox.cpp
  21. 8 11
      Source/Core/DecoratorTiledHorizontal.cpp
  22. 5 3
      Source/Core/DecoratorTiledImage.cpp
  23. 8 11
      Source/Core/DecoratorTiledVertical.cpp
  24. 1 9
      Source/Core/Element.cpp
  25. 1 1
      Source/Core/ElementBackgroundBorder.cpp
  26. 1 1
      Source/Core/ElementBackgroundBorder.h
  27. 1 3
      Source/Core/ElementText.cpp
  28. 18 41
      Source/Core/ElementUtilities.cpp
  29. 4 4
      Source/Core/Elements/ElementImage.cpp
  30. 2 2
      Source/Core/Elements/ElementProgress.cpp
  31. 1 1
      Source/Core/Elements/WidgetTextInput.cpp
  32. 7 44
      Source/Core/Geometry.cpp
  33. 4 13
      Source/Core/RenderInterface.cpp
  34. 5 5
      Source/Core/Texture.cpp
  35. 9 9
      Source/Core/TextureDatabase.cpp
  36. 4 6
      Source/Core/TextureDatabase.h
  37. 29 49
      Source/Core/TextureResource.cpp
  38. 9 9
      Source/Core/TextureResource.h
  39. 5 6
      Source/Debugger/Geometry.cpp
  40. 1 1
      Source/Lottie/ElementLottie.cpp
  41. 1 1
      Source/SVG/ElementSVG.cpp
  42. 1 1
      Tests/Source/VisualTests/main.cpp

+ 0 - 7
Include/RmlUi/Core/Context.h

@@ -42,7 +42,6 @@ class Stream;
 class ContextInstancer;
 class ElementDocument;
 class EventListener;
-class RenderInterface;
 class DataModel;
 class DataModelConstructor;
 class DataTypeRegister;
@@ -247,9 +246,6 @@ public:
 	/// @param[in] speed_factor A factor for adjusting the final smooth scrolling speed, must be strictly positive, defaults to 1.0.
 	void SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float speed_factor);
 
-	/// Gets the context's render interface.
-	/// @return The render interface the context renders through.
-	RenderInterface* GetRenderInterface() const;
 	/// Gets the current clipping region for the render traversal
 	/// @param[out] origin The clipping origin
 	/// @param[out] dimensions The clipping dimensions
@@ -373,8 +369,6 @@ private:
 	// itself can't be part of it.
 	ElementSet drag_hover_chain;
 
-	// The render interface this context renders through.
-	RenderInterface* render_interface;
 	Vector2i clip_origin;
 	Vector2i clip_dimensions;
 
@@ -426,7 +420,6 @@ private:
 	static void SendEvents(const ElementSet& old_items, const ElementSet& new_items, EventId id, const Dictionary& parameters);
 
 	friend class Rml::Element;
-	friend RMLUICORE_API Context* CreateContext(const String&, Vector2i, RenderInterface*);
 };
 
 } // namespace Rml

+ 3 - 9
Include/RmlUi/Core/Core.h

@@ -66,9 +66,7 @@ RMLUICORE_API void SetSystemInterface(SystemInterface* system_interface);
 /// Returns RmlUi's system interface.
 RMLUICORE_API SystemInterface* GetSystemInterface();
 
-/// Sets the interface through which all rendering requests are made. This is not required to be called, but if it is
-/// it must be called before Initialise(). If no render interface is specified, then all contexts must have a custom
-/// render interface.
+/// Sets the interface through which all rendering requests are made. This must be called before Initialise().
 /// @param[in] render_interface A non-owning pointer to the render interface implementation.
 /// @lifetime The interface must be kept alive until after the call to Rml::Shutdown.
 RMLUICORE_API void SetRenderInterface(RenderInterface* render_interface);
@@ -94,11 +92,8 @@ RMLUICORE_API FontEngineInterface* GetFontEngineInterface();
 /// Creates a new element context.
 /// @param[in] name The new name of the context. This must be unique.
 /// @param[in] dimensions The initial dimensions of the new context.
-/// @param[in] render_interface The custom render interface to use, or nullptr to use the default.
-/// @lifetime If specified, the render interface must be kept alive until after the call to Rml::Shutdown. Alternatively, the render interface can be
-///           destroyed after all contexts it belongs to have been destroyed and a subsequent call has been made to Rml::ReleaseTextures.
 /// @return A non-owning pointer to the new context, or nullptr if the context could not be created.
-RMLUICORE_API Context* CreateContext(const String& name, Vector2i dimensions, RenderInterface* render_interface = nullptr);
+RMLUICORE_API Context* CreateContext(const String& name, Vector2i dimensions);
 /// Removes and destroys a context.
 /// @param[in] name The name of the context to remove.
 /// @return True if name is a valid context, false otherwise.
@@ -154,8 +149,7 @@ RMLUICORE_API EventId RegisterEventType(const String& type, bool interruptible,
 /// Returns a list of source URLs to textures in all loaded documents.
 RMLUICORE_API StringList GetTextureSourceList();
 /// Forces all texture handles loaded and generated by RmlUi to be released.
-/// @param[in] render_interface Release all textures belonging to the given interface, or nullptr to release all textures in all interfaces.
-RMLUICORE_API void ReleaseTextures(RenderInterface* render_interface = nullptr);
+RMLUICORE_API void ReleaseTextures();
 /// Forces all compiled geometry handles generated by RmlUi to be released.
 RMLUICORE_API void ReleaseCompiledGeometry();
 /// Releases unused font textures and rendered glyphs to free up memory, and regenerates actively used fonts.

+ 0 - 5
Include/RmlUi/Core/Element.h

@@ -60,7 +60,6 @@ class InlineLevelBox;
 class ReplacedBox;
 class PropertiesIteratorView;
 class PropertyDictionary;
-class RenderInterface;
 class StyleSheet;
 class StyleSheetContainer;
 class TransformState;
@@ -584,10 +583,6 @@ public:
 	DataModel* GetDataModel() const;
 	//@}
 
-	/// Gets the render interface owned by this element's context.
-	/// @return The element's context's render interface.
-	RenderInterface* GetRenderInterface();
-
 	/// Sets the instancer to use for releasing this element.
 	/// @param[in] instancer Instancer to set on this element.
 	void SetInstancer(ElementInstancer* instancer);

+ 3 - 5
Include/RmlUi/Core/ElementUtilities.h

@@ -36,7 +36,6 @@ namespace Rml {
 
 class Box;
 class Context;
-class RenderInterface;
 namespace Style {
 	class ComputedValues;
 }
@@ -100,8 +99,7 @@ public:
 	static bool SetClippingRegion(Element* element, Context* context = nullptr);
 	/// Applies the clip region from the render interface to the renderer
 	/// @param[in] context The context to read the clip region from
-	/// @param[in] render_interface The render interface to update.
-	static void ApplyActiveClipRegion(Context* context, RenderInterface* render_interface);
+	static void ApplyActiveClipRegion(Context* context);
 
 	/// Formats the contents of an element. This does not need to be called for ordinary elements, but can be useful
 	/// for non-DOM elements of custom elements.
@@ -124,9 +122,9 @@ public:
 	static bool PositionElement(Element* element, Vector2f offset, PositionAnchor anchor);
 
 	/// Applies an element's accumulated transform matrix, determined from its and ancestor's `perspective' and `transform' properties.
-	/// Note: All calls to RenderInterface::SetTransform must go through here.
-	/// @param[in] element		The element whose transform to apply.
+	/// @param[in] element The element whose transform to apply.
 	/// @return true if a render interface is available to set the transform.
+	/// @note All calls to RenderInterface::SetTransform must go through here.
 	static bool ApplyTransform(Element& element);
 
 	/// Creates data views and data controllers if a data model applies to the element.

+ 1 - 13
Include/RmlUi/Core/Geometry.h

@@ -37,7 +37,6 @@ namespace Rml {
 
 class Context;
 class Element;
-class RenderInterface;
 struct Texture;
 using GeometryDatabaseHandle = uint32_t;
 
@@ -49,8 +48,7 @@ using GeometryDatabaseHandle = uint32_t;
 
 class RMLUICORE_API Geometry {
 public:
-	Geometry(Element* host_element = nullptr);
-	Geometry(Context* host_context);
+	Geometry();
 
 	Geometry(const Geometry&) = delete;
 	Geometry& operator=(const Geometry&) = delete;
@@ -60,10 +58,6 @@ public:
 
 	~Geometry();
 
-	/// Set the host element for this geometry; this should be passed in the constructor if possible.
-	/// @param[in] host_element The new host element for the geometry.
-	void SetHostElement(Element* host_element);
-
 	/// Attempts to compile the geometry if appropriate, then renders the geometry, compiled if it can.
 	/// @param[in] translation The translation of the geometry.
 	void Render(Vector2f translation);
@@ -92,12 +86,6 @@ private:
 	// Move members from another geometry.
 	void MoveFrom(Geometry& other) noexcept;
 
-	// Returns the host context's render interface.
-	RenderInterface* GetRenderInterface();
-
-	Context* host_context = nullptr;
-	Element* host_element = nullptr;
-
 	Vector<Vertex> vertices;
 	Vector<int> indices;
 	const Texture* texture = nullptr;

+ 0 - 11
Include/RmlUi/Core/RenderInterface.h

@@ -37,8 +37,6 @@
 
 namespace Rml {
 
-class Context;
-
 /**
     The abstract base class for application-specific rendering implementation. Your application must provide a concrete
     implementation of this class and install it through Rml::SetRenderInterface() in order for anything to be rendered.
@@ -112,15 +110,6 @@ public:
 	/// is submitted. Then it expects the renderer to use an identity matrix or otherwise omit the multiplication with the transform.
 	/// @param[in] transform The new transform to apply, or nullptr if no transform applies to the current element.
 	virtual void SetTransform(const Matrix4f* transform);
-
-	/// Get the context currently being rendered. This is only valid during RenderGeometry,
-	/// CompileGeometry, RenderCompiledGeometry, EnableScissorRegion and SetScissorRegion.
-	Context* GetContext() const;
-
-private:
-	Context* context;
-
-	friend class Rml::Context;
 };
 
 } // namespace Rml

+ 6 - 8
Include/RmlUi/Core/Texture.h

@@ -68,14 +68,12 @@ public:
 	/// Returns the texture's source name. This is usually the name of the file the texture was loaded from.
 	/// @return The name of the this texture's source. This will be the empty string if this texture is not loaded.
 	const String& GetSource() const;
-	/// Returns the texture's handle.
-	/// @param[in] The render interface that is requesting the handle.
-	/// @return The texture's handle. This will be nullptr if the texture isn't loaded.
-	TextureHandle GetHandle(RenderInterface* render_interface) const;
-	/// Returns the texture's dimensions.
-	/// @param[in] The render interface that is requesting the dimensions.
-	/// @return The texture's dimensions. This will be (0, 0) if the texture isn't loaded.
-	Vector2i GetDimensions(RenderInterface* render_interface) const;
+	/// Returns the texture's handle, will attempt to load the texture as necessary.
+	/// @return The texture's handle. This will be 0 if the texture cannot be loaded.
+	TextureHandle GetHandle() const;
+	/// Returns the texture's dimensions, will attempt to load the texture as necessary.
+	/// @return The texture's dimensions. This will be (0, 0) if the texture cannot be loaded.
+	Vector2i GetDimensions() const;
 
 	/// Returns true if the texture points to the same underlying resource.
 	bool operator==(const Texture&) const;

+ 3 - 2
Samples/invaders/src/DecoratorDefender.cpp

@@ -27,6 +27,7 @@
  */
 
 #include "DecoratorDefender.h"
+#include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
 #include <RmlUi/Core/GeometryUtilities.h>
 #include <RmlUi/Core/Math.h>
@@ -59,9 +60,9 @@ void DecoratorDefender::RenderElement(Rml::Element* element, Rml::DecoratorDataH
 	Rml::Vector2f size = element->GetBox().GetSize(Rml::Box::PADDING);
 	Rml::Math::SnapToPixelGrid(position, size);
 
-	if (Rml::RenderInterface* render_interface = element->GetRenderInterface())
+	if (Rml::RenderInterface* render_interface = ::Rml::GetRenderInterface())
 	{
-		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle(render_interface);
+		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle();
 		Rml::Colourb color = element->GetProperty<Rml::Colourb>("color");
 
 		Rml::Vertex vertices[4];

+ 0 - 4
Samples/invaders/src/DecoratorStarfield.cpp

@@ -106,10 +106,6 @@ void DecoratorStarfield::RenderElement(Rml::Element* element, Rml::DecoratorData
 	const float dp_ratio = Rml::ElementUtilities::GetDensityIndependentPixelRatio(element);
 	const float point_size = Rml::Math::RoundUp(2.f * dp_ratio);
 
-	Rml::RenderInterface* render_interface = element->GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	int num_stars = 0;
 
 	for (size_t i = 0; i < star_field->star_layers.size(); i++)

+ 1 - 1
Samples/invaders/src/Game.cpp

@@ -144,7 +144,7 @@ void Game::Render(float dp_ratio)
 	if (defender_lives <= 0)
 		return;
 
-	Rml::TextureHandle texture_handle = texture.GetHandle(Rml::GetRenderInterface());
+	Rml::TextureHandle texture_handle = texture.GetHandle();
 
 	// Render all available shields
 	for (int i = 0; i < NUM_SHIELDS; i++)

+ 3 - 2
Samples/luainvaders/src/DecoratorDefender.cpp

@@ -27,6 +27,7 @@
  */
 
 #include "DecoratorDefender.h"
+#include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
 #include <RmlUi/Core/GeometryUtilities.h>
 #include <RmlUi/Core/Math.h>
@@ -59,9 +60,9 @@ void DecoratorDefender::RenderElement(Rml::Element* element, Rml::DecoratorDataH
 	Rml::Vector2f size = element->GetBox().GetSize(Rml::Box::PADDING);
 	Rml::Math::SnapToPixelGrid(position, size);
 
-	if (Rml::RenderInterface* render_interface = element->GetRenderInterface())
+	if (Rml::RenderInterface* render_interface = ::Rml::GetRenderInterface())
 	{
-		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle(render_interface);
+		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle();
 		Rml::Colourb color = element->GetProperty<Rml::Colourb>("color");
 
 		Rml::Vertex vertices[4];

+ 0 - 4
Samples/luainvaders/src/DecoratorStarfield.cpp

@@ -106,10 +106,6 @@ void DecoratorStarfield::RenderElement(Rml::Element* element, Rml::DecoratorData
 	const float dp_ratio = Rml::ElementUtilities::GetDensityIndependentPixelRatio(element);
 	const float point_size = Rml::Math::RoundUp(2.f * dp_ratio);
 
-	Rml::RenderInterface* render_interface = element->GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	int num_stars = 0;
 
 	for (size_t i = 0; i < star_field->star_layers.size(); i++)

+ 1 - 1
Samples/luainvaders/src/Game.cpp

@@ -142,7 +142,7 @@ void Game::Render(float dp_ratio)
 	if (defender_lives <= 0)
 		return;
 
-	Rml::TextureHandle texture_handle = texture.GetHandle(Rml::GetRenderInterface());
+	Rml::TextureHandle texture_handle = texture.GetHandle();
 
 	// Render all available shields
 	for (int i = 0; i < NUM_SHIELDS; i++)

+ 1 - 18
Source/Core/Context.cpp

@@ -60,9 +60,6 @@ Context::Context(const String& name) :
 {
 	instancer = nullptr;
 
-	// Initialise this to nullptr; this will be set in Rml::CreateContext().
-	render_interface = nullptr;
-
 	root = Factory::InstanceElement(nullptr, "*", "#root", XMLAttributes());
 	root->SetId(name);
 	root->SetOffset(Vector2f(0, 0), nullptr);
@@ -117,8 +114,6 @@ Context::~Context()
 	cursor_proxy.reset();
 
 	instancer = nullptr;
-
-	render_interface = nullptr;
 }
 
 const String& Context::GetName() const
@@ -223,12 +218,7 @@ bool Context::Render()
 {
 	RMLUI_ZoneScoped;
 
-	RenderInterface* render_interface = GetRenderInterface();
-	if (render_interface == nullptr)
-		return false;
-
-	render_interface->context = this;
-	ElementUtilities::ApplyActiveClipRegion(this, render_interface);
+	ElementUtilities::ApplyActiveClipRegion(this);
 
 	root->Render();
 
@@ -243,8 +233,6 @@ bool Context::Render()
 		cursor_proxy->Render();
 	}
 
-	render_interface->context = nullptr;
-
 	return true;
 }
 
@@ -867,11 +855,6 @@ void Context::SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float spe
 	scroll_controller->SetDefaultScrollBehavior(scroll_behavior, speed_factor);
 }
 
-RenderInterface* Context::GetRenderInterface() const
-{
-	return render_interface;
-}
-
 bool Context::GetActiveClipRegion(Vector2i& origin, Vector2i& dimensions) const
 {
 	if (clip_dimensions.x < 0 || clip_dimensions.y < 0)

+ 15 - 17
Source/Core/Core.cpp

@@ -99,6 +99,11 @@ bool Initialise()
 		Log::Message(Log::LT_ERROR, "No system interface set!");
 		return false;
 	}
+	if (!render_interface)
+	{
+		Log::Message(Log::LT_ERROR, "No render interface set!");
+		return false;
+	}
 
 	if (!file_interface)
 	{
@@ -121,7 +126,7 @@ bool Initialise()
 		default_font_interface = MakeUnique<FontEngineInterfaceDefault>();
 		font_interface = default_font_interface.get();
 #else
-		Log::Message(Log::LT_ERROR, "No font interface set!");
+		Log::Message(Log::LT_ERROR, "No font engine interface set!");
 		return false;
 #endif
 	}
@@ -202,6 +207,12 @@ SystemInterface* GetSystemInterface()
 
 void SetRenderInterface(RenderInterface* _render_interface)
 {
+	if (initialised)
+	{
+		Log::Message(Log::LT_ERROR, "The render interface is not allowed to be set or changed after RmlUi has been initialised.");
+		return;
+	}
+
 	render_interface = _render_interface;
 }
 
@@ -230,18 +241,11 @@ FontEngineInterface* GetFontEngineInterface()
 	return font_interface;
 }
 
-Context* CreateContext(const String& name, const Vector2i dimensions, RenderInterface* custom_render_interface)
+Context* CreateContext(const String& name, const Vector2i dimensions)
 {
 	if (!initialised)
 		return nullptr;
 
-	if (!custom_render_interface && !render_interface)
-	{
-		Log::Message(Log::LT_WARNING, "Failed to create context '%s', no render interface specified and no default render interface exists.",
-			name.c_str());
-		return nullptr;
-	}
-
 	if (GetContext(name))
 	{
 		Log::Message(Log::LT_WARNING, "Failed to create context '%s', context already exists.", name.c_str());
@@ -255,12 +259,6 @@ Context* CreateContext(const String& name, const Vector2i dimensions, RenderInte
 		return nullptr;
 	}
 
-	// Set the render interface on the context, and add a reference onto it.
-	if (custom_render_interface)
-		new_context->render_interface = custom_render_interface;
-	else
-		new_context->render_interface = render_interface;
-
 	new_context->SetDimensions(dimensions);
 
 	Context* new_context_raw = new_context.get();
@@ -352,9 +350,9 @@ StringList GetTextureSourceList()
 	return TextureDatabase::GetSourceList();
 }
 
-void ReleaseTextures(RenderInterface* in_render_interface)
+void ReleaseTextures()
 {
-	TextureDatabase::ReleaseTextures(in_render_interface);
+	TextureDatabase::ReleaseTextures();
 }
 
 void ReleaseCompiledGeometry()

+ 1 - 1
Source/Core/DecoratorGradient.cpp

@@ -61,7 +61,7 @@ bool DecoratorGradient::Initialise(const Direction dir_, const Colourb start_, c
 
 DecoratorDataHandle DecoratorGradient::GenerateElementData(Element* element) const
 {
-	Geometry* geometry = new Geometry(element);
+	Geometry* geometry = new Geometry();
 	const Box& box = element->GetBox();
 
 	const ComputedValues& computed = element->GetComputedValues();

+ 2 - 3
Source/Core/DecoratorNinePatch.cpp

@@ -56,14 +56,13 @@ bool DecoratorNinePatch::Initialise(const Rectanglef& _rect_outer, const Rectang
 
 DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element) const
 {
-	RenderInterface* render_interface = element->GetRenderInterface();
 	const auto& computed = element->GetComputedValues();
 
-	Geometry* data = new Geometry(element);
+	Geometry* data = new Geometry();
 
 	const Texture* texture = GetTexture();
 	data->SetTexture(texture);
-	const Vector2f texture_dimensions(texture->GetDimensions(render_interface));
+	const Vector2f texture_dimensions(texture->GetDimensions());
 
 	const Vector2f surface_dimensions = element->GetBox().GetSize(Box::PADDING).Round();
 

+ 20 - 31
Source/Core/DecoratorTiled.cpp

@@ -53,80 +53,69 @@ DecoratorTiled::Tile::Tile() : display_scale(1), position(0, 0), size(0, 0)
 	orientation = ORIENTATION_NONE;
 }
 
-void DecoratorTiled::Tile::CalculateDimensions(Element* element, const Texture& texture) const
+void DecoratorTiled::Tile::CalculateDimensions(const Texture& texture) const
 {
-	RenderInterface* render_interface = element->GetRenderInterface();
-	auto data_iterator = data.find(render_interface);
-	if (data_iterator == data.end())
+	if (!tile_data_calculated)
 	{
-		TileData new_data;
-		const Vector2f texture_dimensions(texture.GetDimensions(render_interface));
+		tile_data_calculated = true;
+		tile_data = {};
 
+		const Vector2f texture_dimensions(texture.GetDimensions());
 		if (texture_dimensions.x == 0 || texture_dimensions.y == 0)
 		{
-			new_data.size = Vector2f(0, 0);
-			new_data.texcoords[0] = Vector2f(0, 0);
-			new_data.texcoords[1] = Vector2f(0, 0);
+			tile_data.size = Vector2f(0, 0);
+			tile_data.texcoords[0] = Vector2f(0, 0);
+			tile_data.texcoords[1] = Vector2f(0, 0);
 		}
 		else
 		{
 			// Need to scale the coordinates to normalized units and 'size' to absolute size (pixels)
 			if (size.x == 0 && size.y == 0 && position.x == 0 && position.y == 0)
-				new_data.size = texture_dimensions;
+				tile_data.size = texture_dimensions;
 			else
-				new_data.size = size;
+				tile_data.size = size;
 
-			const Vector2f size_relative = new_data.size / texture_dimensions;
+			const Vector2f size_relative = tile_data.size / texture_dimensions;
 
-			new_data.size = Vector2f(Math::Absolute(new_data.size.x), Math::Absolute(new_data.size.y));
+			tile_data.size = Vector2f(Math::Absolute(tile_data.size.x), Math::Absolute(tile_data.size.y));
 
-			new_data.texcoords[0] = position / texture_dimensions;
-			new_data.texcoords[1] = size_relative + new_data.texcoords[0];
+			tile_data.texcoords[0] = position / texture_dimensions;
+			tile_data.texcoords[1] = size_relative + tile_data.texcoords[0];
 		}
-
-		data.emplace(render_interface, new_data);
 	}
 }
 
 Vector2f DecoratorTiled::Tile::GetNaturalDimensions(Element* element) const
 {
-	RenderInterface* render_interface = element->GetRenderInterface();
-	auto data_iterator = data.find(render_interface);
-	if (data_iterator == data.end())
+	if (!tile_data_calculated)
 		return Vector2f(0, 0);
 
 	const float scale_raw_to_natural_dimensions = ElementUtilities::GetDensityIndependentPixelRatio(element) * display_scale;
-	const Vector2f raw_dimensions = data_iterator->second.size;
+	const Vector2f raw_dimensions = tile_data.size;
 
 	return raw_dimensions * scale_raw_to_natural_dimensions;
 }
 
-void DecoratorTiled::Tile::GenerateGeometry(Vector<Vertex>& vertices, Vector<int>& indices, Element* element, const Vector2f surface_origin,
-	const Vector2f surface_dimensions, const Vector2f tile_dimensions) const
+void DecoratorTiled::Tile::GenerateGeometry(Vector<Vertex>& vertices, Vector<int>& indices, const ComputedValues& computed,
+	const Vector2f surface_origin, const Vector2f surface_dimensions, const Vector2f tile_dimensions) const
 {
 	if (surface_dimensions.x <= 0 || surface_dimensions.y <= 0)
 		return;
 
-	RenderInterface* render_interface = element->GetRenderInterface();
-	const auto& computed = element->GetComputedValues();
-
 	float opacity = computed.opacity();
 	Colourb quad_colour = computed.image_color();
 
 	// Apply opacity
 	quad_colour.alpha = (byte)(opacity * (float)quad_colour.alpha);
 
-	auto data_iterator = data.find(render_interface);
-	if (data_iterator == data.end())
+	if (!tile_data_calculated)
 		return;
 
-	const TileData& data = data_iterator->second;
-
 	// Generate the oriented texture coordinates for the tiles.
 	Vector2f scaled_texcoords[2];
 	for (int i = 0; i < 2; i++)
 	{
-		scaled_texcoords[i] = data.texcoords[0] + oriented_texcoords[orientation][i] * (data.texcoords[1] - data.texcoords[0]);
+		scaled_texcoords[i] = tile_data.texcoords[0] + oriented_texcoords[orientation][i] * (tile_data.texcoords[1] - tile_data.texcoords[0]);
 	}
 
 	Vector2f final_tile_dimensions;

+ 6 - 7
Source/Core/DecoratorTiled.h

@@ -79,7 +79,7 @@ public:
 		Tile();
 
 		/// Calculates the tile's dimensions from the texture and texture coordinates.
-		void CalculateDimensions(Element* element, const Texture& texture) const;
+		void CalculateDimensions(const Texture& texture) const;
 		/// Get the dimensions (in px) that this tile is ideally displayed as.
 		/// Uses the dp-ratio of the current element and 'display_scale' to calculate the dimensions.
 		Vector2f GetNaturalDimensions(Element* element) const;
@@ -87,20 +87,18 @@ public:
 		/// Generates geometry to render this tile across a surface.
 		/// @param[out] vertices The array to store the generated vertex data.
 		/// @param[out] indices The array to store the generated index data.
-		/// @param[in] element The element hosting the decorator.
+		/// @param[in] computed_values The computed values of the element being decorated.
 		/// @param[in] surface_origin The starting point of the first tile to generate.
 		/// @param[in] surface_dimensions The dimensions of the surface to be tiled.
 		/// @param[in] tile_dimensions The dimensions to render this tile at.
-		void GenerateGeometry(Vector<Vertex>& vertices, Vector<int>& indices, Element* element, Vector2f surface_origin, Vector2f surface_dimensions,
-			Vector2f tile_dimensions) const;
+		void GenerateGeometry(Vector<Vertex>& vertices, Vector<int>& indices, const ComputedValues& computed_values, Vector2f surface_origin,
+			Vector2f surface_dimensions, Vector2f tile_dimensions) const;
 
 		struct TileData {
 			Vector2f size;         // 'px' units
 			Vector2f texcoords[2]; // relative units
 		};
 
-		using TileDataMap = SmallUnorderedMap<RenderInterface*, TileData>;
-
 		int texture_index;
 
 		// Scales the desired displayed size of the tile from raw pixel size. Eg. the 'display_scale' in a sprite sheet.
@@ -109,7 +107,8 @@ public:
 		// Position and size within the texture, absolute units (px)
 		Vector2f position, size;
 
-		mutable TileDataMap data;
+		mutable TileData tile_data;
+		mutable bool tile_data_calculated = false;
 
 		TileOrientation orientation;
 

+ 13 - 17
Source/Core/DecoratorTiledBox.cpp

@@ -33,12 +33,7 @@
 namespace Rml {
 
 struct DecoratorTiledBoxData {
-	DecoratorTiledBoxData(Element* host_element, int num_textures) : num_textures(num_textures)
-	{
-		geometry = new Geometry[num_textures];
-		for (int i = 0; i < num_textures; i++)
-			geometry[i].SetHostElement(host_element);
-	}
+	DecoratorTiledBoxData(int num_textures) : num_textures(num_textures) { geometry = new Geometry[num_textures]; }
 
 	~DecoratorTiledBoxData() { delete[] geometry; }
 
@@ -106,7 +101,7 @@ DecoratorDataHandle DecoratorTiledBox::GenerateElementData(Element* element) con
 	for (int i = 0; i < 9; i++)
 	{
 		RMLUI_ASSERT(tiles[i].texture_index >= 0);
-		tiles[i].CalculateDimensions(element, *GetTexture(tiles[i].texture_index));
+		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
 	}
 
 	Vector2f padded_size = element->GetBox().GetSize(Box::PADDING);
@@ -196,40 +191,41 @@ DecoratorDataHandle DecoratorTiledBox::GenerateElementData(Element* element) con
 	}
 
 	const int num_textures = GetNumTextures();
-	DecoratorTiledBoxData* data = new DecoratorTiledBoxData(element, num_textures);
+	DecoratorTiledBoxData* data = new DecoratorTiledBoxData(num_textures);
+	const ComputedValues& computed = element->GetComputedValues();
 
 	// Generate the geometry for the top-left tile.
 	tiles[TOP_LEFT_CORNER].GenerateGeometry(data->geometry[tiles[TOP_LEFT_CORNER].texture_index].GetVertices(),
-		data->geometry[tiles[TOP_LEFT_CORNER].texture_index].GetIndices(), element, Vector2f(0, 0), top_left, top_left);
+		data->geometry[tiles[TOP_LEFT_CORNER].texture_index].GetIndices(), computed, Vector2f(0, 0), top_left, top_left);
 	// Generate the geometry for the top edge tiles.
 	tiles[TOP_EDGE].GenerateGeometry(data->geometry[tiles[TOP_EDGE].texture_index].GetVertices(),
-		data->geometry[tiles[TOP_EDGE].texture_index].GetIndices(), element, Vector2f(top_left.x, 0),
+		data->geometry[tiles[TOP_EDGE].texture_index].GetIndices(), computed, Vector2f(top_left.x, 0),
 		Vector2f(padded_size.x - (top_left.x + top_right.x), top.y), top);
 	// Generate the geometry for the top-right tile.
 	tiles[TOP_RIGHT_CORNER].GenerateGeometry(data->geometry[tiles[TOP_RIGHT_CORNER].texture_index].GetVertices(),
-		data->geometry[tiles[TOP_RIGHT_CORNER].texture_index].GetIndices(), element, Vector2f(padded_size.x - top_right.x, 0), top_right, top_right);
+		data->geometry[tiles[TOP_RIGHT_CORNER].texture_index].GetIndices(), computed, Vector2f(padded_size.x - top_right.x, 0), top_right, top_right);
 
 	// Generate the geometry for the left side.
 	tiles[LEFT_EDGE].GenerateGeometry(data->geometry[tiles[LEFT_EDGE].texture_index].GetVertices(),
-		data->geometry[tiles[LEFT_EDGE].texture_index].GetIndices(), element, Vector2f(0, top_left.y),
+		data->geometry[tiles[LEFT_EDGE].texture_index].GetIndices(), computed, Vector2f(0, top_left.y),
 		Vector2f(left.x, padded_size.y - (top_left.y + bottom_left.y)), left);
 
 	// Generate the geometry for the right side.
 	tiles[RIGHT_EDGE].GenerateGeometry(data->geometry[tiles[RIGHT_EDGE].texture_index].GetVertices(),
-		data->geometry[tiles[RIGHT_EDGE].texture_index].GetIndices(), element, Vector2f((padded_size.x - right.x), top_right.y),
+		data->geometry[tiles[RIGHT_EDGE].texture_index].GetIndices(), computed, Vector2f((padded_size.x - right.x), top_right.y),
 		Vector2f(right.x, padded_size.y - (top_right.y + bottom_right.y)), right);
 
 	// Generate the geometry for the bottom-left tile.
 	tiles[BOTTOM_LEFT_CORNER].GenerateGeometry(data->geometry[tiles[BOTTOM_LEFT_CORNER].texture_index].GetVertices(),
-		data->geometry[tiles[BOTTOM_LEFT_CORNER].texture_index].GetIndices(), element, Vector2f(0, padded_size.y - bottom_left.y), bottom_left,
+		data->geometry[tiles[BOTTOM_LEFT_CORNER].texture_index].GetIndices(), computed, Vector2f(0, padded_size.y - bottom_left.y), bottom_left,
 		bottom_left);
 	// Generate the geometry for the bottom edge tiles.
 	tiles[BOTTOM_EDGE].GenerateGeometry(data->geometry[tiles[BOTTOM_EDGE].texture_index].GetVertices(),
-		data->geometry[tiles[BOTTOM_EDGE].texture_index].GetIndices(), element, Vector2f(bottom_left.x, padded_size.y - bottom.y),
+		data->geometry[tiles[BOTTOM_EDGE].texture_index].GetIndices(), computed, Vector2f(bottom_left.x, padded_size.y - bottom.y),
 		Vector2f(padded_size.x - (bottom_left.x + bottom_right.x), bottom.y), bottom);
 	// Generate the geometry for the bottom-right tile.
 	tiles[BOTTOM_RIGHT_CORNER].GenerateGeometry(data->geometry[tiles[BOTTOM_RIGHT_CORNER].texture_index].GetVertices(),
-		data->geometry[tiles[BOTTOM_RIGHT_CORNER].texture_index].GetIndices(), element,
+		data->geometry[tiles[BOTTOM_RIGHT_CORNER].texture_index].GetIndices(), computed,
 		Vector2f(padded_size.x - bottom_right.x, padded_size.y - bottom_right.y), bottom_right, bottom_right);
 
 	// Generate the centre geometry.
@@ -237,7 +233,7 @@ DecoratorDataHandle DecoratorTiledBox::GenerateElementData(Element* element) con
 	Vector2f centre_surface_dimensions(padded_size.x - (left.x + right.x), padded_size.y - (top.y + bottom.y));
 
 	tiles[CENTRE].GenerateGeometry(data->geometry[tiles[CENTRE].texture_index].GetVertices(),
-		data->geometry[tiles[CENTRE].texture_index].GetIndices(), element, Vector2f(left.x, top.y), centre_surface_dimensions, centre_dimensions);
+		data->geometry[tiles[CENTRE].texture_index].GetIndices(), computed, Vector2f(left.x, top.y), centre_surface_dimensions, centre_dimensions);
 
 	// Set the textures on the geometry.
 	const Texture* texture = nullptr;

+ 8 - 11
Source/Core/DecoratorTiledHorizontal.cpp

@@ -34,12 +34,7 @@
 namespace Rml {
 
 struct DecoratorTiledHorizontalData {
-	DecoratorTiledHorizontalData(Element* host_element, int num_textures) : num_textures(num_textures)
-	{
-		geometry = new Geometry[num_textures];
-		for (int i = 0; i < num_textures; i++)
-			geometry[i].SetHostElement(host_element);
-	}
+	DecoratorTiledHorizontalData(int num_textures) : num_textures(num_textures) { geometry = new Geometry[num_textures]; }
 
 	~DecoratorTiledHorizontalData() { delete[] geometry; }
 
@@ -84,10 +79,10 @@ DecoratorDataHandle DecoratorTiledHorizontal::GenerateElementData(Element* eleme
 {
 	// Initialise the tiles for this element.
 	for (int i = 0; i < 3; i++)
-		tiles[i].CalculateDimensions(element, *(GetTexture(tiles[i].texture_index)));
+		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
 
 	const int num_textures = GetNumTextures();
-	DecoratorTiledHorizontalData* data = new DecoratorTiledHorizontalData(element, num_textures);
+	DecoratorTiledHorizontalData* data = new DecoratorTiledHorizontalData(num_textures);
 
 	Vector2f padded_size = element->GetBox().GetSize(Box::PADDING);
 
@@ -112,16 +107,18 @@ DecoratorDataHandle DecoratorTiledHorizontal::GenerateElementData(Element* eleme
 		right_dimensions.x = padded_size.x * (right_dimensions.x / minimum_width);
 	}
 
+	const ComputedValues& computed = element->GetComputedValues();
+
 	// Generate the geometry for the left tile.
 	tiles[LEFT].GenerateGeometry(data->geometry[tiles[LEFT].texture_index].GetVertices(), data->geometry[tiles[LEFT].texture_index].GetIndices(),
-		element, Vector2f(0, 0), left_dimensions, left_dimensions);
+		computed, Vector2f(0, 0), left_dimensions, left_dimensions);
 	// Generate the geometry for the centre tiles.
 	tiles[CENTRE].GenerateGeometry(data->geometry[tiles[CENTRE].texture_index].GetVertices(),
-		data->geometry[tiles[CENTRE].texture_index].GetIndices(), element, Vector2f(left_dimensions.x, 0),
+		data->geometry[tiles[CENTRE].texture_index].GetIndices(), computed, Vector2f(left_dimensions.x, 0),
 		Vector2f(padded_size.x - (left_dimensions.x + right_dimensions.x), centre_dimensions.y), centre_dimensions);
 	// Generate the geometry for the right tile.
 	tiles[RIGHT].GenerateGeometry(data->geometry[tiles[RIGHT].texture_index].GetVertices(), data->geometry[tiles[RIGHT].texture_index].GetIndices(),
-		element, Vector2f(padded_size.x - right_dimensions.x, 0), right_dimensions, right_dimensions);
+		computed, Vector2f(padded_size.x - right_dimensions.x, 0), right_dimensions, right_dimensions);
 
 	// Set the textures on the geometry.
 	const Texture* texture = nullptr;

+ 5 - 3
Source/Core/DecoratorTiledImage.cpp

@@ -47,13 +47,15 @@ bool DecoratorTiledImage::Initialise(const Tile& _tile, const Texture& _texture)
 DecoratorDataHandle DecoratorTiledImage::GenerateElementData(Element* element) const
 {
 	// Calculate the tile's dimensions for this element.
-	tile.CalculateDimensions(element, *GetTexture(tile.texture_index));
+	tile.CalculateDimensions(*GetTexture(tile.texture_index));
 
-	Geometry* data = new Geometry(element);
+	Geometry* data = new Geometry();
 	data->SetTexture(GetTexture());
 
+	const ComputedValues& computed = element->GetComputedValues();
+
 	// Generate the geometry for the tile.
-	tile.GenerateGeometry(data->GetVertices(), data->GetIndices(), element, Vector2f(0, 0), element->GetBox().GetSize(Box::PADDING),
+	tile.GenerateGeometry(data->GetVertices(), data->GetIndices(), computed, Vector2f(0, 0), element->GetBox().GetSize(Box::PADDING),
 		tile.GetNaturalDimensions(element));
 
 	return reinterpret_cast<DecoratorDataHandle>(data);

+ 8 - 11
Source/Core/DecoratorTiledVertical.cpp

@@ -35,12 +35,7 @@
 namespace Rml {
 
 struct DecoratorTiledVerticalData {
-	DecoratorTiledVerticalData(Element* host_element, int num_textures) : num_textures(num_textures)
-	{
-		geometry = new Geometry[num_textures];
-		for (int i = 0; i < num_textures; i++)
-			geometry[i].SetHostElement(host_element);
-	}
+	DecoratorTiledVerticalData(int num_textures) : num_textures(num_textures) { geometry = new Geometry[num_textures]; }
 
 	~DecoratorTiledVerticalData() { delete[] geometry; }
 
@@ -85,10 +80,10 @@ DecoratorDataHandle DecoratorTiledVertical::GenerateElementData(Element* element
 {
 	// Initialise the tile for this element.
 	for (int i = 0; i < 3; i++)
-		tiles[i].CalculateDimensions(element, *GetTexture(tiles[i].texture_index));
+		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
 
 	const int num_textures = GetNumTextures();
-	DecoratorTiledVerticalData* data = new DecoratorTiledVerticalData(element, num_textures);
+	DecoratorTiledVerticalData* data = new DecoratorTiledVerticalData(num_textures);
 
 	Vector2f padded_size = element->GetBox().GetSize(Box::PADDING);
 
@@ -113,16 +108,18 @@ DecoratorDataHandle DecoratorTiledVertical::GenerateElementData(Element* element
 		bottom_dimensions.y = padded_size.y * (bottom_dimensions.y / minimum_height);
 	}
 
+	const ComputedValues& computed = element->GetComputedValues();
+
 	// Generate the geometry for the left tile.
 	tiles[TOP].GenerateGeometry(data->geometry[tiles[TOP].texture_index].GetVertices(), data->geometry[tiles[TOP].texture_index].GetIndices(),
-		element, Vector2f(0, 0), top_dimensions, top_dimensions);
+		computed, Vector2f(0, 0), top_dimensions, top_dimensions);
 	// Generate the geometry for the centre tiles.
 	tiles[CENTRE].GenerateGeometry(data->geometry[tiles[CENTRE].texture_index].GetVertices(),
-		data->geometry[tiles[CENTRE].texture_index].GetIndices(), element, Vector2f(0, top_dimensions.y),
+		data->geometry[tiles[CENTRE].texture_index].GetIndices(), computed, Vector2f(0, top_dimensions.y),
 		Vector2f(centre_dimensions.x, padded_size.y - (top_dimensions.y + bottom_dimensions.y)), centre_dimensions);
 	// Generate the geometry for the right tile.
 	tiles[BOTTOM].GenerateGeometry(data->geometry[tiles[BOTTOM].texture_index].GetVertices(),
-		data->geometry[tiles[BOTTOM].texture_index].GetIndices(), element, Vector2f(0, padded_size.y - bottom_dimensions.y), bottom_dimensions,
+		data->geometry[tiles[BOTTOM].texture_index].GetIndices(), computed, Vector2f(0, padded_size.y - bottom_dimensions.y), bottom_dimensions,
 		bottom_dimensions);
 
 	// Set the textures on the geometry.

+ 1 - 9
Source/Core/Element.cpp

@@ -92,7 +92,7 @@ static float GetScrollOffsetDelta(ScrollAlignment alignment, float begin_offset,
 
 // Meta objects for element collected in a single struct to reduce memory allocations
 struct ElementMeta {
-	ElementMeta(Element* el) : event_dispatcher(el), style(el), background_border(el), decoration(el), scroll(el), computed_values(el) {}
+	ElementMeta(Element* el) : event_dispatcher(el), style(el), background_border(), decoration(el), scroll(el), computed_values(el) {}
 	SmallUnorderedMap<EventId, EventListener*> attribute_event_listeners;
 	EventDispatcher event_dispatcher;
 	ElementStyle style;
@@ -1547,14 +1547,6 @@ DataModel* Element::GetDataModel() const
 	return data_model;
 }
 
-RenderInterface* Element::GetRenderInterface()
-{
-	if (Context* context = GetContext())
-		return context->GetRenderInterface();
-
-	return ::Rml::GetRenderInterface();
-}
-
 void Element::SetInstancer(ElementInstancer* _instancer)
 {
 	// Only record the first instancer being set as some instancers call other instancers to do their dirty work, in

+ 1 - 1
Source/Core/ElementBackgroundBorder.cpp

@@ -34,7 +34,7 @@
 
 namespace Rml {
 
-ElementBackgroundBorder::ElementBackgroundBorder(Element* element) : geometry(element) {}
+ElementBackgroundBorder::ElementBackgroundBorder() {}
 
 void ElementBackgroundBorder::Render(Element* element)
 {

+ 1 - 1
Source/Core/ElementBackgroundBorder.h

@@ -36,7 +36,7 @@ namespace Rml {
 
 class ElementBackgroundBorder {
 public:
-	ElementBackgroundBorder(Element* element);
+	ElementBackgroundBorder();
 
 	void Render(Element* element);
 

+ 1 - 3
Source/Core/ElementText.cpp

@@ -125,7 +125,7 @@ void ElementText::OnRender()
 			if (decoration)
 				decoration->Release(true);
 			else
-				decoration = MakeUnique<Geometry>(this);
+				decoration = MakeUnique<Geometry>();
 
 			GenerateDecoration(font_face_handle);
 		}
@@ -464,8 +464,6 @@ void ElementText::GenerateGeometry(const FontFaceHandle font_face_handle, Line&
 
 	line.width = GetFontEngineInterface()->GenerateString(font_face_handle, font_effects_handle, line.text, line.position, colour, opacity,
 		letter_spacing, geometry);
-	for (size_t i = 0; i < geometry.size(); ++i)
-		geometry[i].SetHostElement(this);
 }
 
 void ElementText::GenerateDecoration(const FontFaceHandle font_face_handle)

+ 18 - 41
Source/Core/ElementUtilities.cpp

@@ -240,21 +240,10 @@ bool ElementUtilities::GetClippingRegion(Vector2i& clip_origin, Vector2i& clip_d
 
 bool ElementUtilities::SetClippingRegion(Element* element, Context* context)
 {
-	RenderInterface* render_interface = nullptr;
-	if (element)
-	{
-		render_interface = element->GetRenderInterface();
-		if (!context)
-			context = element->GetContext();
-	}
-	else if (context)
-	{
-		render_interface = context->GetRenderInterface();
-		if (!render_interface)
-			render_interface = GetRenderInterface();
-	}
+	if (element && !context)
+		context = element->GetContext();
 
-	if (!render_interface || !context)
+	if (!context)
 		return false;
 
 	Vector2i clip_origin = {-1, -1};
@@ -267,15 +256,16 @@ bool ElementUtilities::SetClippingRegion(Element* element, Context* context)
 	if (current_clip != clip || (clip && (clip_origin != current_origin || clip_dimensions != current_dimensions)))
 	{
 		context->SetActiveClipRegion(clip_origin, clip_dimensions);
-		ApplyActiveClipRegion(context, render_interface);
+		ApplyActiveClipRegion(context);
 	}
 
 	return true;
 }
 
-void ElementUtilities::ApplyActiveClipRegion(Context* context, RenderInterface* render_interface)
+void ElementUtilities::ApplyActiveClipRegion(Context* context)
 {
-	if (render_interface == nullptr)
+	RenderInterface* render_interface = ::Rml::GetRenderInterface();
+	if (!render_interface)
 		return;
 
 	Vector2i origin;
@@ -325,43 +315,30 @@ bool ElementUtilities::PositionElement(Element* element, Vector2f offset, Positi
 
 bool ElementUtilities::ApplyTransform(Element& element)
 {
-	RenderInterface* render_interface = element.GetRenderInterface();
+	RenderInterface* render_interface = ::Rml::GetRenderInterface();
 	if (!render_interface)
 		return false;
 
-	struct PreviousMatrix {
-		const Matrix4f* pointer; // This may be expired, dereferencing not allowed!
-		Matrix4f value;
-	};
-	static SmallUnorderedMap<RenderInterface*, PreviousMatrix> previous_matrix;
-
-	auto it = previous_matrix.find(render_interface);
-	if (it == previous_matrix.end())
-		it = previous_matrix.emplace(render_interface, PreviousMatrix{nullptr, Matrix4f::Identity()}).first;
-
-	RMLUI_ASSERT(it != previous_matrix.end());
-
-	const Matrix4f*& old_transform = it->second.pointer;
-	const Matrix4f* new_transform = nullptr;
+	static const Matrix4f* old_transform_ptr = {}; // This may be expired, dereferencing not allowed!
+	static Matrix4f old_transform_value = Matrix4f::Identity();
 
+	const Matrix4f* new_transform_ptr = nullptr;
 	if (const TransformState* state = element.GetTransformState())
-		new_transform = state->GetTransform();
+		new_transform_ptr = state->GetTransform();
 
 	// Only changed transforms are submitted.
-	if (old_transform != new_transform)
+	if (old_transform_ptr != new_transform_ptr)
 	{
-		Matrix4f& old_transform_value = it->second.value;
-
 		// Do a deep comparison as well to avoid submitting a new transform which is equal.
-		if (!old_transform || !new_transform || (old_transform_value != *new_transform))
+		if (!old_transform_ptr || !new_transform_ptr || (old_transform_value != *new_transform_ptr))
 		{
-			render_interface->SetTransform(new_transform);
+			render_interface->SetTransform(new_transform_ptr);
 
-			if (new_transform)
-				old_transform_value = *new_transform;
+			if (new_transform_ptr)
+				old_transform_value = *new_transform_ptr;
 		}
 
-		old_transform = new_transform;
+		old_transform_ptr = new_transform_ptr;
 	}
 
 	return true;

+ 4 - 4
Source/Core/Elements/ElementImage.cpp

@@ -38,7 +38,7 @@
 
 namespace Rml {
 
-ElementImage::ElementImage(const String& tag) : Element(tag), dimensions(-1, -1), rect_source(RectSource::None), geometry(this)
+ElementImage::ElementImage(const String& tag) : Element(tag), dimensions(-1, -1), rect_source(RectSource::None)
 {
 	dimensions_scale = 1.0f;
 	geometry_dirty = false;
@@ -57,7 +57,7 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 	if (HasAttribute("width"))
 		dimensions.x = GetAttribute<float>("width", -1);
 	else if (rect_source == RectSource::None)
-		dimensions.x = (float)texture.GetDimensions(GetRenderInterface()).x;
+		dimensions.x = (float)texture.GetDimensions().x;
 	else
 		dimensions.x = rect.Width();
 
@@ -65,7 +65,7 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 	if (HasAttribute("height"))
 		dimensions.y = GetAttribute<float>("height", -1);
 	else if (rect_source == RectSource::None)
-		dimensions.y = (float)texture.GetDimensions(GetRenderInterface()).y;
+		dimensions.y = (float)texture.GetDimensions().y;
 	else
 		dimensions.y = rect.Height();
 
@@ -181,7 +181,7 @@ void ElementImage::GenerateGeometry()
 	Vector2f texcoords[2];
 	if (rect_source != RectSource::None)
 	{
-		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(GetRenderInterface()), Vector2i(1)));
+		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(), Vector2i(1)));
 		texcoords[0] = rect.TopLeft() / texture_dimensions;
 		texcoords[1] = rect.BottomRight() / texture_dimensions;
 	}

+ 2 - 2
Source/Core/Elements/ElementProgress.cpp

@@ -41,7 +41,7 @@
 namespace Rml {
 
 ElementProgress::ElementProgress(const String& tag) :
-	Element(tag), direction(DefaultDirection), start_edge(DefaultStartEdge), fill(nullptr), rect_set(false), geometry(this)
+	Element(tag), direction(DefaultDirection), start_edge(DefaultStartEdge), fill(nullptr), rect_set(false)
 {
 	if (tag == "progressbar")
 		Log::Message(Log::LT_WARNING, "Deprecation notice: Element '<progressbar>' renamed to '<progress>', please adjust RML tags accordingly.");
@@ -233,7 +233,7 @@ void ElementProgress::GenerateGeometry()
 	Vector2f texcoords[2];
 	if (rect_set)
 	{
-		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(GetRenderInterface()), Vector2i(1)));
+		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(), Vector2i(1)));
 		texcoords[0] = rect.TopLeft() / texture_dimensions;
 		texcoords[1] = rect.BottomRight() / texture_dimensions;
 	}

+ 1 - 1
Source/Core/Elements/WidgetTextInput.cpp

@@ -105,7 +105,7 @@ static bool ClampValue(String& value, int max_length)
 }
 
 WidgetTextInput::WidgetTextInput(ElementFormControl* _parent) :
-	internal_dimensions(0, 0), scroll_offset(0, 0), selection_geometry(_parent), cursor_position(0, 0), cursor_size(0, 0), cursor_geometry(_parent)
+	internal_dimensions(0, 0), scroll_offset(0, 0), cursor_position(0, 0), cursor_size(0, 0)
 {
 	keyboard_showed = false;
 

+ 7 - 44
Source/Core/Geometry.cpp

@@ -37,12 +37,7 @@
 
 namespace Rml {
 
-Geometry::Geometry(Element* host_element) : host_element(host_element)
-{
-	database_handle = GeometryDatabase::Insert(this);
-}
-
-Geometry::Geometry(Context* host_context) : host_context(host_context)
+Geometry::Geometry()
 {
 	database_handle = GeometryDatabase::Insert(this);
 }
@@ -62,9 +57,6 @@ Geometry& Geometry::operator=(Geometry&& other) noexcept
 
 void Geometry::MoveFrom(Geometry& other) noexcept
 {
-	host_context = std::exchange(other.host_context, nullptr);
-	host_element = std::exchange(other.host_element, nullptr);
-
 	vertices = std::move(other.vertices);
 	indices = std::move(other.indices);
 
@@ -81,25 +73,10 @@ Geometry::~Geometry()
 	Release();
 }
 
-void Geometry::SetHostElement(Element* _host_element)
-{
-	if (host_element == _host_element)
-		return;
-
-	if (host_element != nullptr)
-	{
-		Release();
-		host_context = nullptr;
-	}
-
-	host_element = _host_element;
-}
-
 void Geometry::Render(Vector2f translation)
 {
-	RenderInterface* const render_interface = GetRenderInterface();
-	if (!render_interface)
-		return;
+	RenderInterface* const render_interface = ::Rml::GetRenderInterface();
+	RMLUI_ASSERT(render_interface);
 
 	translation = translation.Round();
 
@@ -122,7 +99,7 @@ void Geometry::Render(Vector2f translation)
 		{
 			compile_attempted = true;
 			compiled_geometry = render_interface->CompileGeometry(&vertices[0], (int)vertices.size(), &indices[0], (int)indices.size(),
-				texture ? texture->GetHandle(render_interface) : 0);
+				texture ? texture->GetHandle() : 0);
 
 			// If we managed to compile the geometry, we can clear the local copy of vertices and indices and
 			// immediately render the compiled version.
@@ -135,8 +112,8 @@ void Geometry::Render(Vector2f translation)
 
 		// Either we've attempted to compile before (and failed), or the compile we just attempted failed; either way,
 		// render the uncompiled version.
-		render_interface->RenderGeometry(&vertices[0], (int)vertices.size(), &indices[0], (int)indices.size(),
-			texture ? texture->GetHandle(GetRenderInterface()) : 0, translation);
+		render_interface->RenderGeometry(&vertices[0], (int)vertices.size(), &indices[0], (int)indices.size(), texture ? texture->GetHandle() : 0,
+			translation);
 	}
 }
 
@@ -165,7 +142,7 @@ void Geometry::Release(bool clear_buffers)
 {
 	if (compiled_geometry)
 	{
-		GetRenderInterface()->ReleaseCompiledGeometry(compiled_geometry);
+		::Rml::GetRenderInterface()->ReleaseCompiledGeometry(compiled_geometry);
 		compiled_geometry = 0;
 	}
 
@@ -183,18 +160,4 @@ Geometry::operator bool() const
 	return !indices.empty();
 }
 
-RenderInterface* Geometry::GetRenderInterface()
-{
-	if (!host_context)
-	{
-		if (host_element)
-			host_context = host_element->GetContext();
-	}
-
-	if (host_context)
-		return host_context->GetRenderInterface();
-	else
-		return ::Rml::GetRenderInterface();
-}
-
 } // namespace Rml

+ 4 - 13
Source/Core/RenderInterface.cpp

@@ -31,19 +31,15 @@
 
 namespace Rml {
 
-RenderInterface::RenderInterface()
-{
-	context = nullptr;
-}
+RenderInterface::RenderInterface() {}
 
 RenderInterface::~RenderInterface()
 {
 	// Note: We cannot automatically release the textures from the database here, because that involves a virtual call to this interface during its
 	// destruction which is illegal.
-	RMLUI_ASSERTMSG(!TextureDatabase::HoldsReferenceToRenderInterface(this),
-		"RenderInterface is being destroyed, but there are still active textures referencing it in the texture database. Ensure either that (1) the "
-		"render interface is destroyed *after* the call to Rml::Shutdown, or that (2) all the contexts the render interface belongs to has been "
-		"destroyed and a subsequent call has been made to Rml::ReleaseTextures before the render interface is destroyed.");
+	RMLUI_ASSERTMSG(TextureDatabase::AllTexturesReleased(),
+		"RenderInterface is being destroyed, but there are still active textures in the texture database. This may lead to use-after-free or nullptr "
+		"dereference when releasing the textures. Ensure that the render interface is destroyed *after* the call to Rml::Shutdown.");
 }
 
 CompiledGeometryHandle RenderInterface::CompileGeometry(Vertex* /*vertices*/, int /*num_vertices*/, int* /*indices*/, int /*num_indices*/,
@@ -70,9 +66,4 @@ void RenderInterface::ReleaseTexture(TextureHandle /*texture*/) {}
 
 void RenderInterface::SetTransform(const Matrix4f* /*transform*/) {}
 
-Context* RenderInterface::GetContext() const
-{
-	return context;
-}
-
 } // namespace Rml

+ 5 - 5
Source/Core/Texture.cpp

@@ -52,20 +52,20 @@ const String& Texture::GetSource() const
 	return resource->GetSource();
 }
 
-TextureHandle Texture::GetHandle(RenderInterface* render_interface) const
+TextureHandle Texture::GetHandle() const
 {
 	if (!resource)
 		return 0;
 
-	return resource->GetHandle(render_interface);
+	return resource->GetHandle();
 }
 
-Vector2i Texture::GetDimensions(RenderInterface* render_interface) const
+Vector2i Texture::GetDimensions() const
 {
 	if (!resource)
-		return Vector2i(0, 0);
+		return {};
 
-	return resource->GetDimensions(render_interface);
+	return resource->GetDimensions();
 }
 
 bool Texture::operator==(const Texture& other) const

+ 9 - 9
Source/Core/TextureDatabase.cpp

@@ -122,32 +122,32 @@ StringList TextureDatabase::GetSourceList()
 	return result;
 }
 
-void TextureDatabase::ReleaseTextures(RenderInterface* render_interface)
+void TextureDatabase::ReleaseTextures()
 {
 	if (texture_database)
 	{
 		for (const auto& texture : texture_database->textures)
-			texture.second->Release(render_interface);
+			texture.second->Release();
 
 		for (const auto& texture : texture_database->callback_textures)
-			texture->Release(render_interface);
+			texture->Release();
 	}
 }
 
-bool TextureDatabase::HoldsReferenceToRenderInterface(RenderInterface* render_interface)
+bool TextureDatabase::AllTexturesReleased()
 {
 	if (texture_database)
 	{
 		for (const auto& texture : texture_database->textures)
-			if (texture.second->HoldsRenderInterface(render_interface))
-				return true;
+			if (!texture.second->IsLoaded())
+				return false;
 
 		for (const auto& texture : texture_database->callback_textures)
-			if (texture->HoldsRenderInterface(render_interface))
-				return true;
+			if (!texture->IsLoaded())
+				return false;
 	}
 
-	return false;
+	return true;
 }
 
 } // namespace Rml

+ 4 - 6
Source/Core/TextureDatabase.h

@@ -33,7 +33,6 @@
 
 namespace Rml {
 
-class RenderInterface;
 class TextureResource;
 
 /**
@@ -50,9 +49,8 @@ public:
 	/// entry will be added and returned.
 	static SharedPtr<TextureResource> Fetch(const String& source, const String& source_directory);
 
-	/// Release all textures bound through a render interface.
-	/// Pass nullptr to release all textures in the database.
-	static void ReleaseTextures(RenderInterface* render_interface = nullptr);
+	/// Release all textures in the database.
+	static void ReleaseTextures();
 
 	/// Adds a texture resource with a callback function and stores it as a weak (raw) pointer in the database.
 	static void AddCallbackTexture(TextureResource* texture);
@@ -63,8 +61,8 @@ public:
 	/// Return a list of all texture sources currently in the database.
 	static StringList GetSourceList();
 
-	/// For debugging. Returns true if any textures hold a reference to the given render interface.
-	static bool HoldsReferenceToRenderInterface(RenderInterface* render_interface);
+	/// Returns true if there are no textures in the database yet to be released through the render interface.
+	static bool AllTexturesReleased();
 
 private:
 	TextureDatabase();

+ 29 - 49
Source/Core/TextureResource.cpp

@@ -27,6 +27,7 @@
  */
 
 #include "TextureResource.h"
+#include "../../Include/RmlUi/Core/Core.h"
 #include "../../Include/RmlUi/Core/Log.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
 #include "../../Include/RmlUi/Core/RenderInterface.h"
@@ -68,28 +69,18 @@ void TextureResource::Reset()
 	source.clear();
 }
 
-TextureHandle TextureResource::GetHandle(RenderInterface* render_interface)
+TextureHandle TextureResource::GetHandle()
 {
-	auto texture_iterator = texture_data.find(render_interface);
-	if (texture_iterator == texture_data.end())
-	{
-		Load(render_interface);
-		texture_iterator = texture_data.find(render_interface);
-	}
-
-	return texture_iterator->second.first;
+	if (!loaded)
+		Load();
+	return handle;
 }
 
-Vector2i TextureResource::GetDimensions(RenderInterface* render_interface)
+Vector2i TextureResource::GetDimensions()
 {
-	auto texture_iterator = texture_data.find(render_interface);
-	if (texture_iterator == texture_data.end())
-	{
-		Load(render_interface);
-		texture_iterator = texture_data.find(render_interface);
-	}
-
-	return texture_iterator->second.second;
+	if (!loaded)
+		Load();
+	return dimensions;
 }
 
 const String& TextureResource::GetSource() const
@@ -97,67 +88,56 @@ const String& TextureResource::GetSource() const
 	return source;
 }
 
-void TextureResource::Release(RenderInterface* render_interface)
+void TextureResource::Release()
 {
-	if (!render_interface)
+	if (loaded)
 	{
-		for (auto& interface_data_pair : texture_data)
-		{
-			TextureHandle handle = interface_data_pair.second.first;
-			if (handle)
-				interface_data_pair.first->ReleaseTexture(handle);
-		}
+		RenderInterface* render_interface = ::Rml::GetRenderInterface();
+		RMLUI_ASSERT(render_interface);
+		render_interface->ReleaseTexture(handle);
 
-		texture_data.clear();
+		handle = {};
+		dimensions = {};
+		loaded = false;
 	}
-	else
-	{
-		auto texture_iterator = texture_data.find(render_interface);
-		if (texture_iterator == texture_data.end())
-			return;
-
-		TextureHandle handle = texture_iterator->second.first;
-		if (handle)
-			texture_iterator->first->ReleaseTexture(handle);
+}
 
-		texture_data.erase(render_interface);
-	}
+bool TextureResource::IsLoaded() const
+{
+	return loaded;
 }
 
-bool TextureResource::Load(RenderInterface* render_interface)
+bool TextureResource::Load()
 {
 	RMLUI_ZoneScoped;
+	RenderInterface* render_interface = ::Rml::GetRenderInterface();
+
+	loaded = true;
 
 	// Generate the texture from the callback function if we have one.
 	if (texture_callback)
 	{
 		TextureCallback& callback_fnc = *texture_callback;
-		TextureHandle handle = {};
-		Vector2i dimensions;
-
 		if (!callback_fnc(render_interface, source, handle, dimensions) || !handle)
 		{
 			Log::Message(Log::LT_WARNING, "Failed to generate texture from callback function %s.", source.c_str());
-			texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
+			handle = {};
+			dimensions = {};
 			return false;
 		}
 
-		texture_data[render_interface] = TextureData(handle, dimensions);
 		return true;
 	}
 
 	// No callback function, load the texture through the render interface.
-	TextureHandle handle;
-	Vector2i dimensions;
 	if (!render_interface->LoadTexture(handle, dimensions, source))
 	{
 		Log::Message(Log::LT_WARNING, "Failed to load texture from %s.", source.c_str());
-		texture_data[render_interface] = TextureData(0, Vector2i(0, 0));
-
+		handle = {};
+		dimensions = {};
 		return false;
 	}
 
-	texture_data[render_interface] = TextureData(handle, dimensions);
 	return true;
 }
 

+ 9 - 9
Source/Core/TextureResource.h

@@ -55,30 +55,30 @@ public:
 	void Set(const String& name, const TextureCallback& callback);
 
 	/// Returns the resource's underlying texture handle.
-	TextureHandle GetHandle(RenderInterface* render_interface);
+	TextureHandle GetHandle();
 	/// Returns the dimensions of the resource's texture.
-	Vector2i GetDimensions(RenderInterface* render_interface);
+	Vector2i GetDimensions();
 
 	/// Returns the resource's source.
 	const String& GetSource() const;
 
 	/// Releases the texture's handle.
-	void Release(RenderInterface* render_interface = nullptr);
+	void Release();
 
-	/// For debugging. Returns true if the texture holds a reference to the given render interface, otherwise false.
-	inline bool HoldsRenderInterface(RenderInterface* render_interface) const { return texture_data.count(render_interface); }
+	/// Returns true if the texture has been loaded through the render interface, and not yet released.
+	bool IsLoaded() const;
 
 private:
 	void Reset();
 
 	/// Attempts to load the texture from the source, or the callback function if set.
-	bool Load(RenderInterface* render_interface);
+	bool Load();
 
 	String source;
 
-	using TextureData = Pair<TextureHandle, Vector2i>;
-	using TextureDataMap = SmallUnorderedMap<RenderInterface*, TextureData>;
-	TextureDataMap texture_data;
+	TextureHandle handle = {};
+	Vector2i dimensions;
+	bool loaded = false;
 
 	UniquePtr<TextureCallback> texture_callback;
 };

+ 5 - 6
Source/Debugger/Geometry.cpp

@@ -28,6 +28,7 @@
 
 #include "Geometry.h"
 #include "../../Include/RmlUi/Core/Context.h"
+#include "../../Include/RmlUi/Core/Core.h"
 #include "../../Include/RmlUi/Core/GeometryUtilities.h"
 #include "../../Include/RmlUi/Core/RenderInterface.h"
 
@@ -45,11 +46,10 @@ void Geometry::SetContext(Context* _context)
 
 void Geometry::RenderOutline(const Vector2f origin, const Vector2f dimensions, const Colourb colour, float width)
 {
-	if (context == nullptr)
+	RenderInterface* render_interface = ::Rml::GetRenderInterface();
+	if (!context || !render_interface)
 		return;
 
-	RenderInterface* render_interface = context->GetRenderInterface();
-
 	Vertex vertices[4 * 4];
 	int indices[6 * 4];
 
@@ -63,11 +63,10 @@ void Geometry::RenderOutline(const Vector2f origin, const Vector2f dimensions, c
 
 void Geometry::RenderBox(const Vector2f origin, const Vector2f dimensions, const Colourb colour)
 {
-	if (context == nullptr)
+	RenderInterface* render_interface = ::Rml::GetRenderInterface();
+	if (!context || !render_interface)
 		return;
 
-	RenderInterface* render_interface = context->GetRenderInterface();
-
 	Vertex vertices[4];
 	int indices[6];
 

+ 1 - 1
Source/Lottie/ElementLottie.cpp

@@ -41,7 +41,7 @@
 
 namespace Rml {
 
-ElementLottie::ElementLottie(const String& tag) : Element(tag), geometry(this) {}
+ElementLottie::ElementLottie(const String& tag) : Element(tag) {}
 
 ElementLottie::~ElementLottie() {}
 

+ 1 - 1
Source/SVG/ElementSVG.cpp

@@ -42,7 +42,7 @@
 
 namespace Rml {
 
-ElementSVG::ElementSVG(const String& tag) : Element(tag), geometry(this) {}
+ElementSVG::ElementSVG(const String& tag) : Element(tag) {}
 
 ElementSVG::~ElementSVG() {}
 

+ 1 - 1
Tests/Source/VisualTests/main.cpp

@@ -127,7 +127,7 @@ int main(int argc, char** argv)
 
 		TestViewer viewer(context);
 
-		TestNavigator navigator(context->GetRenderInterface(), context, &viewer, std::move(test_suites), load_suite_index, load_case_index);
+		TestNavigator navigator(Backend::GetRenderInterface(), context, &viewer, std::move(test_suites), load_suite_index, load_case_index);
 
 		bool running = true;
 		while (running)