Jelajahi Sumber

Refactor Geometry and Texture, introduce unique render resources

Unique render resources
- Consist of a unique handle and a pointer to its render manger.
- Automatically cleans up its underlying resource when out of scope.
- Ensuring resources are always released in the correct render interface.
- Geometry, CompiledShader, CompiledFilter and CallbackTexture, are now unique render resources.
  - All of which are constructed through the render manager.

RenderManager
- Now the owner of all resources constructed through the render interface.
- Wraps all calls to the render interface, the interface should no longer be called directly.
- Added back ability to use multiple render interfaces for separate contexts.
  - Each render interface is wrapped by a unique render manager.

Geometry
- Geometry is now a unique render resource.
- Geometry is now constructed from a Mesh through the render manager. The mesh cannot be modified after it is submitted. However, it can be released to reuse its buffers, then resubmitted.
  - With this change, we can guarantee pointer stability of submitted vertex and index pointers, until the call to ReleaseCompiledGeometry.
- Move texture out of Geometry, instead, a texture can be provided during the call to render.

Mesh
- Simple data structure, contains indices and vertices defining the mesh.
- Meshes can be constructed directly or using MeshUtilities (previously GeometryUtilities).

Texture
- The render manager now owns and stores file textures.
  - File textures are released when the render manager is destroyed, during Rml::Shutdown.
  - Each render manager has its own texture database to lookup and reuse textures with the same path.
- `Texture` is now simply a non-owning view and can be freely copied.
  - The user is responsible for ensuring validity of the underlying resource's lifetime.
- `CallbackTexture` on the other hand is a unique render resource, automatically released when out of scope.
  - Can make a non-owning reference (Texture).
- Handlers are available for managing texture sources for unknown or multiple render managers, generating references (Texture) as needed.
   - `TextureSource` for file textures and `CallbackTextureSource` for callback textures.

StableVector
- New container for stable indices which remain valid after entries are erased.

Breaking changes
- New procedure for generating geometry and textures (see above for details).
- GeometryUtilities renamed to MeshUtilities.
Michael Ragazzon 2 tahun lalu
induk
melakukan
a452f26951
100 mengubah file dengan 1388 tambahan dan 840 penghapusan
  1. 11 11
      Backends/RmlUi_Renderer_GL3.cpp
  2. 11 6
      CMake/FileList.cmake
  3. 5 1
      Include/RmlUi/Core.h
  4. 117 0
      Include/RmlUi/Core/CallbackTexture.h
  5. 71 0
      Include/RmlUi/Core/CompiledFilterShader.h
  6. 9 9
      Include/RmlUi/Core/Context.h
  7. 3 1
      Include/RmlUi/Core/ContextInstancer.h
  8. 11 4
      Include/RmlUi/Core/Core.h
  9. 10 5
      Include/RmlUi/Core/Decorator.h
  10. 4 0
      Include/RmlUi/Core/Element.h
  11. 7 5
      Include/RmlUi/Core/ElementText.h
  12. 3 1
      Include/RmlUi/Core/Factory.h
  13. 2 8
      Include/RmlUi/Core/Filter.h
  14. 7 6
      Include/RmlUi/Core/FontEngineInterface.h
  15. 14 64
      Include/RmlUi/Core/Geometry.h
  16. 15 25
      Include/RmlUi/Core/Mesh.h
  17. 13 18
      Include/RmlUi/Core/MeshUtilities.h
  18. 0 3
      Include/RmlUi/Core/RenderInterface.h
  19. 61 11
      Include/RmlUi/Core/RenderManager.h
  20. 2 5
      Include/RmlUi/Core/Spritesheet.h
  21. 124 0
      Include/RmlUi/Core/StableVector.h
  22. 3 1
      Include/RmlUi/Core/StyleSheet.h
  23. 36 39
      Include/RmlUi/Core/Texture.h
  24. 7 1
      Include/RmlUi/Core/Types.h
  25. 87 0
      Include/RmlUi/Core/UniqueRenderResource.h
  26. 2 2
      Include/RmlUi/Lottie/ElementLottie.h
  27. 2 2
      Include/RmlUi/SVG/ElementSVG.h
  28. 15 18
      Samples/basic/bitmapfont/src/FontEngineBitmap.cpp
  29. 5 4
      Samples/basic/bitmapfont/src/FontEngineBitmap.h
  30. 4 3
      Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.cpp
  31. 4 3
      Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.h
  32. 9 8
      Samples/invaders/src/DecoratorDefender.cpp
  33. 2 4
      Samples/invaders/src/DecoratorStarfield.cpp
  34. 3 3
      Samples/invaders/src/Defender.cpp
  35. 1 1
      Samples/invaders/src/Defender.h
  36. 2 1
      Samples/invaders/src/ElementGame.cpp
  37. 6 7
      Samples/invaders/src/Game.cpp
  38. 1 1
      Samples/invaders/src/Game.h
  39. 3 3
      Samples/invaders/src/Invader.cpp
  40. 1 1
      Samples/invaders/src/Invader.h
  41. 2 2
      Samples/invaders/src/Shield.cpp
  42. 1 1
      Samples/invaders/src/Shield.h
  43. 16 30
      Samples/invaders/src/Sprite.cpp
  44. 4 2
      Samples/invaders/src/Sprite.h
  45. 9 8
      Samples/luainvaders/src/DecoratorDefender.cpp
  46. 2 4
      Samples/luainvaders/src/DecoratorStarfield.cpp
  47. 3 3
      Samples/luainvaders/src/Defender.cpp
  48. 1 1
      Samples/luainvaders/src/Defender.h
  49. 2 1
      Samples/luainvaders/src/ElementGame.cpp
  50. 6 7
      Samples/luainvaders/src/Game.cpp
  51. 1 1
      Samples/luainvaders/src/Game.h
  52. 3 3
      Samples/luainvaders/src/Invader.cpp
  53. 1 1
      Samples/luainvaders/src/Invader.h
  54. 2 2
      Samples/luainvaders/src/Shield.cpp
  55. 1 1
      Samples/luainvaders/src/Shield.h
  56. 16 30
      Samples/luainvaders/src/Sprite.cpp
  57. 3 2
      Samples/luainvaders/src/Sprite.h
  58. 108 0
      Source/Core/CallbackTexture.cpp
  59. 60 0
      Source/Core/CompiledFilterShader.cpp
  60. 6 7
      Source/Core/Context.cpp
  61. 2 2
      Source/Core/ContextInstancerDefault.cpp
  62. 1 4
      Source/Core/ContextInstancerDefault.h
  63. 66 28
      Source/Core/Core.cpp
  64. 13 10
      Source/Core/Decorator.cpp
  65. 38 37
      Source/Core/DecoratorGradient.cpp
  66. 11 12
      Source/Core/DecoratorNinePatch.cpp
  67. 1 1
      Source/Core/DecoratorNinePatch.h
  68. 13 12
      Source/Core/DecoratorShader.cpp
  69. 2 2
      Source/Core/DecoratorShader.h
  70. 7 17
      Source/Core/DecoratorTiled.cpp
  71. 5 4
      Source/Core/DecoratorTiled.h
  72. 22 29
      Source/Core/DecoratorTiledBox.cpp
  73. 11 10
      Source/Core/DecoratorTiledBox.h
  74. 17 19
      Source/Core/DecoratorTiledHorizontal.cpp
  75. 2 2
      Source/Core/DecoratorTiledHorizontal.h
  76. 9 8
      Source/Core/DecoratorTiledImage.cpp
  77. 1 1
      Source/Core/DecoratorTiledImage.h
  78. 17 20
      Source/Core/DecoratorTiledVertical.cpp
  79. 2 2
      Source/Core/DecoratorTiledVertical.h
  80. 7 0
      Source/Core/Element.cpp
  81. 30 18
      Source/Core/ElementBackgroundBorder.cpp
  82. 3 3
      Source/Core/ElementBackgroundBorder.h
  83. 35 51
      Source/Core/ElementDecoration.cpp
  84. 5 2
      Source/Core/ElementDecoration.h
  85. 37 31
      Source/Core/ElementText.cpp
  86. 1 2
      Source/Core/ElementUtilities.cpp
  87. 19 19
      Source/Core/Elements/ElementImage.cpp
  88. 16 14
      Source/Core/Elements/ElementProgress.cpp
  89. 10 21
      Source/Core/Elements/WidgetTextInput.cpp
  90. 2 2
      Source/Core/Factory.cpp
  91. 1 0
      Source/Core/Filter.cpp
  92. 5 10
      Source/Core/FilterBasic.cpp
  93. 1 3
      Source/Core/FilterBasic.h
  94. 4 9
      Source/Core/FilterBlur.cpp
  95. 1 3
      Source/Core/FilterBlur.h
  96. 5 9
      Source/Core/FilterDropShadow.cpp
  97. 1 3
      Source/Core/FilterDropShadow.h
  98. 3 3
      Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp
  99. 4 4
      Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h
  100. 18 22
      Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp

+ 11 - 11
Backends/RmlUi_Renderer_GL3.cpp

@@ -30,7 +30,8 @@
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/DecorationTypes.h>
 #include <RmlUi/Core/FileInterface.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/Geometry.h>
+#include <RmlUi/Core/MeshUtilities.h>
 #include <RmlUi/Core/Log.h>
 #include <RmlUi/Core/Platform.h>
 #include <RmlUi/Core/SystemInterface.h>
@@ -780,11 +781,10 @@ RenderInterface_GL3::RenderInterface_GL3()
 	if (Gfx::CreateShaders(*mut_program_data))
 	{
 		program_data = std::move(mut_program_data);
-
-		Rml::Vertex vertices[4];
-		int indices[6];
-		Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(-1), Rml::Vector2f(2), {});
-		fullscreen_quad_geometry = RenderInterface_GL3::CompileGeometry(vertices, 4, indices, 6);
+		Rml::Mesh mesh;
+		Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {});
+		fullscreen_quad_geometry =
+			RenderInterface_GL3::CompileGeometry(mesh.vertices.data(), (int)mesh.vertices.size(), mesh.indices.data(), (int)mesh.indices.size());
 	}
 }
 
@@ -1298,15 +1298,15 @@ void RenderInterface_GL3::DrawFullscreenQuad()
 
 void RenderInterface_GL3::DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling)
 {
-	Rml::Vertex vertices[4];
-	int indices[6];
-	Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(-1), Rml::Vector2f(2), {});
+	Rml::Mesh mesh;
+	Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {});
 	if (uv_offset != Rml::Vector2f() || uv_scaling != Rml::Vector2f(1.f))
 	{
-		for (Rml::Vertex& vertex : vertices)
+		for (Rml::Vertex& vertex : mesh.vertices)
 			vertex.tex_coord = (vertex.tex_coord * uv_scaling) + uv_offset;
 	}
-	RenderGeometry(vertices, 4, indices, 6, RenderInterface_GL3::TexturePostprocess, {});
+	RenderGeometry(mesh.vertices.data(), (int)mesh.vertices.size(), mesh.indices.data(), (int)mesh.indices.size(),
+		RenderInterface_GL3::TexturePostprocess, {});
 }
 
 static Rml::Colourf ConvertToColorf(Rml::ColourbPremultiplied c0)

+ 11 - 6
CMake/FileList.cmake

@@ -57,7 +57,6 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/FontEffectShadow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryBackgroundBorder.h
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryBoxShadow.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.h
     ${PROJECT_SOURCE_DIR}/Source/Core/IdNameMap.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Layout/BlockContainer.h
     ${PROJECT_SOURCE_DIR}/Source/Core/Layout/BlockFormattingContext.h
@@ -95,6 +94,7 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserString.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyParserTransform.h
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertyShorthandDefinition.h
+    ${PROJECT_SOURCE_DIR}/Source/Core/RenderManagerAccess.h
     ${PROJECT_SOURCE_DIR}/Source/Core/ScrollController.h
     ${PROJECT_SOURCE_DIR}/Source/Core/StreamFile.h
     ${PROJECT_SOURCE_DIR}/Source/Core/StyleSheetFactory.h
@@ -108,7 +108,6 @@ set(Core_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRectangle.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRow.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.h
-    ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformState.h
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformUtilities.h
     ${PROJECT_SOURCE_DIR}/Source/Core/WidgetScroll.h
@@ -128,8 +127,10 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Animation.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/BaseXMLParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Box.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/CallbackTexture.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Colour.inl
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/CompiledFilterShader.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ComputedValues.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/itlib/flat_map.hpp
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Containers/itlib/flat_set.hpp
@@ -175,7 +176,6 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontGlyph.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/FontMetrics.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Geometry.h
-    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/GeometryUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Header.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ID.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Input.h
@@ -183,6 +183,8 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Math.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Matrix4.inl
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Mesh.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/MeshUtilities.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/NumericValue.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ObserverPtr.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Platform.h
@@ -201,6 +203,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScrollTypes.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Spritesheet.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StableVector.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Stream.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StreamMemory.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StringUtilities.h
@@ -218,6 +221,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/TypeConverter.inl
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Types.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/UniqueRenderResource.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Unit.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/URL.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Utilities.h
@@ -237,7 +241,9 @@ set(Core_PUB_HDR_FILES
 set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/BaseXMLParser.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Box.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/CallbackTexture.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Clock.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/CompiledFilterShader.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ComputedValues.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ComputeProperty.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Context.cpp
@@ -326,8 +332,6 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Geometry.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryBackgroundBorder.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/GeometryBoxShadow.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryDatabase.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/GeometryUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Layout/BlockContainer.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Layout/BlockFormattingContext.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Layout/ContainerBox.cpp
@@ -348,6 +352,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/Log.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Math.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Memory.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/MeshUtilities.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ObserverPtr.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Plugin.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/PluginRegistry.cpp
@@ -371,6 +376,7 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/PropertySpecification.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/RenderInterface.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/RenderManager.cpp
+    ${PROJECT_SOURCE_DIR}/Source/Core/RenderManagerAccess.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/ScrollController.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Spritesheet.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Stream.cpp
@@ -393,7 +399,6 @@ set(Core_SRC_FILES
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRectangle.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutRow.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TextureLayoutTexture.cpp
-    ${PROJECT_SOURCE_DIR}/Source/Core/TextureResource.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/Transform.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformPrimitive.cpp
     ${PROJECT_SOURCE_DIR}/Source/Core/TransformState.cpp

+ 5 - 1
Include/RmlUi/Core.h

@@ -31,6 +31,8 @@
 
 #include "Core/Animation.h"
 #include "Core/Box.h"
+#include "Core/CallbackTexture.h"
+#include "Core/CompiledFilterShader.h"
 #include "Core/ComputedValues.h"
 #include "Core/Context.h"
 #include "Core/ContextInstancer.h"
@@ -61,12 +63,13 @@
 #include "Core/FontEngineInterface.h"
 #include "Core/FontGlyph.h"
 #include "Core/Geometry.h"
-#include "Core/GeometryUtilities.h"
 #include "Core/Header.h"
 #include "Core/ID.h"
 #include "Core/Input.h"
 #include "Core/Log.h"
 #include "Core/Math.h"
+#include "Core/Mesh.h"
+#include "Core/MeshUtilities.h"
 #include "Core/NumericValue.h"
 #include "Core/Plugin.h"
 #include "Core/PropertiesIteratorView.h"
@@ -91,6 +94,7 @@
 #include "Core/Tween.h"
 #include "Core/TypeConverter.h"
 #include "Core/Types.h"
+#include "Core/UniqueRenderResource.h"
 #include "Core/Unit.h"
 #include "Core/Vertex.h"
 #include "Core/XMLNodeHandler.h"

+ 117 - 0
Include/RmlUi/Core/CallbackTexture.h

@@ -0,0 +1,117 @@
+/*
+ * 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-2023 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 RMLUI_CORE_CALLBACKTEXTURE_H
+#define RMLUI_CORE_CALLBACKTEXTURE_H
+
+#include "Header.h"
+#include "Types.h"
+#include "UniqueRenderResource.h"
+
+namespace Rml {
+
+class RenderInterface;
+class RenderManager;
+class CallbackTextureInterface;
+class Texture;
+
+/*
+    Callback function for generating textures on demand.
+    /// @param[in] texture_interface The interface used to specify the texture.
+    /// @return True on success.
+ */
+using CallbackTextureFunction = Function<bool(const CallbackTextureInterface& texture_interface)>;
+
+/**
+    Callback texture is a unique render resource for generating textures on demand.
+
+    It is constructed through the render manager.
+ */
+class RMLUICORE_API CallbackTexture final : public UniqueRenderResource<CallbackTexture, StableVectorIndex, StableVectorIndex::Invalid> {
+public:
+	CallbackTexture() = default;
+
+	operator Texture() const;
+
+	void Release();
+
+private:
+	CallbackTexture(RenderManager* render_manager, StableVectorIndex resource_handle) : UniqueRenderResource(render_manager, resource_handle) {}
+	friend class RenderManager;
+};
+
+/**
+    Interface handed to the texture callback function, which the client can use to submit a single texture.
+ */
+class RMLUICORE_API CallbackTextureInterface {
+public:
+	CallbackTextureInterface(RenderManager& render_manager, RenderInterface& render_interface, TextureHandle& texture_handle, Vector2i& dimensions);
+
+	/// Generate texture from byte source.
+	/// @param[in] source Texture data in 8-bit RGBA (premultiplied) format.
+	/// @param[in] dimensions The width and height of the texture.
+	/// @return True on success.
+	bool GenerateTexture(const byte* source, Vector2i dimensions) const;
+
+	/// Store the current layer as a texture, so that it can be rendered with geometry later.
+	/// @param[in] dimensions The dimensions of the resulting texture, which will be copied from the top-left part of the active layer.
+	void SaveLayerAsTexture(Vector2i dimensions) const;
+
+	RenderManager& GetRenderManager() const;
+
+private:
+	RenderManager& render_manager;
+	RenderInterface& render_interface;
+	TextureHandle& texture_handle;
+	Vector2i& dimensions;
+};
+
+/**
+    Stores a texture callback function, which is used to generate and cache callback textures possibly for multiple render managers.
+ */
+class RMLUICORE_API CallbackTextureSource {
+public:
+	CallbackTextureSource() = default;
+	CallbackTextureSource(CallbackTextureFunction&& callback);
+	~CallbackTextureSource() = default;
+
+	CallbackTextureSource(const CallbackTextureSource&) = delete;
+	CallbackTextureSource& operator=(const CallbackTextureSource&) = delete;
+
+	CallbackTextureSource(CallbackTextureSource&& other) noexcept;
+	CallbackTextureSource& operator=(CallbackTextureSource&& other) noexcept;
+
+	Texture GetTexture(RenderManager& render_manager) const;
+
+private:
+	CallbackTextureFunction callback;
+	mutable SmallUnorderedMap<RenderManager*, CallbackTexture> textures;
+};
+
+} // namespace Rml
+#endif

+ 71 - 0
Include/RmlUi/Core/CompiledFilterShader.h

@@ -0,0 +1,71 @@
+/*
+ * 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-2023 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 RMLUI_CORE_COMPILEDFILTERSHADER_H
+#define RMLUI_CORE_COMPILEDFILTERSHADER_H
+
+#include "Header.h"
+#include "UniqueRenderResource.h"
+
+namespace Rml {
+
+class RenderManager;
+
+/**
+    A compiled filter to be applied during layer pop in its render manager. A unique resource constructed through the render manager.
+ */
+class RMLUICORE_API CompiledFilter final : public UniqueRenderResource<CompiledFilter, CompiledFilterHandle, CompiledFilterHandle(0)> {
+public:
+	CompiledFilter() = default;
+
+	void AddHandleTo(FilterHandleList& list);
+
+	void Release();
+
+private:
+	CompiledFilter(RenderManager* render_manager, CompiledFilterHandle resource_handle) : UniqueRenderResource(render_manager, resource_handle) {}
+	friend class RenderManager;
+};
+
+/**
+    A compiled shader to be used when rendering geometry. A unique resource constructed through the render manager.
+ */
+class RMLUICORE_API CompiledShader final : public UniqueRenderResource<CompiledShader, CompiledShaderHandle, CompiledShaderHandle(0)> {
+public:
+	CompiledShader() = default;
+
+	void Release();
+
+private:
+	CompiledShader(RenderManager* render_manager, CompiledShaderHandle resource_handle) : UniqueRenderResource(render_manager, resource_handle) {}
+	friend class RenderManager;
+};
+
+} // namespace Rml
+
+#endif

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

@@ -31,7 +31,6 @@
 
 #include "Header.h"
 #include "Input.h"
-#include "RenderManager.h"
 #include "ScriptInterface.h"
 #include "ScrollTypes.h"
 #include "Traits.h"
@@ -47,6 +46,7 @@ class DataModel;
 class DataModelConstructor;
 class DataTypeRegister;
 class ScrollController;
+class RenderManager;
 enum class EventId : uint16_t;
 
 /**
@@ -57,10 +57,10 @@ enum class EventId : uint16_t;
 
 class RMLUICORE_API Context : public ScriptInterface {
 public:
-	/// Constructs a new, uninitialised context. This should not be called directly, use CreateContext()
-	/// instead.
+	/// Constructs a new, uninitialised context. This should not be called directly, use CreateContext() instead.
 	/// @param[in] name The name of the context.
-	Context(const String& name);
+	/// @param[in] render_manager The render manager used for this context.
+	Context(const String& name, RenderManager* render_manager);
 	/// Destroys a context.
 	virtual ~Context();
 
@@ -300,9 +300,12 @@ protected:
 private:
 	String name;
 	Vector2i dimensions;
-	float density_independent_pixel_ratio;
+	float density_independent_pixel_ratio = 1.f;
 	String documents_base_tag = "body";
 
+	// Wrapper around the render interface for tracking the render state.
+	RenderManager* render_manager;
+
 	SmallUnorderedSet<String> active_themes;
 
 	ContextInstancer* instancer;
@@ -364,9 +367,6 @@ private:
 	// itself can't be part of it.
 	ElementSet drag_hover_chain;
 
-	// Wrapper around the render interface for tracking the render state.
-	RenderManager render_manager;
-
 	using DataModels = UnorderedMap<String, UniquePtr<DataModel>>;
 	DataModels data_models;
 
@@ -374,7 +374,7 @@ private:
 
 	// Time in seconds until Update and Render should be called again. This allows applications to only redraw the ui if needed.
 	// See RequestNextUpdate() and NextUpdateRequested() for details.
-	double next_update_timeout;
+	double next_update_timeout = 0;
 
 	// Internal callback for when an element is detached or removed from the hierarchy.
 	void OnElementDetach(Element* element);

+ 3 - 1
Include/RmlUi/Core/ContextInstancer.h

@@ -35,6 +35,7 @@
 
 namespace Rml {
 
+class RenderManager;
 class Context;
 class Event;
 
@@ -50,8 +51,9 @@ public:
 
 	/// Instances a context.
 	/// @param[in] name Name of this context.
+	/// @param[in] render_manager The render manager used for this context.
 	/// @return The instanced context.
-	virtual ContextPtr InstanceContext(const String& name) = 0;
+	virtual ContextPtr InstanceContext(const String& name, RenderManager* render_manager) = 0;
 
 	/// Releases a context previously created by this context.
 	/// @param[in] context The context to release.

+ 11 - 4
Include/RmlUi/Core/Core.h

@@ -66,7 +66,9 @@ 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 must be called before Initialise().
+/// 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 specify a render
+/// interface when created.
 /// @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);
@@ -92,8 +94,11 @@ 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);
+RMLUICORE_API Context* CreateContext(const String& name, Vector2i dimensions, RenderInterface* render_interface = nullptr);
 /// 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.
@@ -149,9 +154,11 @@ 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.
-RMLUICORE_API void ReleaseTextures();
+/// @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);
 /// Forces all compiled geometry handles generated by RmlUi to be released.
-RMLUICORE_API void ReleaseCompiledGeometry();
+/// @param[in] render_interface Release all geometry belonging to the given interface, or nullptr to release all geometry in all interfaces.
+RMLUICORE_API void ReleaseCompiledGeometry(RenderInterface* render_interface = nullptr);
 /// Releases unused font textures and rendered glyphs to free up memory, and regenerates actively used fonts.
 /// @note Invalidates all existing FontFaceHandles returned from the font engine.
 RMLUICORE_API void ReleaseFontResources();

+ 10 - 5
Include/RmlUi/Core/Decorator.h

@@ -41,8 +41,9 @@ namespace Rml {
 class Element;
 class PropertyDictionary;
 struct Sprite;
-struct Texture;
+class Texture;
 class StyleSheet;
+class RenderManager;
 class DecoratorInstancerInterface;
 
 /**
@@ -76,13 +77,13 @@ protected:
 	/// Adds a texture if it is valid into the list of textures in use by the decorator.
 	/// @param[in] texture The texture to add.
 	/// @return The index of the texture if it is successful, or -1 if it is invalid.
-	int AddTexture(const Texture& texture);
+	int AddTexture(Texture texture);
 	/// Get number of textures in use by the decorator.
 	int GetNumTextures() const;
 	/// Returns one of the decorator's previously loaded textures.
 	/// @param[in] index The index of the desired texture.
 	/// @return The texture at the appropriate index, or nullptr if the index was invalid.
-	const Texture* GetTexture(int index = 0) const;
+	Texture GetTexture(int index = 0) const;
 
 private:
 	// Stores a list of textures in use by this decorator.
@@ -110,8 +111,8 @@ public:
 
 class RMLUICORE_API DecoratorInstancerInterface {
 public:
-	DecoratorInstancerInterface(const StyleSheet& style_sheet, const PropertySource* property_source) :
-		style_sheet(style_sheet), property_source(property_source)
+	DecoratorInstancerInterface(RenderManager& render_manager, const StyleSheet& style_sheet, const PropertySource* property_source) :
+		render_manager(render_manager), style_sheet(style_sheet), property_source(property_source)
 	{}
 
 	/// Get a sprite from any @spritesheet in the style sheet the decorator is being instanced on.
@@ -121,7 +122,11 @@ public:
 	/// This will use the document path where the 'decorator' property was declared to locate relative files, if available.
 	Texture GetTexture(const String& filename) const;
 
+	/// Get the render manager for the decorator being instanced.
+	RenderManager& GetRenderManager() const;
+
 private:
+	RenderManager& render_manager;
 	const StyleSheet& style_sheet;
 	const PropertySource* property_source;
 };

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

@@ -61,6 +61,7 @@ class InlineLevelBox;
 class ReplacedBox;
 class PropertiesIteratorView;
 class PropertyDictionary;
+class RenderManager;
 class StyleSheet;
 class StyleSheetContainer;
 class TransformState;
@@ -337,6 +338,9 @@ public:
 	/// Returns the element's context.
 	/// @return The context this element's document exists within.
 	Context* GetContext() const;
+	/// Returns the element's render manager.
+	/// @return The render manager responsible for this element.
+	RenderManager* GetRenderManager() const;
 
 	/** @name DOM Properties
 	 */

+ 7 - 5
Include/RmlUi/Core/ElementText.h

@@ -98,18 +98,20 @@ private:
 	};
 
 	// Clears and regenerates all of the text's geometry.
-	void GenerateGeometry(const FontFaceHandle font_face_handle);
-	// Generates the geometry for a single line of text.
-	void GenerateGeometry(const FontFaceHandle font_face_handle, Line& line);
+	void GenerateGeometry(RenderManager& render_manager, FontFaceHandle font_face_handle);
 	// Generates any geometry necessary for rendering decoration (underline, strike-through, etc).
-	void GenerateDecoration(const FontFaceHandle font_face_handle);
+	void GenerateDecoration(Mesh& mesh, FontFaceHandle font_face_handle);
 
 	String text;
 
 	using LineList = Vector<Line>;
 	LineList lines;
 
-	GeometryList geometry;
+	struct TexturedGeometry {
+		Geometry geometry;
+		Texture texture;
+	};
+	Vector<TexturedGeometry> geometry;
 
 	// The decoration geometry we've generated for this string.
 	UniquePtr<Geometry> decoration;

+ 3 - 1
Include/RmlUi/Core/Factory.h

@@ -55,6 +55,7 @@ class StyleSheetContainer;
 class PropertyDictionary;
 class PropertySpecification;
 class DecoratorInstancerInterface;
+class RenderManager;
 enum class EventId : uint16_t;
 
 /**
@@ -79,8 +80,9 @@ public:
 	static void RegisterContextInstancer(ContextInstancer* instancer);
 	/// Instances a new context.
 	/// @param[in] name The name of the new context.
+	/// @param[in] render_manager The render manager used for the new context.
 	/// @return The new context, or nullptr if no context could be created.
-	static ContextPtr InstanceContext(const String& name);
+	static ContextPtr InstanceContext(const String& name, RenderManager* render_manager);
 
 	/// Registers a non-owning pointer to the element instancer that will be used to instance an element when the specified tag is encountered.
 	/// @param[in] name Name of the instancer; elements with this as their tag will use this instancer.

+ 2 - 8
Include/RmlUi/Core/Filter.h

@@ -37,6 +37,7 @@ namespace Rml {
 
 class Element;
 class PropertyDictionary;
+class CompiledFilter;
 
 /**
     The abstract base class for visual filters that are applied when rendering the element.
@@ -49,11 +50,7 @@ public:
 	/// Called on a decorator to generate any required per-element data for a newly decorated element.
 	/// @param[in] element The newly decorated element.
 	/// @return A handle to a decorator-defined data handle, or nullptr if none is needed for the element.
-	virtual CompiledFilterHandle CompileFilter(Element* element) const = 0;
-
-	/// Called to release element data generated by this decorator.
-	/// @param[in] element_data The element data handle to release.
-	virtual void ReleaseCompiledFilter(Element* element, CompiledFilterHandle filter_handle) const = 0;
+	virtual CompiledFilter CompileFilter(Element* element) const = 0;
 
 	/// Allows extending the area being affected by this filter beyond the border box of the element.
 	/// @param[in] element The element the filter is being rendered on.
@@ -61,9 +58,6 @@ public:
 	/// @note Modifying the ink overflow rectangle affects rendering of all filter decorators active on the current element.
 	/// @note Only affects the 'filter' property, not 'backdrop-filter'.
 	virtual void ExtendInkOverflow(Element* element, Rectanglef& overflow) const;
-
-	/// Value specifying an invalid or non-existent filter handle.
-	static constexpr CompiledFilterHandle INVALID_COMPILEDFILTERHANDLE = 0;
 };
 
 /**

+ 7 - 6
Include/RmlUi/Core/FontEngineInterface.h

@@ -29,8 +29,8 @@
 #define RMLUI_CORE_FONTENGINEINTERFACE_H
 
 #include "FontMetrics.h"
-#include "Geometry.h"
 #include "Header.h"
+#include "Mesh.h"
 #include "StyleTypes.h"
 #include "Types.h"
 
@@ -96,7 +96,8 @@ public:
 	/// @return The width, in pixels, this string will occupy if rendered with this handle.
 	virtual int GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character = Character::Null);
 
-	/// Called by RmlUi when it wants to retrieve the geometry required to render a single line of text.
+	/// Called by RmlUi when it wants to retrieve the meshes required to render a single line of text.
+	/// @param[in] render_manager The render manager responsible for rendering the string.
 	/// @param[in] face_handle The font handle.
 	/// @param[in] font_effects_handle The handle to the prepared font effects for which the geometry should be generated.
 	/// @param[in] string The string to render.
@@ -104,10 +105,10 @@ public:
 	/// @param[in] colour The colour to render the text.
 	/// @param[in] opacity The opacity of the text, should be applied to font effects.
 	/// @param[in] letter_spacing The letter spacing size in pixels.
-	/// @param[out] geometry An array of geometries to generate the geometry into.
-	/// @return The width, in pixels, of the string geometry.
-	virtual int GenerateString(FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string, const Vector2f& position,
-		ColourbPremultiplied colour, float opacity, float letter_spacing, GeometryList& geometry);
+	/// @param[out] mesh_list A list to place the meshes and textures representing the string to be rendered.
+	/// @return The width, in pixels, of the string mesh.
+	virtual int GenerateString(RenderManager& render_manager, FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string,
+		const Vector2f& position, ColourbPremultiplied colour, float opacity, float letter_spacing, TexturedMeshList& mesh_list);
 
 	/// Called by RmlUi to determine if the text geometry is required to be re-generated. Whenever the returned version
 	/// is changed, all geometry belonging to the given face handle will be re-generated.

+ 14 - 64
Include/RmlUi/Core/Geometry.h

@@ -29,85 +29,35 @@
 #ifndef RMLUI_CORE_GEOMETRY_H
 #define RMLUI_CORE_GEOMETRY_H
 
+#include "CompiledFilterShader.h"
 #include "Header.h"
-#include "Vertex.h"
-#include <stdint.h>
+#include "Mesh.h"
+#include "Texture.h"
+#include "UniqueRenderResource.h"
 
 namespace Rml {
 
-class Context;
-class Element;
-struct Texture;
-enum class ClipMaskOperation;
-using GeometryDatabaseHandle = uint32_t;
+class RenderManager;
 
 /**
-    A helper object for holding an array of vertices and indices, and compiling it as necessary when rendered.
+    A representation of geometry to be rendered through its underlying render interface.
 
-    @author Peter Curry
+    A unique resource constructed through the render manager.
  */
-
-class RMLUICORE_API Geometry {
+class RMLUICORE_API Geometry final : public UniqueRenderResource<Geometry, StableVectorIndex, StableVectorIndex::Invalid> {
 public:
-	Geometry();
-
-	Geometry(const Geometry&) = delete;
-	Geometry& operator=(const Geometry&) = delete;
-
-	Geometry(Geometry&& other) noexcept;
-	Geometry& operator=(Geometry&& other) noexcept;
-
-	~Geometry();
-
-	/// 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);
-
-	/// Render the geometry with a compiled shader. Requires that the geometry can be compiled.
-	/// @param[in] shader The shader to use for rendering the geometry.
-	/// @param[in] translation The translation of the geometry.
-	void RenderWithShader(CompiledShaderHandle shader, Vector2f translation);
+	enum class ReleaseMode { ReturnMesh, ClearMesh };
 
-	/// Use the geometry to set the clip mask through the render interface. Requires that the geometry can be compiled.
-	/// @param[in] operation The clip mask operation to apply.
-	/// @param[in] translation The translation of the geometry.
-	void RenderToClipMask(ClipMaskOperation operation, Vector2f translation);
+	Geometry() = default;
 
-	/// Returns the geometry's vertices. If these are written to, Release() should be called to force a recompile.
-	/// @return The geometry's vertex array.
-	Vector<Vertex>& GetVertices();
-	/// Returns the geometry's indices. If these are written to, Release() should be called to force a recompile.
-	/// @return The geometry's index array.
-	Vector<int>& GetIndices();
+	void Render(Vector2f translation, Texture texture = {}, const CompiledShader& shader = {}) const;
 
-	/// Gets the geometry's texture.
-	/// @return The geometry's texture.
-	const Texture* GetTexture() const;
-	/// Sets the geometry's texture.
-	void SetTexture(const Texture* texture);
-
-	/// Releases any previously-compiled geometry, and forces any new geometry to have a compile attempted.
-	/// @param[in] clear_buffers True to also clear the vertex and index buffers, false to leave intact.
-	void Release(bool clear_buffers = false);
-
-	/// Returns true if there is geometry to be rendered.
-	explicit operator bool() const;
+	Mesh Release(ReleaseMode mode = ReleaseMode::ReturnMesh);
 
 private:
-	// Move members from another geometry.
-	void MoveFrom(Geometry& other) noexcept;
-
-	Vector<Vertex> vertices;
-	Vector<int> indices;
-	const Texture* texture = nullptr;
-
-	CompiledGeometryHandle compiled_geometry = 0;
-	bool compile_attempted = false;
-
-	GeometryDatabaseHandle database_handle;
+	Geometry(RenderManager* render_manager, StableVectorIndex resource_handle);
+	friend class RenderManager;
 };
 
-using GeometryList = Vector<Geometry>;
-
 } // namespace Rml
 #endif

+ 15 - 25
Source/Core/GeometryDatabase.h → Include/RmlUi/Core/Mesh.h

@@ -26,38 +26,28 @@
  *
  */
 
-#ifndef RMLUI_CORE_GEOMETRYDATABASE_H
-#define RMLUI_CORE_GEOMETRYDATABASE_H
+#ifndef RMLUI_CORE_MESH_H
+#define RMLUI_CORE_MESH_H
 
-#include "../../Include/RmlUi/Core/Types.h"
-#include <stdint.h>
+#include "Header.h"
+#include "Texture.h"
+#include "Vertex.h"
 
 namespace Rml {
 
-class Geometry;
-using GeometryDatabaseHandle = uint32_t;
+struct Mesh {
+	Vector<Vertex> vertices;
+	Vector<int> indices;
 
-/**
-    The geometry database stores a reference to all active geometry.
+	explicit operator bool() const { return !indices.empty(); }
+};
 
-    The intention is for the user to be able to re-compile all geometry in use.
+struct TexturedMesh {
+	Mesh mesh;
+	Texture texture;
+};
 
-    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();
-
-#ifdef RMLUI_TESTS_ENABLED
-	bool PrepareForTests();
-	bool ListMatchesDatabase(const Vector<Geometry>& geometry_list);
-#endif
-} // namespace GeometryDatabase
+using TexturedMeshList = Vector<TexturedMesh>;
 
 } // namespace Rml
 #endif

+ 13 - 18
Include/RmlUi/Core/GeometryUtilities.h → Include/RmlUi/Core/MeshUtilities.h

@@ -26,26 +26,22 @@
  *
  */
 
-#ifndef RMLUI_CORE_GEOMETRYUTILITIES_H
-#define RMLUI_CORE_GEOMETRYUTILITIES_H
+#ifndef RMLUI_CORE_MESHUTILITIES_H
+#define RMLUI_CORE_MESHUTILITIES_H
 
 #include "Header.h"
-#include "StyleTypes.h"
 #include "Types.h"
-#include "Vertex.h"
 
 namespace Rml {
 
 class Box;
-class Geometry;
+struct Mesh;
 
 /**
-    A class containing helper functions for rendering geometry.
+    A class containing helper functions for generating meshes.
 
-    @author Robert Curry
  */
-
-class RMLUICORE_API GeometryUtilities {
+class RMLUICORE_API MeshUtilities {
 public:
 	/// Generates a quad from a position, size and colour.
 	/// @param[out] vertices An array of at least four vertices that the generated vertex data will be written into.
@@ -54,7 +50,7 @@ public:
 	/// @param[in] dimensions The dimensions of the quad to generate.
 	/// @param[in] colour The colour to be assigned to each of the quad's vertices.
 	/// @param[in] index_offset The offset to be added to the generated indices; this should be the number of vertices already in the array.
-	static void GenerateQuad(Vertex* vertices, int* indices, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour, int index_offset = 0);
+	static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour);
 	/// Generates a quad from a position, size, colour and texture coordinates.
 	/// @param[out] vertices An array of at least four vertices that the generated vertex data will be written into.
 	/// @param[out] indices An array of at least six indices that the generated index data will be written into.
@@ -64,15 +60,15 @@ public:
 	/// @param[in] top_left_texcoord The texture coordinates at the top-left of the quad.
 	/// @param[in] bottom_right_texcoord The texture coordinates at the bottom-right of the quad.
 	/// @param[in] index_offset The offset to be added to the generated indices; this should be the number of vertices already in the array.
-	static void GenerateQuad(Vertex* vertices, int* indices, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour,
-		Vector2f top_left_texcoord, Vector2f bottom_right_texcoord, int index_offset = 0);
+	static void GenerateQuad(Mesh& mesh, Vector2f origin, Vector2f dimensions, ColourbPremultiplied colour, Vector2f top_left_texcoord,
+		Vector2f bottom_right_texcoord);
 
 	/// Generates the geometry required to render a line.
 	/// @param[out] geometry The geometry to append the newly created geometry into.
 	/// @param[in] position The top-left position the line.
 	/// @param[in] position The size of the line.
 	/// @param[in] color The color to draw the line in.
-	static void GenerateLine(Geometry* geometry, Vector2f position, Vector2f size, ColourbPremultiplied color);
+	static void GenerateLine(Mesh& mesh, Vector2f position, Vector2f size, ColourbPremultiplied color);
 
 	/// Generates the geometry for an element's background and border, with support for the border-radius property.
 	/// @param[out] geometry The geometry to append the newly created vertices and indices into.
@@ -82,8 +78,8 @@ public:
 	/// @param[in] background_colour The colour applied to the background, set alpha to zero to not generate the background.
 	/// @param[in] border_colours A four-element array of border colors in top-right-bottom-left order.
 	/// @note Vertex positions are relative to the border-box, vertex texture coordinates are default initialized.
-	static void GenerateBackgroundBorder(Geometry* geometry, const Box& box, Vector2f offset, Vector4f border_radius,
-		ColourbPremultiplied background_colour, const ColourbPremultiplied border_colours[4]);
+	static void GenerateBackgroundBorder(Mesh& mesh, const Box& box, Vector2f offset, Vector4f border_radius, ColourbPremultiplied background_colour,
+		const ColourbPremultiplied border_colours[4]);
 
 	/// Generates the background geometry for an element's area, with support for border-radius.
 	/// @param[out] geometry The geometry to append the newly created vertices and indices into.
@@ -93,12 +89,11 @@ public:
 	/// @param[in] colour The colour applied to the background.
 	/// @param[in] area Either the border, padding or content area to be filled.
 	/// @note Vertex positions are relative to the border-box, vertex texture coordinates are default initialized.
-	static void GenerateBackground(Geometry* geometry, const Box& box, Vector2f offset, Vector4f border_radius, ColourbPremultiplied colour,
+	static void GenerateBackground(Mesh& mesh, const Box& box, Vector2f offset, Vector4f border_radius, ColourbPremultiplied colour,
 		BoxArea area = BoxArea::Padding);
 
 private:
-	GeometryUtilities();
-	~GeometryUtilities();
+	MeshUtilities() = delete;
 };
 
 } // namespace Rml

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

@@ -30,7 +30,6 @@
 #define RMLUI_CORE_RENDERINTERFACE_H
 
 #include "Header.h"
-#include "Texture.h"
 #include "Traits.h"
 #include "Types.h"
 #include "Vertex.h"
@@ -53,8 +52,6 @@ enum class BlendMode {
 	Discard, // Leave the destination colors unaltered.
 };
 
-using FilterHandleList = Vector<CompiledFilterHandle>;
-
 /**
     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.

+ 61 - 11
Include/RmlUi/Core/RenderManager.h

@@ -29,13 +29,20 @@
 #ifndef RMLUI_CORE_RENDERMANAGER_H
 #define RMLUI_CORE_RENDERMANAGER_H
 
-#include "Box.h"
+#include "CallbackTexture.h"
+#include "Mesh.h"
 #include "RenderInterface.h"
+#include "StableVector.h"
 #include "Types.h"
 
 namespace Rml {
 
 class Geometry;
+class CompiledFilter;
+class CompiledShader;
+class TextureDatabase;
+class Texture;
+class RenderManagerAccess;
 
 struct ClipMaskGeometry {
 	ClipMaskOperation operation;
@@ -60,15 +67,18 @@ struct RenderState {
 };
 
 /**
-    A wrapper over the render interface which tracks the following state:
-       - Scissor
-       - Clip mask
-       - Transform
-    All such operations on the render interface should go through this class.
+    A wrapper over the render interface, which tracks its state and resources.
+
+    All operations to be submitted to the render interface should go through this class.
  */
 class RMLUICORE_API RenderManager : NonCopyMoveable {
 public:
-	RenderManager();
+	RenderManager(RenderInterface* render_interface);
+	~RenderManager();
+
+	void PrepareRender();
+	void SetViewport(Vector2i dimensions);
+	Vector2i GetViewport() const;
 
 	void DisableScissorRegion();
 	void SetScissorRegion(Rectanglei region);
@@ -79,20 +89,60 @@ public:
 
 	void SetTransform(const Matrix4f* new_transform);
 
+	// Retrieves the cached render state. If setting this state again, ensure the lifetimes of referenced objects are
+	// still valid. Possibly invalidating actions include destroying an element, or altering its transform property.
 	const RenderState& GetState() const { return state; }
 	void SetState(const RenderState& next);
 	void ResetState();
 
-	void BeginRender();
-	void SetViewport(Vector2i dimensions);
-	Vector2i GetViewport() const;
+	Geometry MakeGeometry(Mesh&& mesh);
+
+	Texture LoadTexture(const String& source, const String& document_path = String());
+	CallbackTexture MakeCallbackTexture(CallbackTextureFunction callback);
+
+	CompiledFilter CompileFilter(const String& name, const Dictionary& parameters);
+	CompiledShader CompileShader(const String& name, const Dictionary& parameters);
+
+	void PushLayer(LayerFill layer_fill);
+	void PopLayer(BlendMode blend_mode, const FilterHandleList& filters);
+
+	CompiledFilter SaveLayerAsMaskImage();
 
 private:
 	void ApplyClipMask(const ClipMaskGeometryList& clip_elements);
 
-	RenderState state;
+	StableVectorIndex InsertGeometry(Mesh&& mesh);
+	CompiledGeometryHandle GetCompiledGeometryHandle(StableVectorIndex index);
+
+	void Render(const Geometry& geometry, Vector2f translation, Texture texture, const CompiledShader& shader);
+
+	void GetTextureSourceList(StringList& source_list) const;
+
+	void ReleaseAllTextures();
+	void ReleaseAllCompiledGeometry();
+
+	void ReleaseResource(const CallbackTexture& texture);
+	Mesh ReleaseResource(const Geometry& geometry);
+	void ReleaseResource(const CompiledFilter& filter);
+	void ReleaseResource(const CompiledShader& shader);
+
+	struct GeometryData {
+		Mesh mesh;
+		CompiledGeometryHandle handle = {};
+	};
+
 	RenderInterface* render_interface = nullptr;
+
+	StableVector<GeometryData> geometry_list;
+	UniquePtr<TextureDatabase> texture_database;
+
+	int compiled_filter_count = 0;
+	int compiled_shader_count = 0;
+
+	RenderState state;
 	Vector2i viewport_dimensions;
+
+	friend class RenderManagerAccess;
 };
 
 } // namespace Rml

+ 2 - 5
Include/RmlUi/Core/Spritesheet.h

@@ -46,14 +46,11 @@ using SpriteMap = UnorderedMap<String, Sprite>; // key: sprite name (as given in
  */
 struct Spritesheet {
 	String name;
-	String image_source;
-	String definition_source;
 	int definition_line_number;
 	float display_scale; // The inverse of the 'resolution' spritesheet property.
-	Texture texture;
+	TextureSource texture_source;
 
-	Spritesheet(const String& name, const String& image_source, const String& definition_source, int definition_line_number, float display_scale,
-		const Texture& texture);
+	Spritesheet(const String& name, const String& source, const String& document_path, int definition_line_number, float display_scale);
 };
 
 using SpriteDefinitionList = Vector<Pair<String, Rectanglef>>; // Sprite name and rectangle

+ 124 - 0
Include/RmlUi/Core/StableVector.h

@@ -0,0 +1,124 @@
+/*
+ * 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 RMLUI_CORE_STABLEVECTOR_H
+#define RMLUI_CORE_STABLEVECTOR_H
+
+#include "Header.h"
+#include "Types.h"
+#include <algorithm>
+#include <type_traits>
+
+namespace Rml {
+
+/**
+    A vector-like container that returns stable indices to refer to entries.
+
+    The indices are only invalidated when the element is erased. Pointers on the other hand are invalidated just like for a
+    vector. The container is implemented as a vector with a separate bit mask to track free slots. For simplicity, freed
+    slots are simply replaced with value-initialized elements.
+ */
+template <typename T>
+class StableVector {
+public:
+	StableVector() = default;
+
+	StableVectorIndex insert(T value)
+	{
+		const auto it_free = std::find(free_slots.begin(), free_slots.end(), true);
+		StableVectorIndex index;
+		if (it_free == free_slots.end())
+		{
+			index = StableVectorIndex(elements.size());
+			elements.push_back(std::move(value));
+			free_slots.push_back(false);
+		}
+		else
+		{
+			const size_t numeric_index = static_cast<size_t>(it_free - free_slots.begin());
+			index = static_cast<StableVectorIndex>(numeric_index);
+			elements[numeric_index] = std::move(value);
+			*it_free = false;
+		}
+		return index;
+	}
+
+	bool empty() const { return elements.size() == count_free_slots(); }
+	size_t size() const { return elements.size() - count_free_slots(); }
+
+	void reserve(size_t reserve_size)
+	{
+		elements.reserve(reserve_size);
+		free_slots.reserve(reserve_size);
+	}
+
+	void clear()
+	{
+		elements.clear();
+		free_slots.clear();
+	}
+	T erase(StableVectorIndex index)
+	{
+		RMLUI_ASSERT(size_t(index) < elements.size() && !free_slots[size_t(index)]);
+		free_slots[size_t(index)] = true;
+		return std::exchange(elements[size_t(index)], T());
+	}
+
+	T& operator[](StableVectorIndex index)
+	{
+		RMLUI_ASSERT(size_t(index) < elements.size() && !free_slots[size_t(index)]);
+		return elements[size_t(index)];
+	}
+	const T& operator[](StableVectorIndex index) const
+	{
+		RMLUI_ASSERT(size_t(index) < elements.size() && !free_slots[size_t(index)]);
+		return elements[size_t(index)];
+	}
+
+	// Iterate over every item in the vector, skipping free slots.
+	template <typename Func>
+	void for_each(Func&& func)
+	{
+		for (size_t i = 0; i < elements.size(); i++)
+		{
+			if (!free_slots[i])
+				func(elements[i]);
+		}
+	}
+
+private:
+	size_t count_free_slots() const { return std::count(free_slots.begin(), free_slots.end(), true); }
+
+	// List of all active elements, including any free slots.
+	Vector<T> elements;
+	// Declares free slots in 'elements'.
+	Vector<bool> free_slots;
+};
+
+} // namespace Rml
+#endif

+ 3 - 1
Include/RmlUi/Core/StyleSheet.h

@@ -40,6 +40,7 @@ class Element;
 class ElementDefinition;
 class StyleSheetNode;
 class Decorator;
+class RenderManager;
 class SpritesheetList;
 class StyleSheetContainer;
 class StyleSheetParser;
@@ -84,7 +85,8 @@ public:
 	SharedPtr<const ElementDefinition> GetElementDefinition(const Element* element) const;
 
 	/// Returns a list of instanced decorators from the declarations. The instances are cached for faster future retrieval.
-	const DecoratorPtrList& InstanceDecorators(const DecoratorDeclarationList& declaration_list, const PropertySource* decorator_source) const;
+	const DecoratorPtrList& InstanceDecorators(RenderManager& render_manager, const DecoratorDeclarationList& declaration_list,
+		const PropertySource* decorator_source) const;
 
 private:
 	StyleSheet();

+ 36 - 39
Include/RmlUi/Core/Texture.h

@@ -34,55 +34,52 @@
 
 namespace Rml {
 
-class TextureResource;
-class RenderInterface;
-
-/*
-    Callback function for generating textures.
-    /// @param[in] render_interface The render interface to use for generating the texture.
-    /// @param[in] name The name used to set the texture.
-    /// @param[out] handle The texture handle obtained through the render interface.
-    /// @param[out] dimensions The width and height of the generated texture.
-    /// @return True on success.
-*/
-using TextureCallback = Function<bool(RenderInterface* render_interface, const String& name, TextureHandle& handle, Vector2i& dimensions)>;
+class CallbackTexture;
+class RenderManager;
 
 /**
-    Abstraction of a two-dimensional texture image, with an application-specific texture handle.
+    Texture is a simple view of either a file texture or a callback texture.
 
-    @author Peter Curry
+    It is constructed through the render manager. It can be freely copied, and does not own or release the underlying
+    resource. The user is responsible for ensuring that the lifetime of the texture is valid.
  */
-
-struct RMLUICORE_API Texture {
+class RMLUICORE_API Texture {
 public:
-	/// Set the texture source and path. The texture is added to the global cache and only loaded on first use.
-	/// @param[in] source The source of the texture.
-	/// @param[in] source_path The path of the resource that is requesting the texture (ie, the RCSS file in which it was specified, etc).
-	void Set(const String& source, const String& source_path = "");
-
-	/// Set a callback function for generating the texture on first use. The texture is never added to the global cache.
-	/// @param[in] name The name of the texture.
-	/// @param[in] callback The callback function which generates the data of the texture, see TextureCallback.
-	void Set(const String& name, TextureCallback&& callback);
-
-	/// 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, 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;
+	Texture() = default;
 
-	/// Returns true if the texture points to the same underlying resource.
-	bool operator==(const Texture&) const;
+	Vector2i GetDimensions() const;
 
-	/// Returns true if the underlying resource is set.
 	explicit operator bool() const;
+	bool operator==(const Texture& other) const;
+
+private:
+	Texture(RenderManager* render_manager, TextureFileIndex file_index);
+	Texture(RenderManager* render_manager, StableVectorIndex callback_index);
+
+	RenderManager* render_manager = nullptr;
+	TextureFileIndex file_index = TextureFileIndex::Invalid;
+	StableVectorIndex callback_index = StableVectorIndex::Invalid;
+
+	friend class RenderManager;
+	friend class CallbackTexture;
+};
+
+/**
+    Stores the file source for a texture, which is used to generate textures possibly for multiple render managers.
+ */
+class RMLUICORE_API TextureSource : NonCopyMoveable {
+public:
+	TextureSource() = default;
+	TextureSource(String source, String document_path);
+
+	Texture GetTexture(RenderManager& render_manager) const;
+
+	const String& GetDefinitionSource() const;
 
 private:
-	SharedPtr<TextureResource> resource;
+	String source;
+	String document_path;
+	mutable SmallUnorderedMap<RenderManager*, Texture> textures;
 };
 
 } // namespace Rml

+ 7 - 1
Include/RmlUi/Core/Types.h

@@ -32,8 +32,8 @@
 #include "../Config/Config.h"
 #include "Traits.h"
 #include <cstdlib>
-#include <stdint.h>
 #include <memory>
+#include <stdint.h>
 
 namespace Rml {
 
@@ -78,6 +78,8 @@ using Matrix4f = RMLUI_MATRIX4_TYPE;
 class Element;
 class ElementInstancer;
 class ElementAnimation;
+class RenderManager;
+class Texture;
 class Context;
 class Event;
 class Property;
@@ -112,6 +114,9 @@ using ElementPtr = UniqueReleaserPtr<Element>;
 using ContextPtr = UniqueReleaserPtr<Context>;
 using EventPtr = UniqueReleaserPtr<Event>;
 
+enum class StableVectorIndex : uint32_t { Invalid = uint32_t(-1) };
+enum class TextureFileIndex : uint32_t { Invalid = uint32_t(-1) };
+
 // Container types for common classes
 using ElementList = Vector<Element*>;
 using OwnedElementList = Vector<ElementPtr>;
@@ -133,6 +138,7 @@ struct FontEffects {
 };
 using ColorStopList = Vector<ColorStop>;
 using BoxShadowList = Vector<BoxShadow>;
+using FilterHandleList = Vector<CompiledFilterHandle>;
 
 // Additional smart pointers
 using TransformPtr = SharedPtr<Transform>;

+ 87 - 0
Include/RmlUi/Core/UniqueRenderResource.h

@@ -0,0 +1,87 @@
+/*
+ * 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-2023 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 RMLUI_CORE_UNIQUERENDERRESOURCE_H
+#define RMLUI_CORE_UNIQUERENDERRESOURCE_H
+
+#include "Types.h"
+#include <utility>
+
+namespace Rml {
+
+class RenderManager;
+
+/**
+    Abstraction for a uniquely owned render resource. The underlying resource is released upon destruction.
+ */
+template <typename Derived, typename Handle, Handle InvalidHandleValue>
+class RMLUICORE_API UniqueRenderResource {
+public:
+	static constexpr Handle InvalidHandle() { return InvalidHandleValue; }
+
+	explicit operator bool() const { return resource_handle != InvalidHandleValue; }
+
+protected:
+	UniqueRenderResource() = default;
+	UniqueRenderResource(RenderManager* render_manager, Handle resource_handle) : render_manager(render_manager), resource_handle(resource_handle) {}
+
+	~UniqueRenderResource() noexcept { ReleaseInDerived(); }
+
+	UniqueRenderResource(const UniqueRenderResource&) = delete;
+	UniqueRenderResource& operator=(const UniqueRenderResource&) = delete;
+
+	UniqueRenderResource(UniqueRenderResource&& other) noexcept { MoveFrom(other); }
+	UniqueRenderResource& operator=(UniqueRenderResource&& other) noexcept
+	{
+		ReleaseInDerived();
+		MoveFrom(other);
+		return *this;
+	}
+
+	void Clear() noexcept
+	{
+		render_manager = nullptr;
+		resource_handle = InvalidHandleValue;
+	}
+
+	RenderManager* render_manager = nullptr;
+	Handle resource_handle = InvalidHandleValue;
+
+private:
+	void ReleaseInDerived() { static_cast<Derived*>(this)->Release(); }
+
+	void MoveFrom(UniqueRenderResource& other) noexcept
+	{
+		render_manager = std::exchange(other.render_manager, nullptr);
+		resource_handle = std::exchange(other.resource_handle, InvalidHandleValue);
+	}
+};
+
+} // namespace Rml
+
+#endif

+ 2 - 2
Include/RmlUi/Lottie/ElementLottie.h

@@ -29,10 +29,10 @@
 #ifndef RMLUI_LOTTIE_ELEMENT_LOTTIE_H
 #define RMLUI_LOTTIE_ELEMENT_LOTTIE_H
 
+#include "../Core/CallbackTexture.h"
 #include "../Core/Element.h"
 #include "../Core/Geometry.h"
 #include "../Core/Header.h"
-#include "../Core/Texture.h"
 
 namespace rlottie {
 class Animation;
@@ -81,7 +81,7 @@ private:
 	bool texture_size_dirty = false;
 
 	// The texture this element is rendering from.
-	Texture texture;
+	CallbackTexture texture;
 	// The texture data buffer.
 	size_t texture_data_size = 0;
 	UniquePtr<byte[]> texture_data;

+ 2 - 2
Include/RmlUi/SVG/ElementSVG.h

@@ -29,10 +29,10 @@
 #ifndef RMLUI_SVG_ELEMENT_SVG_H
 #define RMLUI_SVG_ELEMENT_SVG_H
 
+#include "../Core/CallbackTexture.h"
 #include "../Core/Element.h"
 #include "../Core/Geometry.h"
 #include "../Core/Header.h"
-#include "../Core/Texture.h"
 
 namespace lunasvg {
 class Document;
@@ -78,7 +78,7 @@ private:
 	bool texture_dirty = false;
 
 	// The texture this element is rendering from.
-	Texture texture;
+	CallbackTexture texture;
 
 	// The image's intrinsic dimensions.
 	Vector2f intrinsic_dimensions;

+ 15 - 18
Samples/basic/bitmapfont/src/FontEngineBitmap.cpp

@@ -29,7 +29,7 @@
 #include "FontEngineBitmap.h"
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/FileInterface.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/MeshUtilities.h>
 #include <RmlUi/Core/StreamMemory.h>
 #include <cstdio>
 
@@ -84,12 +84,9 @@ bool LoadFontFace(const String& file_name)
 		parser.metrics.underline_thickness = 1.f;
 	}
 
-	Texture texture;
-	texture.Set(parser.texture_name, file_name);
-
 	// Construct and add the font face
-	fonts.push_back(Rml::MakeUnique<FontFaceBitmap>(parser.family, parser.style, parser.weight, parser.metrics, texture, parser.texture_dimensions,
-		std::move(parser.glyphs), std::move(parser.kerning)));
+	fonts.push_back(Rml::MakeUnique<FontFaceBitmap>(parser.family, parser.style, parser.weight, parser.metrics, parser.texture_name, file_name,
+		parser.texture_dimensions, std::move(parser.glyphs), std::move(parser.kerning)));
 
 	return true;
 }
@@ -125,11 +122,11 @@ FontFaceBitmap* GetFontFaceHandle(const String& family, FontStyle style, FontWei
 
 } // namespace FontProviderBitmap
 
-FontFaceBitmap::FontFaceBitmap(String family, FontStyle style, FontWeight weight, FontMetrics metrics, Texture texture, Vector2f texture_dimensions,
-	FontGlyphs&& glyphs, FontKerning&& kerning) :
+FontFaceBitmap::FontFaceBitmap(String family, FontStyle style, FontWeight weight, FontMetrics metrics, String texture_name, String texture_path,
+	Vector2f texture_dimensions, FontGlyphs&& glyphs, FontKerning&& kerning) :
 	family(family),
-	style(style), weight(weight), metrics(metrics), texture(texture), texture_dimensions(texture_dimensions), glyphs(std::move(glyphs)),
-	kerning(std::move(kerning))
+	style(style), weight(weight), metrics(metrics), texture_source(texture_name, texture_path), texture_dimensions(texture_dimensions),
+	glyphs(std::move(glyphs)), kerning(std::move(kerning))
 {}
 
 int FontFaceBitmap::GetStringWidth(const String& string, Character previous_character)
@@ -155,17 +152,18 @@ int FontFaceBitmap::GetStringWidth(const String& string, Character previous_char
 	return width;
 }
 
-int FontFaceBitmap::GenerateString(const String& string, const Vector2f& string_position, ColourbPremultiplied colour, GeometryList& geometry_list)
+int FontFaceBitmap::GenerateString(RenderManager& render_manager, const String& string, const Vector2f& string_position, ColourbPremultiplied colour,
+	TexturedMeshList& mesh_list)
 {
 	int width = 0;
 
-	geometry_list.resize(1);
-	Rml::Geometry& geometry = geometry_list[0];
+	mesh_list.resize(1);
 
-	geometry.SetTexture(&texture);
+	mesh_list[0].texture = texture_source.GetTexture(render_manager);
 
-	auto& vertices = geometry.GetVertices();
-	auto& indices = geometry.GetIndices();
+	Rml::Mesh& mesh = mesh_list[0].mesh;
+	auto& vertices = mesh.vertices;
+	auto& indices = mesh.indices;
 
 	vertices.reserve(string.size() * 4);
 	indices.reserve(string.size() * 6);
@@ -195,8 +193,7 @@ int FontFaceBitmap::GenerateString(const String& string, const Vector2f& string_
 		Vector2f uv_top_left = glyph.position / texture_dimensions;
 		Vector2f uv_bottom_right = (glyph.position + glyph.dimension) / texture_dimensions;
 
-		Rml::GeometryUtilities::GenerateQuad(&vertices[0] + (vertices.size() - 4), &indices[0] + (indices.size() - 6),
-			Vector2f(position + glyph.offset).Round(), glyph.dimension, colour, uv_top_left, uv_bottom_right, (int)vertices.size() - 4);
+		Rml::MeshUtilities::GenerateQuad(mesh, Vector2f(position + glyph.offset).Round(), glyph.dimension, colour, uv_top_left, uv_bottom_right);
 
 		width += glyph.advance;
 		position.x += glyph.advance;

+ 5 - 4
Samples/basic/bitmapfont/src/FontEngineBitmap.h

@@ -35,6 +35,7 @@
 #include <RmlUi/Core/Types.h>
 
 class FontFaceBitmap;
+using Rml::TextureSource;
 
 namespace FontProviderBitmap {
 void Initialise();
@@ -58,14 +59,14 @@ using FontKerning = Rml::UnorderedMap<uint64_t, int>;
 
 class FontFaceBitmap {
 public:
-	FontFaceBitmap(String family, FontStyle style, FontWeight weight, FontMetrics metrics, Texture texture, Vector2f texture_dimensions,
-		FontGlyphs&& glyphs, FontKerning&& kerning);
+	FontFaceBitmap(String family, FontStyle style, FontWeight weight, FontMetrics metrics, String texture_name, String texture_path,
+		Vector2f texture_dimensions, FontGlyphs&& glyphs, FontKerning&& kerning);
 
 	// Get width of string.
 	int GetStringWidth(const String& string, Character prior_character);
 
 	// Generate the string geometry, returning its width.
-	int GenerateString(const String& string, const Vector2f& position, ColourbPremultiplied colour, GeometryList& geometry);
+	int GenerateString(RenderManager& render_manager, const String& string, const Vector2f& position, ColourbPremultiplied colour, TexturedMeshList& mesh_list);
 
 	const FontMetrics& GetMetrics() const { return metrics; }
 
@@ -82,7 +83,7 @@ private:
 
 	FontMetrics metrics;
 
-	Texture texture;
+	TextureSource texture_source;
 	Vector2f texture_dimensions;
 
 	FontGlyphs glyphs;

+ 4 - 3
Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.cpp

@@ -80,11 +80,12 @@ int FontEngineInterfaceBitmap::GetStringWidth(FontFaceHandle handle, const Strin
 	return handle_bitmap->GetStringWidth(string, prior_character);
 }
 
-int FontEngineInterfaceBitmap::GenerateString(FontFaceHandle handle, FontEffectsHandle /*font_effects_handle*/, const String& string,
-	const Vector2f& position, ColourbPremultiplied colour, float /*opacity*/, float /*letter_spacing*/, GeometryList& geometry)
+int FontEngineInterfaceBitmap::GenerateString(RenderManager& render_manager, FontFaceHandle handle, FontEffectsHandle /*font_effects_handle*/,
+	const String& string, const Vector2f& position, ColourbPremultiplied colour, float /*opacity*/, float /*letter_spacing*/,
+	TexturedMeshList& mesh_list)
 {
 	auto handle_bitmap = reinterpret_cast<FontFaceBitmap*>(handle);
-	return handle_bitmap->GenerateString(string, position, colour, geometry);
+	return handle_bitmap->GenerateString(render_manager, string, position, colour, mesh_list);
 }
 
 int FontEngineInterfaceBitmap::GetVersion(FontFaceHandle /*handle*/)

+ 4 - 3
Samples/basic/bitmapfont/src/FontEngineInterfaceBitmap.h

@@ -48,7 +48,8 @@ using Rml::Style::FontWeight;
 
 using Rml::FontEffectList;
 using Rml::FontMetrics;
-using Rml::GeometryList;
+using Rml::RenderManager;
+using Rml::TexturedMeshList;
 
 class FontEngineInterfaceBitmap : public Rml::FontEngineInterface {
 public:
@@ -76,8 +77,8 @@ public:
 	int GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character = Character::Null) override;
 
 	/// Called by RmlUi when it wants to retrieve the geometry required to render a single line of text.
-	int GenerateString(FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string, const Vector2f& position,
-		ColourbPremultiplied colour, float opacity, float letter_spacing, GeometryList& geometry) override;
+	int GenerateString(RenderManager& render_manager, FontFaceHandle face_handle, FontEffectsHandle font_effects_handle, const String& string,
+		const Vector2f& position, ColourbPremultiplied colour, float opacity, float letter_spacing, TexturedMeshList& mesh_list) override;
 
 	/// Called by RmlUi to determine if the text geometry is required to be re-generated.eometry.
 	int GetVersion(FontFaceHandle handle) override;

+ 9 - 8
Samples/invaders/src/DecoratorDefender.cpp

@@ -29,10 +29,11 @@
 #include "DecoratorDefender.h"
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/Geometry.h>
 #include <RmlUi/Core/Math.h>
+#include <RmlUi/Core/MeshUtilities.h>
 #include <RmlUi/Core/PropertyDefinition.h>
-#include <RmlUi/Core/RenderInterface.h>
+#include <RmlUi/Core/RenderManager.h>
 #include <RmlUi/Core/Texture.h>
 #include <RmlUi/Core/Types.h>
 
@@ -62,16 +63,16 @@ void DecoratorDefender::RenderElement(Rml::Element* element, Rml::DecoratorDataH
 	Rml::Vector2f size = element->GetBox().GetSize(Rml::BoxArea::Padding);
 	Rml::Math::SnapToPixelGrid(position, size);
 
-	if (Rml::RenderInterface* render_interface = ::Rml::GetRenderInterface())
+	if (Rml::RenderManager* render_manager = element->GetRenderManager())
 	{
-		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle();
+		Rml::Texture texture = GetTexture(image_index);
 		Rml::ColourbPremultiplied color = element->GetProperty<Rml::Colourb>("color").ToPremultiplied();
 
-		Rml::Vertex vertices[4];
-		int indices[6];
-		Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(0.f), size, color);
+		Rml::Mesh mesh;
+		Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(0.f), size, color);
 
-		render_interface->RenderGeometry(vertices, 4, indices, 6, texture, position);
+		Rml::Geometry geometry = render_manager->MakeGeometry(std::move(mesh));
+		geometry.Render(position, texture);
 	}
 }
 

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

@@ -32,12 +32,9 @@
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
 #include <RmlUi/Core/ElementUtilities.h>
-#include <RmlUi/Core/GeometryUtilities.h>
 #include <RmlUi/Core/Math.h>
 #include <RmlUi/Core/PropertyDefinition.h>
-#include <RmlUi/Core/RenderInterface.h>
 #include <RmlUi/Core/SystemInterface.h>
-#include <Shell.h>
 
 DecoratorStarfield::~DecoratorStarfield() {}
 
@@ -126,7 +123,8 @@ void DecoratorStarfield::RenderElement(Rml::Element* element, Rml::DecoratorData
 		}
 	}
 
-	DrawPoints(point_size, points);
+	if (Rml::RenderManager* render_manager = element->GetRenderManager())
+		DrawPoints(*render_manager, point_size, points);
 }
 
 void DecoratorStarfield::StarField::Update(double t)

+ 3 - 3
Samples/invaders/src/Defender.cpp

@@ -93,17 +93,17 @@ void Defender::Update(double t)
 	}
 }
 
-void Defender::Render(float dp_ratio, Rml::TextureHandle texture)
+void Defender::Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture)
 {
 	Rml::ColourbPremultiplied color = GameDetails::GetDefenderColour().ToPremultiplied();
 
 	// Render our sprite if rendering is enabled
 	if (render)
-		defender_sprite.Render(position, dp_ratio, color, texture);
+		defender_sprite.Render(render_manager, position, dp_ratio, color, texture);
 
 	// Render the bullet
 	if (bullet_in_flight)
-		bullet_sprite.Render(bullet_position, dp_ratio, color, texture);
+		bullet_sprite.Render(render_manager, bullet_position, dp_ratio, color, texture);
 }
 
 void Defender::StartMove(float direction)

+ 1 - 1
Samples/invaders/src/Defender.h

@@ -47,7 +47,7 @@ public:
 	/// Update the defender state.
 	void Update(double t);
 	/// Render the defender.
-	void Render(float dp_ratio, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture);
 
 	/// Move the defender left.
 	void StartMove(float direction);

+ 2 - 1
Samples/invaders/src/ElementGame.cpp

@@ -90,7 +90,8 @@ void ElementGame::OnUpdate()
 
 void ElementGame::OnRender()
 {
-	game->Render(GetContext()->GetDensityIndependentPixelRatio());
+	if (Rml::Context* context = GetContext())
+		game->Render(context->GetRenderManager(), context->GetDensityIndependentPixelRatio());
 }
 
 void ElementGame::OnChildAdd(Rml::Element* element)

+ 6 - 7
Samples/invaders/src/Game.cpp

@@ -79,8 +79,6 @@ Game::Game()
 	for (int i = 0; i < NUM_SHIELDS; i++)
 		shields[i] = nullptr;
 
-	texture.Set("invaders/data/invaders.tga");
-
 	defender = new Defender(this);
 }
 
@@ -139,22 +137,23 @@ void Game::Update(double t)
 	}
 }
 
-void Game::Render(float dp_ratio)
+void Game::Render(Rml::RenderManager& render_manager, float dp_ratio)
 {
 	if (defender_lives <= 0)
 		return;
 
-	Rml::TextureHandle texture_handle = texture.GetHandle();
+	if (!texture)
+		texture = render_manager.LoadTexture("invaders/data/invaders.tga");
 
 	// Render all available shields
 	for (int i = 0; i < NUM_SHIELDS; i++)
-		shields[i]->Render(dp_ratio);
+		shields[i]->Render(render_manager, dp_ratio);
 
 	// Render all available invaders
 	for (int i = 0; i < NUM_INVADERS + 1; i++)
-		invaders[i]->Render(dp_ratio, texture_handle);
+		invaders[i]->Render(render_manager, dp_ratio, texture);
 
-	defender->Render(dp_ratio, texture_handle);
+	defender->Render(render_manager, dp_ratio, texture);
 }
 
 Defender* Game::GetDefender()

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

@@ -57,7 +57,7 @@ public:
 	void Update(double t);
 
 	/// Render the game
-	void Render(float dp_ratio);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio);
 
 	/// Access the defender
 	Defender* GetDefender();

+ 3 - 3
Samples/invaders/src/Invader.cpp

@@ -171,7 +171,7 @@ void Invader::UpdateAnimation()
 	}
 }
 
-void Invader::Render(float dp_ratio, Rml::TextureHandle texture)
+void Invader::Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture)
 {
 	Rml::ColourbPremultiplied color(255);
 
@@ -182,10 +182,10 @@ void Invader::Render(float dp_ratio, Rml::TextureHandle texture)
 	int sprite_offset = int((invader_sprites[sprite_index].dimensions.x - 48) / 2);
 
 	if (state != DEAD)
-		invader_sprites[sprite_index].Render(Rml::Vector2f(position.x - sprite_offset, position.y), dp_ratio, color, texture);
+		invader_sprites[sprite_index].Render(render_manager, Rml::Vector2f(position.x - sprite_offset, position.y), dp_ratio, color, texture);
 
 	if (bomb != NONE)
-		bomb_sprites[bomb_animation_frame].Render(bomb_position, dp_ratio, color, texture);
+		bomb_sprites[bomb_animation_frame].Render(render_manager, bomb_position, dp_ratio, color, texture);
 }
 
 Invader::InvaderState Invader::GetState()

+ 1 - 1
Samples/invaders/src/Invader.h

@@ -59,7 +59,7 @@ public:
 	virtual void Update(double t);
 
 	/// Render the invader
-	void Render(float dp_ratio, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture);
 
 	/// Update the invaders animation
 	void UpdateAnimation();

+ 2 - 2
Samples/invaders/src/Shield.cpp

@@ -116,7 +116,7 @@ const Rml::Vector2f& Shield::GetPosition() const
 	return position;
 }
 
-void Shield::Render(float dp_ratio)
+void Shield::Render(Rml::RenderManager& render_manager, float dp_ratio)
 {
 	if (health > 0)
 	{
@@ -139,7 +139,7 @@ void Shield::Render(float dp_ratio)
 			}
 		}
 
-		DrawPoints((float)scaled_pixel, points);
+		DrawPoints(render_manager, (float)scaled_pixel, points);
 	}
 }
 

+ 1 - 1
Samples/invaders/src/Shield.h

@@ -61,7 +61,7 @@ public:
 	const Rml::Vector2f& GetPosition() const;
 
 	/// Render the shield.
-	void Render(float dp_ratio);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio);
 
 	/// Returns true if the position hits the shield
 	/// If a hit is detected, will degrade the shield.

+ 16 - 30
Samples/invaders/src/Sprite.cpp

@@ -28,9 +28,10 @@
 
 #include "Sprite.h"
 #include <RmlUi/Core/Core.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/Geometry.h>
 #include <RmlUi/Core/Math.h>
-#include <RmlUi/Core/RenderInterface.h>
+#include <RmlUi/Core/MeshUtilities.h>
+#include <RmlUi/Core/RenderManager.h>
 
 Sprite::Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_texcoord, const Rml::Vector2f& bottom_right_texcoord) :
 	dimensions(dimensions), top_left_texcoord(top_left_texcoord), bottom_right_texcoord(bottom_right_texcoord)
@@ -38,53 +39,38 @@ Sprite::Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_te
 
 Sprite::~Sprite() {}
 
-void Sprite::Render(Rml::Vector2f position, const float dp_ratio, Rml::ColourbPremultiplied color, Rml::TextureHandle texture)
+void Sprite::Render(Rml::RenderManager& render_manager, Rml::Vector2f position, const float dp_ratio, Rml::ColourbPremultiplied color,
+	Rml::Texture texture)
 {
-	Rml::RenderInterface* render_interface = Rml::GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	position = dp_ratio * position;
 	Rml::Vector2f dimensions_px = dp_ratio * dimensions;
 	Rml::Math::SnapToPixelGrid(position, dimensions_px);
 
-	Rml::Vertex vertices[4];
-	int indices[6];
-	Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(0.f), dimensions_px, color, top_left_texcoord, bottom_right_texcoord);
+	Rml::Mesh mesh;
+	Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(0.f), dimensions_px, color, top_left_texcoord, bottom_right_texcoord);
 
-	render_interface->RenderGeometry(vertices, 4, indices, 6, texture, position);
+	Rml::Geometry geometry = render_manager.MakeGeometry(std::move(mesh));
+	geometry.Render(position, texture);
 }
 
-void DrawPoints(float point_size, const ColoredPointList& points)
+void DrawPoints(Rml::RenderManager& render_manager, float point_size, const ColoredPointList& points)
 {
-	Rml::RenderInterface* render_interface = Rml::GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	constexpr int num_quad_vertices = 4;
 	constexpr int num_quad_indices = 6;
 
 	const int num_points = (int)points.size();
 
-	Rml::Vector<Rml::Vertex> vertices(num_points * num_quad_vertices);
-	Rml::Vector<int> indices(num_points * num_quad_indices);
-
-	int vertex_offset = 0;
-	int index_offset = 0;
+	Rml::Mesh mesh;
+	mesh.vertices.reserve(num_points * num_quad_vertices);
+	mesh.indices.reserve(num_points * num_quad_indices);
 
 	for (const ColoredPoint& point : points)
 	{
 		Rml::Vector2f position = point.position;
 		Rml::Vector2f size = Rml::Vector2f(point_size);
-		Rml::GeometryUtilities::GenerateQuad(vertices.data() + vertex_offset, indices.data() + index_offset, position, size, point.color,
-			vertex_offset);
-
-		vertex_offset += num_quad_vertices;
-		index_offset += num_quad_indices;
+		Rml::MeshUtilities::GenerateQuad(mesh, position, size, point.color);
 	}
 
-	RMLUI_ASSERT(vertex_offset == (int)vertices.size());
-	RMLUI_ASSERT(index_offset == (int)indices.size());
-
-	render_interface->RenderGeometry(vertices.data(), vertex_offset, indices.data(), index_offset, {}, Rml::Vector2f(0.f));
+	Rml::Geometry geometry = render_manager.MakeGeometry(std::move(mesh));
+	geometry.Render(Rml::Vector2f(0.f));
 }

+ 4 - 2
Samples/invaders/src/Sprite.h

@@ -29,6 +29,7 @@
 #ifndef RMLUI_INVADERS_SPRITE_H
 #define RMLUI_INVADERS_SPRITE_H
 
+#include <RmlUi/Core/Texture.h>
 #include <RmlUi/Core/Types.h>
 
 /**
@@ -40,7 +41,8 @@ public:
 	Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_texcoord, const Rml::Vector2f& bottom_right_texcoord);
 	~Sprite();
 
-	void Render(Rml::Vector2f position, float dp_ratio, Rml::ColourbPremultiplied color, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, Rml::Vector2f position, float dp_ratio, Rml::ColourbPremultiplied color,
+		Rml::Texture texture);
 
 	Rml::Vector2f dimensions;
 	Rml::Vector2f top_left_texcoord;
@@ -53,6 +55,6 @@ struct ColoredPoint {
 };
 using ColoredPointList = Rml::Vector<ColoredPoint>;
 
-void DrawPoints(float point_size, const ColoredPointList& points);
+void DrawPoints(Rml::RenderManager& render_manager, float point_size, const ColoredPointList& points);
 
 #endif

+ 9 - 8
Samples/luainvaders/src/DecoratorDefender.cpp

@@ -29,10 +29,11 @@
 #include "DecoratorDefender.h"
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/Geometry.h>
+#include <RmlUi/Core/MeshUtilities.h>
 #include <RmlUi/Core/Math.h>
 #include <RmlUi/Core/PropertyDefinition.h>
-#include <RmlUi/Core/RenderInterface.h>
+#include <RmlUi/Core/RenderManager.h>
 #include <RmlUi/Core/Texture.h>
 #include <RmlUi/Core/Types.h>
 
@@ -62,16 +63,16 @@ void DecoratorDefender::RenderElement(Rml::Element* element, Rml::DecoratorDataH
 	Rml::Vector2f size = element->GetBox().GetSize(Rml::BoxArea::Padding);
 	Rml::Math::SnapToPixelGrid(position, size);
 
-	if (Rml::RenderInterface* render_interface = ::Rml::GetRenderInterface())
+	if (Rml::RenderManager* render_manager = element->GetRenderManager())
 	{
-		Rml::TextureHandle texture = GetTexture(image_index)->GetHandle();
+		Rml::Texture texture = GetTexture(image_index);
 		Rml::ColourbPremultiplied color = element->GetProperty<Rml::Colourb>("color").ToPremultiplied();
 
-		Rml::Vertex vertices[4];
-		int indices[6];
-		Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(0.f), size, color);
+		Rml::Mesh mesh;
+		Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(0.f), size, color);
 
-		render_interface->RenderGeometry(vertices, 4, indices, 6, texture, position);
+		Rml::Geometry geometry = render_manager->MakeGeometry(std::move(mesh));
+		geometry.Render(position, texture);
 	}
 }
 

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

@@ -32,12 +32,9 @@
 #include <RmlUi/Core/Core.h>
 #include <RmlUi/Core/Element.h>
 #include <RmlUi/Core/ElementUtilities.h>
-#include <RmlUi/Core/GeometryUtilities.h>
 #include <RmlUi/Core/Math.h>
 #include <RmlUi/Core/PropertyDefinition.h>
-#include <RmlUi/Core/RenderInterface.h>
 #include <RmlUi/Core/SystemInterface.h>
-#include <Shell.h>
 
 DecoratorStarfield::~DecoratorStarfield() {}
 
@@ -126,7 +123,8 @@ void DecoratorStarfield::RenderElement(Rml::Element* element, Rml::DecoratorData
 		}
 	}
 
-	DrawPoints(point_size, points);
+	if (Rml::RenderManager* render_manager = element->GetRenderManager())
+		DrawPoints(*render_manager, point_size, points);
 }
 
 void DecoratorStarfield::StarField::Update(double t)

+ 3 - 3
Samples/luainvaders/src/Defender.cpp

@@ -93,17 +93,17 @@ void Defender::Update(double t)
 	}
 }
 
-void Defender::Render(float dp_ratio, Rml::TextureHandle texture)
+void Defender::Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture)
 {
 	Rml::ColourbPremultiplied color = GameDetails::GetDefenderColour().ToPremultiplied();
 
 	// Render our sprite if rendering is enabled
 	if (render)
-		defender_sprite.Render(position, dp_ratio, color, texture);
+		defender_sprite.Render(render_manager, position, dp_ratio, color, texture);
 
 	// Render the bullet
 	if (bullet_in_flight)
-		bullet_sprite.Render(bullet_position, dp_ratio, color, texture);
+		bullet_sprite.Render(render_manager, bullet_position, dp_ratio, color, texture);
 }
 
 void Defender::StartMove(float direction)

+ 1 - 1
Samples/luainvaders/src/Defender.h

@@ -47,7 +47,7 @@ public:
 	/// Update the defender state.
 	void Update(double t);
 	/// Render the defender.
-	void Render(float dp_ratio, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture);
 
 	/// Move the defender left.
 	void StartMove(float direction);

+ 2 - 1
Samples/luainvaders/src/ElementGame.cpp

@@ -88,7 +88,8 @@ void ElementGame::OnUpdate()
 
 void ElementGame::OnRender()
 {
-	game->Render(GetContext()->GetDensityIndependentPixelRatio());
+	if (Rml::Context* context = GetContext())
+		game->Render(context->GetRenderManager(), context->GetDensityIndependentPixelRatio());
 }
 
 void ElementGame::OnChildAdd(Rml::Element* element)

+ 6 - 7
Samples/luainvaders/src/Game.cpp

@@ -79,8 +79,6 @@ Game::Game()
 	for (int i = 0; i < NUM_SHIELDS; i++)
 		shields[i] = nullptr;
 
-	texture.Set("luainvaders/data/invaders.tga");
-
 	defender = new Defender(this);
 }
 
@@ -137,22 +135,23 @@ void Game::Update(double t)
 	}
 }
 
-void Game::Render(float dp_ratio)
+void Game::Render(Rml::RenderManager& render_manager, float dp_ratio)
 {
 	if (defender_lives <= 0)
 		return;
 
-	Rml::TextureHandle texture_handle = texture.GetHandle();
+	if (!texture)
+		texture = render_manager.LoadTexture("luainvaders/data/invaders.tga");
 
 	// Render all available shields
 	for (int i = 0; i < NUM_SHIELDS; i++)
-		shields[i]->Render(dp_ratio);
+		shields[i]->Render(render_manager, dp_ratio);
 
 	// Render all available invaders
 	for (int i = 0; i < NUM_INVADERS + 1; i++)
-		invaders[i]->Render(dp_ratio, texture_handle);
+		invaders[i]->Render(render_manager, dp_ratio, texture);
 
-	defender->Render(dp_ratio, texture_handle);
+	defender->Render(render_manager, dp_ratio, texture);
 }
 
 Defender* Game::GetDefender()

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

@@ -57,7 +57,7 @@ public:
 	void Update(double t);
 
 	/// Render the game
-	void Render(float dp_ratio);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio);
 
 	/// Access the defender
 	Defender* GetDefender();

+ 3 - 3
Samples/luainvaders/src/Invader.cpp

@@ -171,7 +171,7 @@ void Invader::UpdateAnimation()
 	}
 }
 
-void Invader::Render(float dp_ratio, Rml::TextureHandle texture)
+void Invader::Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture)
 {
 	Rml::ColourbPremultiplied color(255);
 
@@ -182,10 +182,10 @@ void Invader::Render(float dp_ratio, Rml::TextureHandle texture)
 	int sprite_offset = int((invader_sprites[sprite_index].dimensions.x - 48) / 2);
 
 	if (state != DEAD)
-		invader_sprites[sprite_index].Render(Rml::Vector2f(position.x - sprite_offset, position.y), dp_ratio, color, texture);
+		invader_sprites[sprite_index].Render(render_manager, Rml::Vector2f(position.x - sprite_offset, position.y), dp_ratio, color, texture);
 
 	if (bomb != NONE)
-		bomb_sprites[bomb_animation_frame].Render(bomb_position, dp_ratio, color, texture);
+		bomb_sprites[bomb_animation_frame].Render(render_manager, bomb_position, dp_ratio, color, texture);
 }
 
 Invader::InvaderState Invader::GetState()

+ 1 - 1
Samples/luainvaders/src/Invader.h

@@ -59,7 +59,7 @@ public:
 	virtual void Update(double t);
 
 	/// Render the invader
-	void Render(float dp_ratio, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio, Rml::Texture texture);
 
 	/// Update the invaders animation
 	void UpdateAnimation();

+ 2 - 2
Samples/luainvaders/src/Shield.cpp

@@ -116,7 +116,7 @@ const Rml::Vector2f& Shield::GetPosition() const
 	return position;
 }
 
-void Shield::Render(float dp_ratio)
+void Shield::Render(Rml::RenderManager& render_manager, float dp_ratio)
 {
 	if (health > 0)
 	{
@@ -139,7 +139,7 @@ void Shield::Render(float dp_ratio)
 			}
 		}
 
-		DrawPoints((float)scaled_pixel, points);
+		DrawPoints(render_manager, (float)scaled_pixel, points);
 	}
 }
 

+ 1 - 1
Samples/luainvaders/src/Shield.h

@@ -61,7 +61,7 @@ public:
 	const Rml::Vector2f& GetPosition() const;
 
 	/// Render the shield.
-	void Render(float dp_ratio);
+	void Render(Rml::RenderManager& render_manager, float dp_ratio);
 
 	/// Returns true if the position hits the shield
 	/// If a hit is detected, will degrade the shield.

+ 16 - 30
Samples/luainvaders/src/Sprite.cpp

@@ -28,9 +28,10 @@
 
 #include "Sprite.h"
 #include <RmlUi/Core/Core.h>
-#include <RmlUi/Core/GeometryUtilities.h>
+#include <RmlUi/Core/Geometry.h>
 #include <RmlUi/Core/Math.h>
-#include <RmlUi/Core/RenderInterface.h>
+#include <RmlUi/Core/MeshUtilities.h>
+#include <RmlUi/Core/RenderManager.h>
 
 Sprite::Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_texcoord, const Rml::Vector2f& bottom_right_texcoord) :
 	dimensions(dimensions), top_left_texcoord(top_left_texcoord), bottom_right_texcoord(bottom_right_texcoord)
@@ -38,53 +39,38 @@ Sprite::Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_te
 
 Sprite::~Sprite() {}
 
-void Sprite::Render(Rml::Vector2f position, const float dp_ratio, Rml::ColourbPremultiplied color, Rml::TextureHandle texture)
+void Sprite::Render(Rml::RenderManager& render_manager, Rml::Vector2f position, const float dp_ratio, Rml::ColourbPremultiplied color,
+	Rml::Texture texture)
 {
-	Rml::RenderInterface* render_interface = Rml::GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	position = dp_ratio * position;
 	Rml::Vector2f dimensions_px = dp_ratio * dimensions;
 	Rml::Math::SnapToPixelGrid(position, dimensions_px);
 
-	Rml::Vertex vertices[4];
-	int indices[6];
-	Rml::GeometryUtilities::GenerateQuad(vertices, indices, Rml::Vector2f(0.f), dimensions_px, color, top_left_texcoord, bottom_right_texcoord);
+	Rml::Mesh mesh;
+	Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(0.f), dimensions_px, color, top_left_texcoord, bottom_right_texcoord);
 
-	render_interface->RenderGeometry(vertices, 4, indices, 6, texture, position);
+	Rml::Geometry geometry = render_manager.MakeGeometry(std::move(mesh));
+	geometry.Render(position, texture);
 }
 
-void DrawPoints(float point_size, const ColoredPointList& points)
+void DrawPoints(Rml::RenderManager& render_manager, float point_size, const ColoredPointList& points)
 {
-	Rml::RenderInterface* render_interface = Rml::GetRenderInterface();
-	if (!render_interface)
-		return;
-
 	constexpr int num_quad_vertices = 4;
 	constexpr int num_quad_indices = 6;
 
 	const int num_points = (int)points.size();
 
-	Rml::Vector<Rml::Vertex> vertices(num_points * num_quad_vertices);
-	Rml::Vector<int> indices(num_points * num_quad_indices);
-
-	int vertex_offset = 0;
-	int index_offset = 0;
+	Rml::Mesh mesh;
+	mesh.vertices.reserve(num_points * num_quad_vertices);
+	mesh.indices.reserve(num_points * num_quad_indices);
 
 	for (const ColoredPoint& point : points)
 	{
 		Rml::Vector2f position = point.position;
 		Rml::Vector2f size = Rml::Vector2f(point_size);
-		Rml::GeometryUtilities::GenerateQuad(vertices.data() + vertex_offset, indices.data() + index_offset, position, size, point.color,
-			vertex_offset);
-
-		vertex_offset += num_quad_vertices;
-		index_offset += num_quad_indices;
+		Rml::MeshUtilities::GenerateQuad(mesh, position, size, point.color);
 	}
 
-	RMLUI_ASSERT(vertex_offset == (int)vertices.size());
-	RMLUI_ASSERT(index_offset == (int)indices.size());
-
-	render_interface->RenderGeometry(vertices.data(), vertex_offset, indices.data(), index_offset, {}, Rml::Vector2f(0.f));
+	Rml::Geometry geometry = render_manager.MakeGeometry(std::move(mesh));
+	geometry.Render(Rml::Vector2f(0.f));
 }

+ 3 - 2
Samples/luainvaders/src/Sprite.h

@@ -29,6 +29,7 @@
 #ifndef RMLUI_LUAINVADERS_SPRITE_H
 #define RMLUI_LUAINVADERS_SPRITE_H
 
+#include <RmlUi/Core/Texture.h>
 #include <RmlUi/Core/Types.h>
 
 /**
@@ -40,7 +41,7 @@ public:
 	Sprite(const Rml::Vector2f& dimensions, const Rml::Vector2f& top_left_texcoord, const Rml::Vector2f& bottom_right_texcoord);
 	~Sprite();
 
-	void Render(Rml::Vector2f position, float dp_ratio, Rml::ColourbPremultiplied color, Rml::TextureHandle texture);
+	void Render(Rml::RenderManager& render_manager, Rml::Vector2f position, float dp_ratio, Rml::ColourbPremultiplied color, Rml::Texture texture);
 
 	Rml::Vector2f dimensions;
 	Rml::Vector2f top_left_texcoord;
@@ -53,6 +54,6 @@ struct ColoredPoint {
 };
 using ColoredPointList = Rml::Vector<ColoredPoint>;
 
-void DrawPoints(float point_size, const ColoredPointList& points);
+void DrawPoints(Rml::RenderManager& render_manager, float point_size, const ColoredPointList& points);
 
 #endif

+ 108 - 0
Source/Core/CallbackTexture.cpp

@@ -0,0 +1,108 @@
+/*
+ * 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-2023 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 "../../Include/RmlUi/Core/CallbackTexture.h"
+#include "../../Include/RmlUi/Core/Texture.h"
+#include "RenderManagerAccess.h"
+
+namespace Rml {
+
+void CallbackTexture::Release()
+{
+	if (resource_handle != StableVectorIndex::Invalid)
+	{
+		RenderManagerAccess::ReleaseResource(render_manager, *this);
+		Clear();
+	}
+}
+
+Rml::CallbackTexture::operator Texture() const
+{
+	return Texture(render_manager, resource_handle);
+}
+
+CallbackTextureInterface::CallbackTextureInterface(RenderManager& render_manager, RenderInterface& render_interface, TextureHandle& texture_handle,
+	Vector2i& dimensions) :
+	render_manager(render_manager),
+	render_interface(render_interface), texture_handle(texture_handle), dimensions(dimensions)
+{}
+
+bool CallbackTextureInterface::GenerateTexture(const byte* source, Vector2i new_dimensions) const
+{
+	if (texture_handle)
+	{
+		RMLUI_ERRORMSG("Texture already set");
+		return false;
+	}
+	const bool result = render_interface.GenerateTexture(texture_handle, source, new_dimensions);
+	if (result)
+		dimensions = new_dimensions;
+	else
+		texture_handle = {};
+	return result;
+}
+
+void CallbackTextureInterface::SaveLayerAsTexture(Vector2i new_dimensions) const
+{
+	if (texture_handle)
+	{
+		RMLUI_ERRORMSG("Texture already set");
+		return;
+	}
+	texture_handle = render_interface.SaveLayerAsTexture(new_dimensions);
+	if (texture_handle)
+		dimensions = new_dimensions;
+}
+
+RenderManager& CallbackTextureInterface::GetRenderManager() const
+{
+	return render_manager;
+}
+
+CallbackTextureSource::CallbackTextureSource(CallbackTextureFunction&& callback) : callback(std::move(callback)) {}
+
+CallbackTextureSource::CallbackTextureSource(CallbackTextureSource&& other) noexcept :
+	callback(std::move(other.callback)), textures(std::move(other.textures))
+{}
+
+CallbackTextureSource& CallbackTextureSource::operator=(CallbackTextureSource&& other) noexcept
+{
+	callback = std::move(other.callback);
+	textures = std::move(other.textures);
+	return *this;
+}
+
+Texture CallbackTextureSource::GetTexture(RenderManager& render_manager) const
+{
+	CallbackTexture& texture = textures[&render_manager];
+	if (!texture)
+		texture = render_manager.MakeCallbackTexture(callback);
+	return Texture(texture);
+}
+
+} // namespace Rml

+ 60 - 0
Source/Core/CompiledFilterShader.cpp

@@ -0,0 +1,60 @@
+/*
+ * 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-2023 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 "../../Include/RmlUi/Core/CompiledFilterShader.h"
+#include "RenderManagerAccess.h"
+
+namespace Rml {
+
+void CompiledFilter::AddHandleTo(FilterHandleList& list)
+{
+	if (resource_handle != InvalidHandle())
+	{
+		list.push_back(resource_handle);
+	}
+}
+
+void CompiledFilter::Release()
+{
+	if (resource_handle != InvalidHandle())
+	{
+		RenderManagerAccess::ReleaseResource(render_manager, *this);
+		Clear();
+	}
+}
+
+void CompiledShader::Release()
+{
+	if (resource_handle != InvalidHandle())
+	{
+		RenderManagerAccess::ReleaseResource(render_manager, *this);
+		Clear();
+	}
+}
+
+} // namespace Rml

+ 6 - 7
Source/Core/Context.cpp

@@ -35,7 +35,7 @@
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../Include/RmlUi/Core/Factory.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "../../Include/RmlUi/Core/StreamMemory.h"
 #include "../../Include/RmlUi/Core/SystemInterface.h"
 #include "DataModel.h"
@@ -54,8 +54,7 @@ static constexpr float DOUBLE_CLICK_TIME = 0.5f;    // [s]
 static constexpr float DOUBLE_CLICK_MAX_DIST = 3.f; // [dp]
 static constexpr float UNIT_SCROLL_LENGTH = 80.f;   // [dp]
 
-Context::Context(const String& name) :
-	name(name), dimensions(0, 0), density_independent_pixel_ratio(1.0f), mouse_position(0, 0), next_update_timeout(0)
+Context::Context(const String& name, RenderManager* render_manager) : name(name), render_manager(render_manager)
 {
 	instancer = nullptr;
 
@@ -125,7 +124,7 @@ void Context::SetDimensions(const Vector2i _dimensions)
 	if (dimensions != _dimensions)
 	{
 		dimensions = _dimensions;
-		render_manager.SetViewport(dimensions);
+		render_manager->SetViewport(dimensions);
 		root->SetBox(Box(Vector2f(dimensions)));
 		root->DirtyLayout();
 
@@ -216,7 +215,7 @@ bool Context::Render()
 {
 	RMLUI_ZoneScoped;
 
-	render_manager.BeginRender();
+	render_manager->PrepareRender();
 
 	root->Render();
 
@@ -229,7 +228,7 @@ bool Context::Render()
 		cursor_proxy->Render();
 	}
 
-	render_manager.ResetState();
+	render_manager->ResetState();
 
 	return true;
 }
@@ -855,7 +854,7 @@ void Context::SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float spe
 
 RenderManager& Context::GetRenderManager()
 {
-	return render_manager;
+	return *render_manager;
 }
 
 void Context::SetInstancer(ContextInstancer* _instancer)

+ 2 - 2
Source/Core/ContextInstancerDefault.cpp

@@ -35,9 +35,9 @@ ContextInstancerDefault::ContextInstancerDefault() {}
 
 ContextInstancerDefault::~ContextInstancerDefault() {}
 
-ContextPtr ContextInstancerDefault::InstanceContext(const String& name)
+ContextPtr ContextInstancerDefault::InstanceContext(const String& name, RenderManager* render_manager)
 {
-	return ContextPtr(new Context(name));
+	return ContextPtr(new Context(name, render_manager));
 }
 
 void ContextInstancerDefault::ReleaseContext(Context* context)

+ 1 - 4
Source/Core/ContextInstancerDefault.h

@@ -45,12 +45,9 @@ public:
 	virtual ~ContextInstancerDefault();
 
 	/// Instances a context.
-	/// @param[in] name Name of this context.
-	/// @return The instanced context.
-	ContextPtr InstanceContext(const String& name) override;
+	ContextPtr InstanceContext(const String& name, RenderManager* render_manager) override;
 
 	/// Releases a context previously created by this context.
-	/// @param[in] context The context to release.
 	void ReleaseContext(Context* context) override;
 
 	/// Releases this context instancer.

+ 66 - 28
Source/Core/Core.cpp

@@ -34,13 +34,14 @@
 #include "../../Include/RmlUi/Core/FontEngineInterface.h"
 #include "../../Include/RmlUi/Core/Plugin.h"
 #include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "../../Include/RmlUi/Core/StyleSheetSpecification.h"
 #include "../../Include/RmlUi/Core/SystemInterface.h"
 #include "../../Include/RmlUi/Core/Types.h"
 #include "EventSpecification.h"
 #include "FileInterfaceDefault.h"
-#include "GeometryDatabase.h"
 #include "PluginRegistry.h"
+#include "RenderManagerAccess.h"
 #include "StyleSheetFactory.h"
 #include "StyleSheetParser.h"
 #include "TemplateCache.h"
@@ -75,6 +76,8 @@ static FontEngineInterface* font_interface = nullptr;
 static UniquePtr<FileInterface> default_file_interface;
 static UniquePtr<FontEngineInterface> default_font_interface;
 
+static UniquePtr<SmallUnorderedMap<RenderInterface*, UniquePtr<RenderManager>>> render_managers;
+
 static bool initialised = false;
 
 using ContextMap = UnorderedMap<String, ContextPtr>;
@@ -99,11 +102,6 @@ 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)
 	{
@@ -118,7 +116,9 @@ bool Initialise()
 
 	EventSpecificationInterface::Initialize();
 
-	TextureDatabase::Initialise();
+	render_managers = MakeUnique<SmallUnorderedMap<RenderInterface*, UniquePtr<RenderManager>>>();
+	if (render_interface)
+		(*render_managers)[render_interface] = MakeUnique<RenderManager>(render_interface);
 
 	if (!font_interface)
 	{
@@ -174,7 +174,7 @@ void Shutdown()
 	font_interface = nullptr;
 	default_font_interface.reset();
 
-	TextureDatabase::Shutdown();
+	render_managers.reset();
 
 	initialised = false;
 
@@ -207,12 +207,6 @@ 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;
 }
 
@@ -241,18 +235,33 @@ FontEngineInterface* GetFontEngineInterface()
 	return font_interface;
 }
 
-Context* CreateContext(const String& name, const Vector2i dimensions)
+Context* CreateContext(const String& name, const Vector2i dimensions, RenderInterface* render_interface_for_context)
 {
 	if (!initialised)
 		return nullptr;
 
+	if (!render_interface_for_context)
+		render_interface_for_context = render_interface;
+
+	if (!render_interface_for_context)
+	{
+		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());
 		return nullptr;
 	}
 
-	ContextPtr new_context = Factory::InstanceContext(name);
+	// Each unique render interface gets its own render manager.
+	auto& render_manager = (*render_managers)[render_interface_for_context];
+	if (!render_manager)
+		render_manager = MakeUnique<RenderManager>(render_interface_for_context);
+
+	ContextPtr new_context = Factory::InstanceContext(name, render_manager.get());
 	if (!new_context)
 	{
 		Log::Message(Log::LT_WARNING, "Failed to instance context '%s', instancer returned nullptr.", name.c_str());
@@ -347,25 +356,35 @@ EventId RegisterEventType(const String& type, bool interruptible, bool bubbles,
 
 StringList GetTextureSourceList()
 {
-	return TextureDatabase::GetSourceList();
-}
-
-void ReleaseTextures()
-{
-	TextureDatabase::ReleaseTextures();
+	StringList result;
+	if (!render_managers)
+		return result;
+	for (const auto& render_manager : *render_managers)
+	{
+		RenderManagerAccess::GetTextureSourceList(render_manager.second.get(), result);
+	}
+	return result;
 }
 
-void ReleaseCompiledGeometry()
+void ReleaseTextures(RenderInterface* match_render_interface)
 {
-	return GeometryDatabase::ReleaseAll();
+	if (!render_managers)
+		return;
+	for (auto& render_manager : *render_managers)
+	{
+		if (!match_render_interface || render_manager.first == match_render_interface)
+			RenderManagerAccess::ReleaseAllTextures(render_manager.second.get());
+	}
 }
 
-void ReleaseMemoryPools()
+void ReleaseCompiledGeometry(RenderInterface* match_render_interface)
 {
-	if (observerPtrBlockPool && observerPtrBlockPool->GetNumAllocatedObjects() <= 0)
+	if (!render_managers)
+		return;
+	for (auto& render_manager : *render_managers)
 	{
-		delete observerPtrBlockPool;
-		observerPtrBlockPool = nullptr;
+		if (!match_render_interface || render_manager.first == match_render_interface)
+			RenderManagerAccess::ReleaseAllCompiledGeometry(render_manager.second.get());
 	}
 }
 
@@ -383,4 +402,23 @@ void ReleaseFontResources()
 	}
 }
 
+void ReleaseMemoryPools()
+{
+	if (observerPtrBlockPool && observerPtrBlockPool->GetNumAllocatedObjects() <= 0)
+	{
+		delete observerPtrBlockPool;
+		observerPtrBlockPool = nullptr;
+	}
+}
+
+// Functions that need to be accessible within the Core library, but not publicly.
+namespace CoreInternal {
+
+	bool HasRenderManager(RenderInterface* match_render_interface)
+	{
+		return render_managers && (*render_managers).find(match_render_interface) != render_managers->end();
+	}
+
+} // namespace CoreInternal
+
 } // namespace Rml

+ 13 - 10
Source/Core/Decorator.cpp

@@ -28,6 +28,7 @@
 
 #include "../../Include/RmlUi/Core/Decorator.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "../../Include/RmlUi/Core/StyleSheet.h"
 #include "../../Include/RmlUi/Core/Texture.h"
 #include <algorithm>
@@ -38,7 +39,7 @@ Decorator::Decorator() {}
 
 Decorator::~Decorator() {}
 
-int Decorator::AddTexture(const Texture& texture)
+int Decorator::AddTexture(Texture texture)
 {
 	if (!texture)
 		return -1;
@@ -64,16 +65,16 @@ int Decorator::GetNumTextures() const
 	return result;
 }
 
-const Texture* Decorator::GetTexture(int index) const
+Texture Decorator::GetTexture(int index) const
 {
 	if (index == 0)
-		return &first_texture;
+		return first_texture;
 
 	index -= 1;
 	if (index < 0 || index >= (int)additional_textures.size())
-		return nullptr;
+		return {};
 
-	return &(additional_textures[index]);
+	return additional_textures[index];
 }
 
 DecoratorInstancer::DecoratorInstancer() {}
@@ -87,16 +88,18 @@ const Sprite* DecoratorInstancerInterface::GetSprite(const String& name) const
 
 Texture DecoratorInstancerInterface::GetTexture(const String& filename) const
 {
-	Texture texture;
-
 	if (!property_source)
 	{
 		Log::Message(Log::LT_WARNING, "Texture name '%s' in decorator could not be loaded, no property source available.", filename.c_str());
-		return texture;
+		return {};
 	}
 
-	texture.Set(filename, property_source->path);
+	return render_manager.LoadTexture(filename, property_source->path);
+}
 
-	return texture;
+RenderManager& DecoratorInstancerInterface::GetRenderManager() const
+{
+	return render_manager;
 }
+
 } // namespace Rml

+ 38 - 37
Source/Core/DecoratorGradient.cpp

@@ -31,8 +31,8 @@
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
 #include "../../Include/RmlUi/Core/Math.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "ComputeProperty.h"
 #include "DecoratorShader.h"
@@ -189,13 +189,13 @@ bool DecoratorStraightGradient::Initialise(const Direction in_direction, const C
 
 DecoratorDataHandle DecoratorStraightGradient::GenerateElementData(Element* element, BoxArea paint_area) const
 {
-	Geometry* geometry = new Geometry();
 	const Box& box = element->GetBox();
 
 	const ComputedValues& computed = element->GetComputedValues();
 	const float opacity = computed.opacity();
 
-	GeometryUtilities::GenerateBackground(geometry, element->GetBox(), Vector2f(0), computed.border_radius(), ColourbPremultiplied(), paint_area);
+	Mesh mesh;
+	MeshUtilities::GenerateBackground(mesh, element->GetBox(), Vector2f(0), computed.border_radius(), ColourbPremultiplied(), paint_area);
 
 	ColourbPremultiplied colour_start = start.ToPremultiplied(opacity);
 	ColourbPremultiplied colour_stop = stop.ToPremultiplied(opacity);
@@ -203,7 +203,7 @@ DecoratorDataHandle DecoratorStraightGradient::GenerateElementData(Element* elem
 	const Vector2f offset = box.GetPosition(paint_area);
 	const Vector2f size = box.GetSize(paint_area);
 
-	Vector<Vertex>& vertices = geometry->GetVertices();
+	Vector<Vertex>& vertices = mesh.vertices;
 
 	if (direction == Direction::Horizontal)
 	{
@@ -222,6 +222,8 @@ DecoratorDataHandle DecoratorStraightGradient::GenerateElementData(Element* elem
 		}
 	}
 
+	Geometry* geometry = new Geometry(element->GetRenderManager()->MakeGeometry(std::move(mesh)));
+
 	return reinterpret_cast<DecoratorDataHandle>(geometry);
 }
 
@@ -288,8 +290,8 @@ bool DecoratorLinearGradient::Initialise(bool in_repeating, Corner in_corner, fl
 
 DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* element, BoxArea paint_area) const
 {
-	RenderInterface* render_interface = GetRenderInterface();
-	if (!render_interface)
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
 		return INVALID_DECORATORDATAHANDLE;
 
 	RMLUI_ASSERT(!color_stops.empty());
@@ -304,7 +306,7 @@ DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* elemen
 
 	ColorStopList resolved_stops = ResolveColorStops(element, gradient_shape.length, soft_spacing, color_stops);
 
-	CompiledShaderHandle shader_handle = render_interface->CompileShader("linear-gradient",
+	CompiledShader shader = render_manager->CompileShader("linear-gradient",
 		Dictionary{
 			{"angle", Variant(angle)},
 			{"p0", Variant(gradient_shape.p0)},
@@ -313,21 +315,20 @@ DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* elemen
 			{"repeating", Variant(repeating)},
 			{"color_stop_list", Variant(std::move(resolved_stops))},
 		});
-
-	if (!shader_handle)
+	if (!shader)
 		return INVALID_DECORATORDATAHANDLE;
 
-	Geometry geometry;
-
+	Mesh mesh;
 	const ComputedValues& computed = element->GetComputedValues();
 	const byte alpha = byte(computed.opacity() * 255.f);
-	GeometryUtilities::GenerateBackground(&geometry, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), paint_area);
+	MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), paint_area);
 
 	const Vector2f render_offset = box.GetPosition(paint_area);
-	for (Vertex& vertex : geometry.GetVertices())
+	for (Vertex& vertex : mesh.vertices)
 		vertex.tex_coord = vertex.position - render_offset;
 
-	ShaderElementData* element_data = GetShaderElementDataPool().AllocateAndConstruct(std::move(geometry), shader_handle);
+	ShaderElementData* element_data =
+		GetShaderElementDataPool().AllocateAndConstruct(render_manager->MakeGeometry(std::move(mesh)), std::move(shader));
 
 	return reinterpret_cast<DecoratorDataHandle>(element_data);
 }
@@ -335,14 +336,13 @@ DecoratorDataHandle DecoratorLinearGradient::GenerateElementData(Element* elemen
 void DecoratorLinearGradient::ReleaseElementData(DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	GetRenderInterface()->ReleaseCompiledShader(element_data->shader);
 	GetShaderElementDataPool().DestroyAndDeallocate(element_data);
 }
 
 void DecoratorLinearGradient::RenderElement(Element* element, DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	element_data->geometry.RenderWithShader(element_data->shader, element->GetAbsoluteOffset(BoxArea::Border));
+	element_data->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), {}, element_data->shader);
 }
 
 DecoratorLinearGradient::LinearGradientShape DecoratorLinearGradient::CalculateShape(Vector2f dim) const
@@ -461,8 +461,8 @@ bool DecoratorRadialGradient::Initialise(bool in_repeating, Shape in_shape, Size
 
 DecoratorDataHandle DecoratorRadialGradient::GenerateElementData(Element* element, BoxArea box_area) const
 {
-	RenderInterface* render_interface = GetRenderInterface();
-	if (!render_interface)
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
 		return INVALID_DECORATORDATAHANDLE;
 
 	RMLUI_ASSERT(!color_stops.empty() && (shape == Shape::Circle || shape == Shape::Ellipse));
@@ -477,39 +477,40 @@ DecoratorDataHandle DecoratorRadialGradient::GenerateElementData(Element* elemen
 
 	ColorStopList resolved_stops = ResolveColorStops(element, gradient_shape.radius.x, soft_spacing, color_stops);
 
-	CompiledShaderHandle shader_handle = render_interface->CompileShader("radial-gradient",
+	CompiledShader shader = render_manager->CompileShader("radial-gradient",
 		Dictionary{
 			{"center", Variant(gradient_shape.center)},
 			{"radius", Variant(gradient_shape.radius)},
 			{"repeating", Variant(repeating)},
 			{"color_stop_list", Variant(std::move(resolved_stops))},
 		});
+	if (!shader)
+		return INVALID_DECORATORDATAHANDLE;
 
-	Geometry geometry;
-
+	Mesh mesh;
 	const ComputedValues& computed = element->GetComputedValues();
 	const byte alpha = byte(computed.opacity() * 255.f);
-	GeometryUtilities::GenerateBackground(&geometry, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
+	MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
 
 	const Vector2f render_offset = box.GetPosition(box_area);
-	for (Vertex& vertex : geometry.GetVertices())
+	for (Vertex& vertex : mesh.vertices)
 		vertex.tex_coord = vertex.position - render_offset;
 
-	ShaderElementData* element_data = GetShaderElementDataPool().AllocateAndConstruct(std::move(geometry), shader_handle);
+	ShaderElementData* element_data =
+		GetShaderElementDataPool().AllocateAndConstruct(render_manager->MakeGeometry(std::move(mesh)), std::move(shader));
 	return reinterpret_cast<DecoratorDataHandle>(element_data);
 }
 
 void DecoratorRadialGradient::ReleaseElementData(DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	GetRenderInterface()->ReleaseCompiledShader(element_data->shader);
 	GetShaderElementDataPool().DestroyAndDeallocate(element_data);
 }
 
 void DecoratorRadialGradient::RenderElement(Element* element, DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	element_data->geometry.RenderWithShader(element_data->shader, element->GetAbsoluteOffset(BoxArea::Border));
+	element_data->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), {}, element_data->shader);
 }
 
 DecoratorRadialGradient::RadialGradientShape DecoratorRadialGradient::CalculateRadialGradientShape(Element* element, Vector2f dimensions) const
@@ -659,8 +660,8 @@ bool DecoratorConicGradient::Initialise(bool in_repeating, float in_angle, Vecto
 
 DecoratorDataHandle DecoratorConicGradient::GenerateElementData(Element* element, BoxArea box_area) const
 {
-	RenderInterface* render_interface = GetRenderInterface();
-	if (!render_interface)
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
 		return INVALID_DECORATORDATAHANDLE;
 
 	RMLUI_ASSERT(!color_stops.empty());
@@ -673,40 +674,40 @@ DecoratorDataHandle DecoratorConicGradient::GenerateElementData(Element* element
 
 	ColorStopList resolved_stops = ResolveColorStops(element, 1.f, 0.f, color_stops);
 
-	CompiledShaderHandle shader_handle = render_interface->CompileShader("conic-gradient",
+	CompiledShader shader = render_manager->CompileShader("conic-gradient",
 		Dictionary{
 			{"angle", Variant(angle)},
 			{"center", Variant(center)},
 			{"repeating", Variant(repeating)},
 			{"color_stop_list", Variant(std::move(resolved_stops))},
 		});
+	if (!shader)
+		return INVALID_DECORATORDATAHANDLE;
 
-	Geometry geometry;
-
+	Mesh mesh;
 	const ComputedValues& computed = element->GetComputedValues();
 	const byte alpha = byte(computed.opacity() * 255.f);
-	GeometryUtilities::GenerateBackground(&geometry, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
+	MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), box_area);
 
 	const Vector2f render_offset = box.GetPosition(box_area);
-	for (Vertex& vertex : geometry.GetVertices())
+	for (Vertex& vertex : mesh.vertices)
 		vertex.tex_coord = vertex.position - render_offset;
 
-	ShaderElementData* element_data = GetShaderElementDataPool().AllocateAndConstruct(std::move(geometry), shader_handle);
+	ShaderElementData* element_data =
+		GetShaderElementDataPool().AllocateAndConstruct(render_manager->MakeGeometry(std::move(mesh)), std::move(shader));
 	return reinterpret_cast<DecoratorDataHandle>(element_data);
 }
 
 void DecoratorConicGradient::ReleaseElementData(DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	GetRenderInterface()->ReleaseCompiledShader(element_data->shader);
-
 	GetShaderElementDataPool().DestroyAndDeallocate(element_data);
 }
 
 void DecoratorConicGradient::RenderElement(Element* element, DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	element_data->geometry.RenderWithShader(element_data->shader, element->GetAbsoluteOffset(BoxArea::Border));
+	element_data->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), {}, element_data->shader);
 }
 
 DecoratorConicGradientInstancer::DecoratorConicGradientInstancer()

+ 11 - 12
Source/Core/DecoratorNinePatch.cpp

@@ -40,7 +40,7 @@ DecoratorNinePatch::DecoratorNinePatch() {}
 DecoratorNinePatch::~DecoratorNinePatch() {}
 
 bool DecoratorNinePatch::Initialise(const Rectanglef& _rect_outer, const Rectanglef& _rect_inner, const Array<NumericValue, 4>* _edges,
-	const Texture& _texture, float _display_scale)
+	Texture _texture, float _display_scale)
 {
 	rect_outer = _rect_outer;
 	rect_inner = _rect_inner;
@@ -58,11 +58,8 @@ DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element, Bo
 {
 	const auto& computed = element->GetComputedValues();
 
-	Geometry* data = new Geometry();
-
-	const Texture* texture = GetTexture();
-	data->SetTexture(texture);
-	const Vector2f texture_dimensions(texture->GetDimensions());
+	Texture texture = GetTexture();
+	const Vector2f texture_dimensions(texture.GetDimensions());
 
 	const Vector2f surface_offset = element->GetBox().GetPosition(paint_area);
 	const Vector2f surface_dimensions = element->GetBox().GetSize(paint_area).Round();
@@ -133,9 +130,9 @@ DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element, Bo
 	surface_pos[2] = surface_pos[2].Round();
 
 	/* Now we have all the coordinates we need. Expand the diagonal vertices to the 16 individual vertices. */
-
-	Vector<Vertex>& vertices = data->GetVertices();
-	Vector<int>& indices = data->GetIndices();
+	Mesh mesh;
+	Vector<Vertex>& vertices = mesh.vertices;
+	Vector<int>& indices = mesh.indices;
 
 	vertices.resize(4 * 4);
 
@@ -167,6 +164,8 @@ DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element, Bo
 		indices[i + 5] = top_left_index + 5;
 	}
 
+	Geometry* data = new Geometry(element->GetRenderManager()->MakeGeometry(std::move(mesh)));
+
 	return reinterpret_cast<DecoratorDataHandle>(data);
 }
 
@@ -178,7 +177,7 @@ void DecoratorNinePatch::ReleaseElementData(DecoratorDataHandle element_data) co
 void DecoratorNinePatch::RenderElement(Element* element, DecoratorDataHandle element_data) const
 {
 	Geometry* data = reinterpret_cast<Geometry*>(element_data);
-	data->Render(element->GetAbsoluteOffset(BoxArea::Border));
+	data->Render(element->GetAbsoluteOffset(BoxArea::Border), GetTexture());
 }
 
 DecoratorNinePatchInstancer::DecoratorNinePatchInstancer()
@@ -243,8 +242,8 @@ SharedPtr<Decorator> DecoratorNinePatchInstancer::InstanceDecorator(const String
 
 	auto decorator = MakeShared<DecoratorNinePatch>();
 
-	if (!decorator->Initialise(sprite_outer->rectangle, sprite_inner->rectangle, (edges_set ? &edges : nullptr), sprite_outer->sprite_sheet->texture,
-			sprite_outer->sprite_sheet->display_scale))
+	if (!decorator->Initialise(sprite_outer->rectangle, sprite_inner->rectangle, (edges_set ? &edges : nullptr),
+			sprite_outer->sprite_sheet->texture_source.GetTexture(instancer_interface.GetRenderManager()), sprite_outer->sprite_sheet->display_scale))
 	{
 		return nullptr;
 	}

+ 1 - 1
Source/Core/DecoratorNinePatch.h

@@ -40,7 +40,7 @@ public:
 	DecoratorNinePatch();
 	virtual ~DecoratorNinePatch();
 
-	bool Initialise(const Rectanglef& rect_outer, const Rectanglef& rect_inner, const Array<NumericValue, 4>* _edges, const Texture& texture,
+	bool Initialise(const Rectanglef& rect_outer, const Rectanglef& rect_inner, const Array<NumericValue, 4>* _edges, Texture texture,
 		float display_scale);
 
 	DecoratorDataHandle GenerateElementData(Element* element, BoxArea paint_area) const override;

+ 13 - 12
Source/Core/DecoratorShader.cpp

@@ -30,9 +30,9 @@
 #include "../../Include/RmlUi/Core/ComputedValues.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -54,26 +54,28 @@ bool DecoratorShader::Initialise(String&& in_value)
 
 DecoratorDataHandle DecoratorShader::GenerateElementData(Element* element, BoxArea render_area) const
 {
-	RenderInterface* render_interface = GetRenderInterface();
-	if (!render_interface)
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
 		return INVALID_DECORATORDATAHANDLE;
 
 	const Box& box = element->GetBox();
 	const Vector2f dimensions = box.GetSize(render_area);
-	CompiledShaderHandle effect_handle =
-		render_interface->CompileShader("shader", Dictionary{{"value", Variant(value)}, {"dimensions", Variant(dimensions)}});
+	CompiledShader shader = render_manager->CompileShader("shader", Dictionary{{"value", Variant(value)}, {"dimensions", Variant(dimensions)}});
+	if (!shader)
+		return INVALID_DECORATORDATAHANDLE;
 
-	Geometry geometry;
+	Mesh mesh;
 
 	const ComputedValues& computed = element->GetComputedValues();
 	const byte alpha = byte(computed.opacity() * 255.f);
-	GeometryUtilities::GenerateBackground(&geometry, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), render_area);
+	MeshUtilities::GenerateBackground(mesh, box, Vector2f(), computed.border_radius(), ColourbPremultiplied(alpha, alpha), render_area);
 
 	const Vector2f offset = box.GetPosition(render_area);
-	for (Vertex& vertex : geometry.GetVertices())
+	for (Vertex& vertex : mesh.vertices)
 		vertex.tex_coord = (vertex.position - offset) / dimensions;
 
-	ShaderElementData* element_data = GetShaderElementDataPool().AllocateAndConstruct(std::move(geometry), effect_handle);
+	ShaderElementData* element_data =
+		GetShaderElementDataPool().AllocateAndConstruct(render_manager->MakeGeometry(std::move(mesh)), std::move(shader));
 
 	return reinterpret_cast<DecoratorDataHandle>(element_data);
 }
@@ -81,14 +83,13 @@ DecoratorDataHandle DecoratorShader::GenerateElementData(Element* element, BoxAr
 void DecoratorShader::ReleaseElementData(DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	GetRenderInterface()->ReleaseCompiledShader(element_data->shader);
 	GetShaderElementDataPool().DestroyAndDeallocate(element_data);
 }
 
 void DecoratorShader::RenderElement(Element* element, DecoratorDataHandle handle) const
 {
 	ShaderElementData* element_data = reinterpret_cast<ShaderElementData*>(handle);
-	element_data->geometry.RenderWithShader(element_data->shader, element->GetAbsoluteOffset(BoxArea::Border));
+	element_data->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), {}, element_data->shader);
 }
 
 DecoratorShaderInstancer::DecoratorShaderInstancer()

+ 2 - 2
Source/Core/DecoratorShader.h

@@ -69,9 +69,9 @@ private:
 };
 
 struct ShaderElementData {
-	ShaderElementData(Geometry&& geometry, CompiledShaderHandle shader) : geometry(std::move(geometry)), shader(shader) {}
+	ShaderElementData(Geometry&& geometry, CompiledShader&& shader) : geometry(std::move(geometry)), shader(std::move(shader)) {}
 	Geometry geometry;
-	CompiledShaderHandle shader;
+	CompiledShader shader;
 };
 Pool<ShaderElementData>& GetShaderElementDataPool();
 

+ 7 - 17
Source/Core/DecoratorTiled.cpp

@@ -29,8 +29,9 @@
 #include "DecoratorTiled.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/Geometry.h"
 #include "../../Include/RmlUi/Core/Math.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "../../Include/RmlUi/Core/Spritesheet.h"
 #include <algorithm>
@@ -55,7 +56,7 @@ DecoratorTiled::Tile::Tile() : display_scale(1), position(0, 0), size(0, 0)
 	orientation = ORIENTATION_NONE;
 }
 
-void DecoratorTiled::Tile::CalculateDimensions(const Texture& texture) const
+void DecoratorTiled::Tile::CalculateDimensions(Texture texture) const
 {
 	if (!tile_data_calculated)
 	{
@@ -98,8 +99,8 @@ Vector2f DecoratorTiled::Tile::GetNaturalDimensions(Element* element) const
 	return raw_dimensions * scale_raw_to_natural_dimensions;
 }
 
-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
+void DecoratorTiled::Tile::GenerateGeometry(Mesh& mesh, 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;
@@ -204,22 +205,11 @@ void DecoratorTiled::Tile::GenerateGeometry(Vector<Vertex>& vertices, Vector<int
 		}
 	}
 
-	// Resize the vertex and index arrays to fit the new geometry.
-	int index_offset = (int)vertices.size();
-	vertices.resize(vertices.size() + 4);
-	Vertex* new_vertices = &vertices[0] + index_offset;
-
-	size_t num_indices = indices.size();
-	indices.resize(indices.size() + 6);
-	int* new_indices = &indices[0] + num_indices;
-
 	// Generate the vertices for the tiled surface.
 	Vector2f tile_position = (surface_origin + tile_offset);
-
 	Math::SnapToPixelGrid(tile_position, final_tile_dimensions);
 
-	GeometryUtilities::GenerateQuad(new_vertices, new_indices, tile_position, final_tile_dimensions, quad_colour, scaled_texcoords[0],
-		scaled_texcoords[1], index_offset);
+	MeshUtilities::GenerateQuad(mesh, tile_position, final_tile_dimensions, quad_colour, scaled_texcoords[0], scaled_texcoords[1]);
 }
 
 void DecoratorTiled::ScaleTileDimensions(Vector2f& tile_dimensions, float axis_value, Axis axis_enum) const
@@ -306,7 +296,7 @@ bool DecoratorTiledInstancer::GetTileProperties(DecoratorTiled::Tile* tiles, Tex
 			tile.size = sprite->rectangle.Size();
 			tile.display_scale = sprite->sprite_sheet->display_scale;
 
-			texture = sprite->sprite_sheet->texture;
+			texture = sprite->sprite_sheet->texture_source.GetTexture(instancer_interface.GetRenderManager());
 		}
 		else
 		{

+ 5 - 4
Source/Core/DecoratorTiled.h

@@ -35,7 +35,8 @@
 
 namespace Rml {
 
-struct Texture;
+class Texture;
+struct Mesh;
 
 /**
     Base class for tiled decorators.
@@ -79,7 +80,7 @@ public:
 		Tile();
 
 		/// Calculates the tile's dimensions from the texture and texture coordinates.
-		void CalculateDimensions(const Texture& texture) const;
+		void CalculateDimensions(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;
@@ -91,8 +92,8 @@ public:
 		/// @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, const ComputedValues& computed_values, Vector2f surface_origin,
-			Vector2f surface_dimensions, Vector2f tile_dimensions) const;
+		void GenerateGeometry(Mesh& mesh, const ComputedValues& computed_values, Vector2f surface_origin, Vector2f surface_dimensions,
+			Vector2f tile_dimensions) const;
 
 		struct TileData {
 			Vector2f size;         // 'px' units

+ 22 - 29
Source/Core/DecoratorTiledBox.cpp

@@ -29,6 +29,7 @@
 #include "DecoratorTiledBox.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -101,7 +102,7 @@ DecoratorDataHandle DecoratorTiledBox::GenerateElementData(Element* element, Box
 	for (int i = 0; i < 9; i++)
 	{
 		RMLUI_ASSERT(tiles[i].texture_index >= 0);
-		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
+		tiles[i].CalculateDimensions(GetTexture(tiles[i].texture_index));
 	}
 
 	const Vector2f offset = element->GetBox().GetPosition(paint_area);
@@ -191,58 +192,50 @@ DecoratorDataHandle DecoratorTiledBox::GenerateElementData(Element* element, Box
 			bottom.y = bottom_right.y;
 	}
 
-	const int num_textures = GetNumTextures();
-	DecoratorTiledBoxData* data = new DecoratorTiledBoxData(num_textures);
 	const ComputedValues& computed = element->GetComputedValues();
+	Mesh mesh[COUNT];
 
 	// 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(), computed, offset, top_left, top_left);
+	tiles[TOP_LEFT_CORNER].GenerateGeometry(mesh[tiles[TOP_LEFT_CORNER].texture_index], computed, offset, 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(), computed, offset + Vector2f(top_left.x, 0),
+	tiles[TOP_EDGE].GenerateGeometry(mesh[tiles[TOP_EDGE].texture_index], computed, offset + Vector2f(top_left.x, 0),
 		Vector2f(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(), computed, offset + Vector2f(size.x - top_right.x, 0), top_right,
-		top_right);
+	tiles[TOP_RIGHT_CORNER].GenerateGeometry(mesh[tiles[TOP_RIGHT_CORNER].texture_index], computed, offset + Vector2f(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(), computed, offset + Vector2f(0, top_left.y),
+	tiles[LEFT_EDGE].GenerateGeometry(mesh[tiles[LEFT_EDGE].texture_index], computed, offset + Vector2f(0, top_left.y),
 		Vector2f(left.x, 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(), computed, offset + Vector2f((size.x - right.x), top_right.y),
+	tiles[RIGHT_EDGE].GenerateGeometry(mesh[tiles[RIGHT_EDGE].texture_index], computed, offset + Vector2f((size.x - right.x), top_right.y),
 		Vector2f(right.x, 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(), computed, offset + Vector2f(0, size.y - bottom_left.y), bottom_left,
-		bottom_left);
+	tiles[BOTTOM_LEFT_CORNER].GenerateGeometry(mesh[tiles[BOTTOM_LEFT_CORNER].texture_index], computed, offset + Vector2f(0, 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(), computed, offset + Vector2f(bottom_left.x, size.y - bottom.y),
+	tiles[BOTTOM_EDGE].GenerateGeometry(mesh[tiles[BOTTOM_EDGE].texture_index], computed, offset + Vector2f(bottom_left.x, size.y - bottom.y),
 		Vector2f(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(), computed,
+	tiles[BOTTOM_RIGHT_CORNER].GenerateGeometry(mesh[tiles[BOTTOM_RIGHT_CORNER].texture_index], computed,
 		offset + Vector2f(size.x - bottom_right.x, size.y - bottom_right.y), bottom_right, bottom_right);
 
 	// Generate the centre geometry.
 	Vector2f centre_dimensions = tiles[CENTRE].GetNaturalDimensions(element);
 	Vector2f centre_surface_dimensions(size.x - (left.x + right.x), size.y - (top.y + bottom.y));
 
-	tiles[CENTRE].GenerateGeometry(data->geometry[tiles[CENTRE].texture_index].GetVertices(),
-		data->geometry[tiles[CENTRE].texture_index].GetIndices(), computed, offset + Vector2f(left.x, top.y), centre_surface_dimensions,
+	tiles[CENTRE].GenerateGeometry(mesh[tiles[CENTRE].texture_index], computed, offset + Vector2f(left.x, top.y), centre_surface_dimensions,
 		centre_dimensions);
 
-	// Set the textures on the geometry.
-	const Texture* texture = nullptr;
-	int texture_index = 0;
-	while ((texture = GetTexture(texture_index)) != nullptr)
-		data->geometry[texture_index++].SetTexture(texture);
+	const int num_textures = GetNumTextures();
+	DecoratorTiledBoxData* data = new DecoratorTiledBoxData(num_textures);
+	RenderManager* render_manager = element->GetRenderManager();
+
+	// Set the mesh and textures on the geometry.
+	for (int i = 0; i < num_textures; i++)
+		data->geometry[i] = render_manager->MakeGeometry(std::move(mesh[i]));
 
 	return reinterpret_cast<DecoratorDataHandle>(data);
 }
@@ -258,7 +251,7 @@ void DecoratorTiledBox::RenderElement(Element* element, DecoratorDataHandle elem
 	DecoratorTiledBoxData* data = reinterpret_cast<DecoratorTiledBoxData*>(element_data);
 
 	for (int i = 0; i < data->num_textures; i++)
-		data->geometry[i].Render(translation);
+		data->geometry[i].Render(translation, GetTexture(i));
 }
 
 DecoratorTiledBoxInstancer::DecoratorTiledBoxInstancer() : DecoratorTiledInstancer(9)

+ 11 - 10
Source/Core/DecoratorTiledBox.h

@@ -58,18 +58,19 @@ public:
 
 private:
 	enum {
-		TOP_LEFT_CORNER = 0,
-		TOP_RIGHT_CORNER = 1,
-		BOTTOM_LEFT_CORNER = 2,
-		BOTTOM_RIGHT_CORNER = 3,
-		LEFT_EDGE = 4,
-		RIGHT_EDGE = 5,
-		TOP_EDGE = 6,
-		BOTTOM_EDGE = 7,
-		CENTRE = 8
+		TOP_LEFT_CORNER,
+		TOP_RIGHT_CORNER,
+		BOTTOM_LEFT_CORNER,
+		BOTTOM_RIGHT_CORNER,
+		LEFT_EDGE,
+		RIGHT_EDGE,
+		TOP_EDGE,
+		BOTTOM_EDGE,
+		CENTRE,
+		COUNT,
 	};
 
-	Tile tiles[9];
+	Tile tiles[COUNT];
 };
 
 class DecoratorTiledBoxInstancer : public DecoratorTiledInstancer {

+ 17 - 19
Source/Core/DecoratorTiledHorizontal.cpp

@@ -29,6 +29,7 @@
 #include "DecoratorTiledHorizontal.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "../../Include/RmlUi/Core/Texture.h"
 
 namespace Rml {
@@ -79,10 +80,7 @@ DecoratorDataHandle DecoratorTiledHorizontal::GenerateElementData(Element* eleme
 {
 	// Initialise the tiles for this element.
 	for (int i = 0; i < 3; i++)
-		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
-
-	const int num_textures = GetNumTextures();
-	DecoratorTiledHorizontalData* data = new DecoratorTiledHorizontalData(num_textures);
+		tiles[i].CalculateDimensions(GetTexture(tiles[i].texture_index));
 
 	const Vector2f offset = element->GetBox().GetPosition(paint_area);
 	const Vector2f size = element->GetBox().GetSize(paint_area);
@@ -109,23 +107,23 @@ DecoratorDataHandle DecoratorTiledHorizontal::GenerateElementData(Element* eleme
 	}
 
 	const ComputedValues& computed = element->GetComputedValues();
+	Mesh mesh[COUNT];
+
+	tiles[LEFT].GenerateGeometry(mesh[tiles[LEFT].texture_index], computed, offset, left_dimensions, left_dimensions);
 
-	// Generate the geometry for the left tile.
-	tiles[LEFT].GenerateGeometry(data->geometry[tiles[LEFT].texture_index].GetVertices(), data->geometry[tiles[LEFT].texture_index].GetIndices(),
-		computed, offset, 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(), computed, offset + Vector2f(left_dimensions.x, 0),
+	tiles[CENTRE].GenerateGeometry(mesh[tiles[CENTRE].texture_index], computed, offset + Vector2f(left_dimensions.x, 0),
 		Vector2f(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(),
-		computed, offset + Vector2f(size.x - right_dimensions.x, 0), right_dimensions, right_dimensions);
 
-	// Set the textures on the geometry.
-	const Texture* texture = nullptr;
-	int texture_index = 0;
-	while ((texture = GetTexture(texture_index)) != nullptr)
-		data->geometry[texture_index++].SetTexture(texture);
+	tiles[RIGHT].GenerateGeometry(mesh[tiles[RIGHT].texture_index], computed, offset + Vector2f(size.x - right_dimensions.x, 0), right_dimensions,
+		right_dimensions);
+
+	const int num_textures = GetNumTextures();
+	DecoratorTiledHorizontalData* data = new DecoratorTiledHorizontalData(num_textures);
+	RenderManager* render_manager = element->GetRenderManager();
+
+	// Set the mesh and textures on the geometry.
+	for (int i = 0; i < num_textures; i++)
+		data->geometry[i] = render_manager->MakeGeometry(std::move(mesh[i]));
 
 	return reinterpret_cast<DecoratorDataHandle>(data);
 }
@@ -141,7 +139,7 @@ void DecoratorTiledHorizontal::RenderElement(Element* element, DecoratorDataHand
 	DecoratorTiledHorizontalData* data = reinterpret_cast<DecoratorTiledHorizontalData*>(element_data);
 
 	for (int i = 0; i < data->num_textures; i++)
-		data->geometry[i].Render(translation);
+		data->geometry[i].Render(translation, GetTexture(i));
 }
 
 DecoratorTiledHorizontalInstancer::DecoratorTiledHorizontalInstancer() : DecoratorTiledInstancer(3)

+ 2 - 2
Source/Core/DecoratorTiledHorizontal.h

@@ -57,9 +57,9 @@ public:
 	void RenderElement(Element* element, DecoratorDataHandle element_data) const override;
 
 private:
-	enum { LEFT = 0, RIGHT = 1, CENTRE = 2 };
+	enum { LEFT, RIGHT, CENTRE, COUNT };
 
-	Tile tiles[3];
+	Tile tiles[COUNT];
 };
 
 class DecoratorTiledHorizontalInstancer : public DecoratorTiledInstancer {

+ 9 - 8
Source/Core/DecoratorTiledImage.cpp

@@ -29,7 +29,8 @@
 #include "DecoratorTiledImage.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -37,7 +38,7 @@ DecoratorTiledImage::DecoratorTiledImage() {}
 
 DecoratorTiledImage::~DecoratorTiledImage() {}
 
-bool DecoratorTiledImage::Initialise(const Tile& _tile, const Texture& _texture)
+bool DecoratorTiledImage::Initialise(const Tile& _tile, Texture _texture)
 {
 	tile = _tile;
 	tile.texture_index = AddTexture(_texture);
@@ -47,10 +48,7 @@ bool DecoratorTiledImage::Initialise(const Tile& _tile, const Texture& _texture)
 DecoratorDataHandle DecoratorTiledImage::GenerateElementData(Element* element, BoxArea paint_area) const
 {
 	// Calculate the tile's dimensions for this element.
-	tile.CalculateDimensions(*GetTexture(tile.texture_index));
-
-	Geometry* data = new Geometry();
-	data->SetTexture(GetTexture());
+	tile.CalculateDimensions(GetTexture());
 
 	const ComputedValues& computed = element->GetComputedValues();
 
@@ -58,7 +56,10 @@ DecoratorDataHandle DecoratorTiledImage::GenerateElementData(Element* element, B
 	const Vector2f size = element->GetBox().GetSize(paint_area);
 
 	// Generate the geometry for the tile.
-	tile.GenerateGeometry(data->GetVertices(), data->GetIndices(), computed, offset, size, tile.GetNaturalDimensions(element));
+	Mesh mesh;
+	tile.GenerateGeometry(mesh, computed, offset, size, tile.GetNaturalDimensions(element));
+
+	Geometry* data = new Geometry(element->GetRenderManager()->MakeGeometry(std::move(mesh)));
 
 	return reinterpret_cast<DecoratorDataHandle>(data);
 }
@@ -71,7 +72,7 @@ void DecoratorTiledImage::ReleaseElementData(DecoratorDataHandle element_data) c
 void DecoratorTiledImage::RenderElement(Element* element, DecoratorDataHandle element_data) const
 {
 	Geometry* data = reinterpret_cast<Geometry*>(element_data);
-	data->Render(element->GetAbsoluteOffset(BoxArea::Border));
+	data->Render(element->GetAbsoluteOffset(BoxArea::Border), GetTexture());
 }
 
 DecoratorTiledImageInstancer::DecoratorTiledImageInstancer() : DecoratorTiledInstancer(1)

+ 1 - 1
Source/Core/DecoratorTiledImage.h

@@ -46,7 +46,7 @@ public:
 	/// @param tile[in] The declaration for the tile.
 	/// @param texture[in] The texture to apply to the tile.
 	/// @return True if the image is valid, false otherwise.
-	bool Initialise(const Tile& tile, const Texture& texture);
+	bool Initialise(const Tile& tile, Texture texture);
 
 	/// Called on a decorator to generate any required per-element data for a newly decorated element.
 	DecoratorDataHandle GenerateElementData(Element* element, BoxArea paint_area) const override;

+ 17 - 20
Source/Core/DecoratorTiledVertical.cpp

@@ -29,7 +29,8 @@
 #include "DecoratorTiledVertical.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "../../Include/RmlUi/Core/Texture.h"
 
 namespace Rml {
@@ -80,10 +81,7 @@ DecoratorDataHandle DecoratorTiledVertical::GenerateElementData(Element* element
 {
 	// Initialise the tile for this element.
 	for (int i = 0; i < 3; i++)
-		tiles[i].CalculateDimensions(*GetTexture(tiles[i].texture_index));
-
-	const int num_textures = GetNumTextures();
-	DecoratorTiledVerticalData* data = new DecoratorTiledVerticalData(num_textures);
+		tiles[i].CalculateDimensions(GetTexture(tiles[i].texture_index));
 
 	const Vector2f offset = element->GetBox().GetPosition(paint_area);
 	const Vector2f size = element->GetBox().GetSize(paint_area);
@@ -110,24 +108,23 @@ DecoratorDataHandle DecoratorTiledVertical::GenerateElementData(Element* element
 	}
 
 	const ComputedValues& computed = element->GetComputedValues();
+	Mesh mesh[COUNT];
+
+	tiles[TOP].GenerateGeometry(mesh[tiles[TOP].texture_index], computed, offset, top_dimensions, top_dimensions);
 
-	// Generate the geometry for the left tile.
-	tiles[TOP].GenerateGeometry(data->geometry[tiles[TOP].texture_index].GetVertices(), data->geometry[tiles[TOP].texture_index].GetIndices(),
-		computed, offset, 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(), computed, offset + Vector2f(0, top_dimensions.y),
+	tiles[CENTRE].GenerateGeometry(mesh[tiles[CENTRE].texture_index], computed, offset + Vector2f(0, top_dimensions.y),
 		Vector2f(centre_dimensions.x, 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(), computed, offset + Vector2f(0, size.y - bottom_dimensions.y), bottom_dimensions,
+
+	tiles[BOTTOM].GenerateGeometry(mesh[tiles[BOTTOM].texture_index], computed, offset + Vector2f(0, size.y - bottom_dimensions.y), bottom_dimensions,
 		bottom_dimensions);
 
-	// Set the textures on the geometry.
-	const Texture* texture = nullptr;
-	int texture_index = 0;
-	while ((texture = GetTexture(texture_index)) != nullptr)
-		data->geometry[texture_index++].SetTexture(texture);
+	const int num_textures = GetNumTextures();
+	DecoratorTiledVerticalData* data = new DecoratorTiledVerticalData(num_textures);
+
+	// Set the mesh and textures on the geometry.
+	RenderManager* render_manager = element->GetRenderManager();
+	for (int i = 0; i < num_textures; i++)
+		data->geometry[i] = render_manager->MakeGeometry(std::move(mesh[i]));
 
 	return reinterpret_cast<DecoratorDataHandle>(data);
 }
@@ -143,7 +140,7 @@ void DecoratorTiledVertical::RenderElement(Element* element, DecoratorDataHandle
 	DecoratorTiledVerticalData* data = reinterpret_cast<DecoratorTiledVerticalData*>(element_data);
 
 	for (int i = 0; i < data->num_textures; i++)
-		data->geometry[i].Render(translation);
+		data->geometry[i].Render(translation, GetTexture(i));
 }
 
 DecoratorTiledVerticalInstancer::DecoratorTiledVerticalInstancer() : DecoratorTiledInstancer(3)

+ 2 - 2
Source/Core/DecoratorTiledVertical.h

@@ -57,9 +57,9 @@ public:
 	void RenderElement(Element* element, DecoratorDataHandle element_data) const override;
 
 private:
-	enum { TOP = 0, BOTTOM = 1, CENTRE = 2 };
+	enum { TOP, BOTTOM, CENTRE, COUNT };
 
-	Tile tiles[3];
+	Tile tiles[COUNT];
 };
 
 class DecoratorTiledVerticalInstancer : public DecoratorTiledInstancer {

+ 7 - 0
Source/Core/Element.cpp

@@ -830,6 +830,13 @@ Context* Element::GetContext() const
 	return nullptr;
 }
 
+RenderManager* Element::GetRenderManager() const
+{
+	if (Context* context = GetContext())
+		return &context->GetRenderManager();
+	return nullptr;
+}
+
 void Element::SetAttributes(const ElementAttributes& _attributes)
 {
 	attributes.reserve(attributes.size() + _attributes.size());

+ 30 - 18
Source/Core/ElementBackgroundBorder.cpp

@@ -32,7 +32,8 @@
 #include "../../Include/RmlUi/Core/Context.h"
 #include "../../Include/RmlUi/Core/DecorationTypes.h"
 #include "../../Include/RmlUi/Core/Element.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 #include "GeometryBoxShadow.h"
 
 namespace Rml {
@@ -44,7 +45,10 @@ void ElementBackgroundBorder::Render(Element* element)
 	if (background_dirty || border_dirty)
 	{
 		for (auto& background : backgrounds)
-			background.second.geometry.Release(true);
+		{
+			if (background.first != BackgroundType::BackgroundBorder)
+				background.second.geometry.Release();
+		}
 
 		GenerateGeometry(element);
 
@@ -52,11 +56,11 @@ void ElementBackgroundBorder::Render(Element* element)
 		border_dirty = false;
 	}
 
-	Geometry* shadow_geometry = GetGeometry(BackgroundType::BoxShadow);
-	if (shadow_geometry && *shadow_geometry)
-		shadow_geometry->Render(element->GetAbsoluteOffset(BoxArea::Border));
-	else if (Geometry* geometry = GetGeometry(BackgroundType::BackgroundBorder))
-		geometry->Render(element->GetAbsoluteOffset(BoxArea::Border));
+	Background* shadow = GetBackground(BackgroundType::BoxShadow);
+	if (shadow && shadow->geometry)
+		shadow->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border), shadow->texture);
+	else if (Background* background = GetBackground(BackgroundType::BackgroundBorder))
+		background->geometry.Render(element->GetAbsoluteOffset(BoxArea::Border));
 }
 
 void ElementBackgroundBorder::DirtyBackground()
@@ -83,19 +87,22 @@ Geometry* ElementBackgroundBorder::GetClipGeometry(Element* element, BoxArea cli
 	Geometry& geometry = GetOrCreateBackground(type).geometry;
 	if (!geometry)
 	{
+		Mesh mesh = geometry.Release(Geometry::ReleaseMode::ClearMesh);
 		const Box& box = element->GetBox();
 		const Vector4f border_radius = element->GetComputedValues().border_radius();
-		GeometryUtilities::GenerateBackground(&geometry, box, {}, border_radius, ColourbPremultiplied(255), clip_area);
+		MeshUtilities::GenerateBackground(mesh, box, {}, border_radius, ColourbPremultiplied(255), clip_area);
+		if (RenderManager* render_manager = element->GetRenderManager())
+			geometry = render_manager->MakeGeometry(std::move(mesh));
 	}
 
 	return &geometry;
 }
 
-Geometry* ElementBackgroundBorder::GetGeometry(BackgroundType type)
+ElementBackgroundBorder::Background* ElementBackgroundBorder::GetBackground(BackgroundType type)
 {
 	auto it = backgrounds.find(type);
 	if (it != backgrounds.end())
-		return &it->second.geometry;
+		return &it->second;
 	return nullptr;
 }
 
@@ -105,13 +112,17 @@ ElementBackgroundBorder::Background& ElementBackgroundBorder::GetOrCreateBackgro
 	if (it != backgrounds.end())
 		return it->second;
 
-	return backgrounds.emplace(type, Background{}).first->second;
+	Background& background = backgrounds[type];
+	return background;
 }
 
 void ElementBackgroundBorder::GenerateGeometry(Element* element)
 {
-	const ComputedValues& computed = element->GetComputedValues();
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
+		return;
 
+	const ComputedValues& computed = element->GetComputedValues();
 	const bool has_box_shadow = computed.has_box_shadow();
 	const float opacity = computed.opacity();
 
@@ -136,18 +147,19 @@ void ElementBackgroundBorder::GenerateGeometry(Element* element)
 	const Vector4f border_radius = computed.border_radius();
 
 	Geometry& geometry = GetOrCreateBackground(BackgroundType::BackgroundBorder).geometry;
-	RMLUI_ASSERT(!geometry);
+	Mesh mesh = geometry.Release(Geometry::ReleaseMode::ClearMesh);
 
 	for (int i = 0; i < element->GetNumBoxes(); i++)
 	{
 		Vector2f offset;
 		const Box& box = element->GetBox(i, offset);
-		GeometryUtilities::GenerateBackgroundBorder(&geometry, box, offset, border_radius, background_color, border_colors);
+		MeshUtilities::GenerateBackgroundBorder(mesh, box, offset, border_radius, background_color, border_colors);
 	}
+	geometry = render_manager->MakeGeometry(std::move(mesh));
 
 	if (has_box_shadow)
 	{
-		Geometry& background_border_geometry = *GetGeometry(BackgroundType::BackgroundBorder);
+		Geometry& background_border_geometry = geometry;
 
 		const Property* p_box_shadow = element->GetLocalProperty(PropertyId::BoxShadow);
 		RMLUI_ASSERT(p_box_shadow->value.GetType() == Variant::BOXSHADOWLIST);
@@ -156,10 +168,10 @@ void ElementBackgroundBorder::GenerateGeometry(Element* element)
 		// Generate the geometry for the box-shadow texture.
 		Background& shadow_background = GetOrCreateBackground(BackgroundType::BoxShadow);
 		Geometry& shadow_geometry = shadow_background.geometry;
-		Texture& shadow_texture = shadow_background.texture;
+		CallbackTexture& shadow_texture = shadow_background.texture;
 
-		GeometryBoxShadow::Generate(shadow_geometry, shadow_texture, element, background_border_geometry, std::move(shadow_list), border_radius,
-			computed.opacity());
+		GeometryBoxShadow::Generate(shadow_geometry, shadow_texture, *render_manager, element, background_border_geometry, std::move(shadow_list),
+			border_radius, computed.opacity());
 	}
 }
 

+ 3 - 3
Source/Core/ElementBackgroundBorder.h

@@ -29,8 +29,8 @@
 #ifndef RMLUI_CORE_ELEMENTBACKGROUNDBORDER_H
 #define RMLUI_CORE_ELEMENTBACKGROUNDBORDER_H
 
+#include "../../Include/RmlUi/Core/CallbackTexture.h"
 #include "../../Include/RmlUi/Core/Geometry.h"
-#include "../../Include/RmlUi/Core/Texture.h"
 #include "../../Include/RmlUi/Core/Types.h"
 
 namespace Rml {
@@ -50,10 +50,10 @@ private:
 	enum class BackgroundType { BackgroundBorder, BoxShadow, ClipBorder, ClipPadding, ClipContent, Count };
 	struct Background {
 		Geometry geometry;
-		Texture texture;
+		CallbackTexture texture;
 	};
 
-	Geometry* GetGeometry(BackgroundType type);
+	Background* GetBackground(BackgroundType type);
 	Background& GetOrCreateBackground(BackgroundType type);
 
 	void GenerateGeometry(Element* element);

+ 35 - 51
Source/Core/ElementDecoration.cpp

@@ -28,14 +28,12 @@
 
 #include "ElementDecoration.h"
 #include "../../Include/RmlUi/Core/ComputedValues.h"
-#include "../../Include/RmlUi/Core/Context.h"
 #include "../../Include/RmlUi/Core/Decorator.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/ElementDocument.h"
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../Include/RmlUi/Core/Filter.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
 #include "../../Include/RmlUi/Core/StyleSheet.h"
 
 namespace Rml {
@@ -58,6 +56,13 @@ void ElementDecoration::InstanceDecorators()
 	RMLUI_ZoneScopedC(0xB22222);
 	ReleaseDecorators();
 
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
+	{
+		RMLUI_ERRORMSG("Decorators are being instanced before a render manager is available. Is this element attached to the document?");
+		return;
+	}
+
 	const ComputedValues& computed = element->GetComputedValues();
 
 	if (computed.has_decorator() || computed.has_mask_image())
@@ -88,7 +93,7 @@ void ElementDecoration::InstanceDecorators()
 				}
 			}
 
-			const DecoratorPtrList& decorator_list = style_sheet->InstanceDecorators(*decorators_ptr, source);
+			const DecoratorPtrList& decorator_list = style_sheet->InstanceDecorators(*render_manager, *decorators_ptr, source);
 			RMLUI_ASSERT(decorator_list.empty() || decorator_list.size() == decorators_ptr->list.size());
 
 			DecoratorEntryList& decorators_target = (id == PropertyId::Decorator ? decorators : mask_images);
@@ -133,7 +138,7 @@ void ElementDecoration::InstanceDecorators()
 				SharedPtr<const Filter> filter = declaration.instancer->InstanceFilter(declaration.type, declaration.properties);
 				if (filter)
 				{
-					list.push_back({std::move(filter), CompiledFilterHandle{}});
+					list.push_back({std::move(filter), CompiledFilter{}});
 				}
 				else
 				{
@@ -166,12 +171,7 @@ void ElementDecoration::ReloadDecoratorsData()
 		for (FilterEntryList* list : {&filters, &backdrop_filters})
 		{
 			for (FilterEntry& filter : *list)
-			{
-				if (filter.handle)
-					filter.filter->ReleaseCompiledFilter(element, filter.handle);
-
-				filter.handle = filter.filter->CompileFilter(element);
-			}
+				filter.compiled = filter.filter->CompileFilter(element);
 		}
 	}
 }
@@ -188,15 +188,8 @@ void ElementDecoration::ReleaseDecorators()
 		list->clear();
 	}
 
-	for (FilterEntryList* list : {&filters, &backdrop_filters})
-	{
-		for (FilterEntry& filter : *list)
-		{
-			if (filter.handle)
-				filter.filter->ReleaseCompiledFilter(element, filter.handle);
-		}
-		list->clear();
-	}
+	filters.clear();
+	backdrop_filters.clear();
 }
 
 void ElementDecoration::RenderDecorators(RenderStage render_stage)
@@ -213,7 +206,8 @@ void ElementDecoration::RenderDecorators(RenderStage render_stage)
 			for (int i = (int)decorators.size() - 1; i >= 0; i--)
 			{
 				DecoratorEntry& decorator = decorators[i];
-				decorator.decorator->RenderElement(element, decorator.decorator_data);
+				if (decorator.decorator_data)
+					decorator.decorator->RenderElement(element, decorator.decorator_data);
 			}
 		}
 	}
@@ -221,13 +215,11 @@ void ElementDecoration::RenderDecorators(RenderStage render_stage)
 	if (filters.empty() && backdrop_filters.empty() && mask_images.empty())
 		return;
 
-	RenderInterface* render_interface = ::Rml::GetRenderInterface();
-	Context* context = element->GetContext();
-	if (!render_interface || !context)
+	RenderManager* render_manager = element->GetRenderManager();
+	if (!render_manager)
 		return;
 
-	RenderManager& render_manager = context->GetRenderManager();
-	Rectanglei initial_scissor_region = render_manager.GetState().scissor_region;
+	Rectanglei initial_scissor_region = render_manager->GetState().scissor_region;
 
 	auto ApplyClippingRegion = [this, &render_manager](PropertyId filter_id) {
 		RMLUI_ASSERT(filter_id == PropertyId::Filter || filter_id == PropertyId::BackdropFilter);
@@ -249,8 +241,8 @@ void ElementDecoration::RenderDecorators(RenderStage render_stage)
 		Math::ExpandToPixelGrid(filter_region);
 
 		Rectanglei scissor_region = Rectanglei(filter_region);
-		scissor_region.IntersectIfValid(render_manager.GetState().scissor_region);
-		render_manager.SetScissorRegion(scissor_region);
+		scissor_region.IntersectIfValid(render_manager->GetState().scissor_region);
+		render_manager->SetScissorRegion(scissor_region);
 	};
 
 	if (!backdrop_filters.empty())
@@ -259,18 +251,15 @@ void ElementDecoration::RenderDecorators(RenderStage render_stage)
 		{
 			ApplyClippingRegion(PropertyId::BackdropFilter);
 
-			render_interface->PushLayer(LayerFill::Clone);
+			render_manager->PushLayer(LayerFill::Clone);
 
 			FilterHandleList filter_handles;
 			for (auto& filter : backdrop_filters)
-			{
-				if (filter.handle)
-					filter_handles.push_back(filter.handle);
-			}
+				filter.compiled.AddHandleTo(filter_handles);
 
-			render_interface->PopLayer(BlendMode::Replace, filter_handles);
+			render_manager->PopLayer(BlendMode::Replace, filter_handles);
 
-			render_manager.SetScissorRegion(initial_scissor_region);
+			render_manager->SetScissorRegion(initial_scissor_region);
 		}
 	}
 
@@ -278,41 +267,36 @@ void ElementDecoration::RenderDecorators(RenderStage render_stage)
 	{
 		if (render_stage == RenderStage::Enter)
 		{
-			render_interface->PushLayer(LayerFill::Clear);
+			render_manager->PushLayer(LayerFill::Clear);
 		}
 		else if (render_stage == RenderStage::Exit)
 		{
 			ApplyClippingRegion(PropertyId::Filter);
 
-			CompiledFilterHandle mask_image_handle = {};
+			CompiledFilter mask_image_filter;
 			FilterHandleList filter_handles;
+			filter_handles.reserve(filters.size() + (mask_images.empty() ? 0 : 1));
 
 			for (auto& filter : filters)
-			{
-				if (filter.handle)
-					filter_handles.push_back(filter.handle);
-			}
+				filter.compiled.AddHandleTo(filter_handles);
 
 			if (!mask_images.empty())
 			{
-				render_interface->PushLayer(LayerFill::Clear);
+				render_manager->PushLayer(LayerFill::Clear);
 
 				for (int i = (int)mask_images.size() - 1; i >= 0; i--)
 				{
 					DecoratorEntry& mask_image = mask_images[i];
-					mask_image.decorator->RenderElement(element, mask_image.decorator_data);
+					if (mask_image.decorator_data)
+						mask_image.decorator->RenderElement(element, mask_image.decorator_data);
 				}
-				mask_image_handle = render_interface->SaveLayerAsMaskImage();
-				if (mask_image_handle)
-					filter_handles.push_back(mask_image_handle);
-				render_interface->PopLayer(BlendMode::Discard, {});
+				mask_image_filter = render_manager->SaveLayerAsMaskImage();
+				mask_image_filter.AddHandleTo(filter_handles);
+				render_manager->PopLayer(BlendMode::Discard, {});
 			}
 
-			render_interface->PopLayer(BlendMode::Blend, filter_handles);
-
-			if (mask_image_handle)
-				render_interface->ReleaseCompiledFilter(mask_image_handle);
-			render_manager.SetScissorRegion(initial_scissor_region);
+			render_manager->PopLayer(BlendMode::Blend, filter_handles);
+			render_manager->SetScissorRegion(initial_scissor_region);
 		}
 	}
 }

+ 5 - 2
Source/Core/ElementDecoration.h

@@ -29,13 +29,16 @@
 #ifndef RMLUI_CORE_ELEMENTDECORATION_H
 #define RMLUI_CORE_ELEMENTDECORATION_H
 
+#include "../../Include/RmlUi/Core/CompiledFilterShader.h"
+#include "../../Include/RmlUi/Core/Filter.h"
 #include "../../Include/RmlUi/Core/Types.h"
 
 namespace Rml {
 
 class Decorator;
-class Filter;
 class Element;
+class Filter;
+class CompiledFilter;
 
 enum class RenderStage { Enter, Decoration, Exit };
 
@@ -76,7 +79,7 @@ private:
 
 	struct FilterEntry {
 		SharedPtr<const Filter> filter;
-		CompiledFilterHandle handle;
+		CompiledFilter compiled;
 	};
 	using FilterEntryList = Vector<FilterEntry>;
 

+ 37 - 31
Source/Core/ElementText.cpp

@@ -33,7 +33,7 @@
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../Include/RmlUi/Core/Event.h"
 #include "../../Include/RmlUi/Core/FontEngineInterface.h"
-#include "../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../Include/RmlUi/Core/Profiling.h"
 #include "../../Include/RmlUi/Core/Property.h"
 #include "../../Include/RmlUi/Core/RenderManager.h"
@@ -99,6 +99,8 @@ void ElementText::OnRender()
 	if (font_face_handle == 0)
 		return;
 
+	RenderManager& render_manager = GetContext()->GetRenderManager();
+
 	// If our font effects have potentially changed, update it and force a geometry generation if necessary.
 	if (font_effects_dirty && UpdateFontEffects())
 		geometry_dirty = true;
@@ -113,7 +115,7 @@ void ElementText::OnRender()
 
 	// Regenerate the geometry if the colour or font configuration has altered.
 	if (geometry_dirty)
-		GenerateGeometry(font_face_handle);
+		GenerateGeometry(render_manager, font_face_handle);
 
 	// Regenerate text decoration if necessary.
 	if (decoration_property != generated_decoration)
@@ -124,12 +126,14 @@ void ElementText::OnRender()
 		}
 		else
 		{
+			Mesh mesh;
 			if (decoration)
-				decoration->Release(true);
+				mesh = decoration->Release(Geometry::ReleaseMode::ClearMesh);
 			else
 				decoration = MakeUnique<Geometry>();
 
-			GenerateDecoration(font_face_handle);
+			GenerateDecoration(mesh, font_face_handle);
+			*decoration = GetRenderManager()->MakeGeometry(std::move(mesh));
 		}
 
 		generated_decoration = decoration_property;
@@ -138,7 +142,6 @@ void ElementText::OnRender()
 	const Vector2f translation = GetAbsoluteOffset();
 
 	bool render = true;
-	const RenderManager& render_manager = GetContext()->GetRenderManager();
 
 	// Do a visibility test against the scissor region to avoid unnecessary render calls. Instead of handling
 	// culling in complicated transform cases, for simplicity we always proceed to render if one is detected.
@@ -169,7 +172,7 @@ void ElementText::OnRender()
 	if (render)
 	{
 		for (size_t i = 0; i < geometry.size(); ++i)
-			geometry[i].Render(translation);
+			geometry[i].geometry.Render(translation, geometry[i].texture);
 	}
 
 	if (decoration)
@@ -302,10 +305,7 @@ bool ElementText::GenerateLine(String& line, int& line_length, float& line_width
 
 void ElementText::ClearLines()
 {
-	// Clear the rendering information.
-	for (size_t i = 0; i < geometry.size(); ++i)
-		geometry[i].Release(true);
-
+	geometry.clear();
 	lines.clear();
 	generated_decoration = Style::TextDecoration::None;
 }
@@ -397,11 +397,12 @@ void ElementText::OnPropertyChange(const PropertyIdSet& changed_properties)
 		// Re-colour the decoration geometry.
 		if (decoration)
 		{
-			Vector<Vertex>& vertices = decoration->GetVertices();
-			for (size_t i = 0; i < vertices.size(); ++i)
-				vertices[i].colour = colour;
+			Mesh mesh = decoration->Release();
+			for (Vertex& vertex : mesh.vertices)
+				vertex.colour = colour;
 
-			decoration->Release();
+			if (RenderManager* render_manager = GetRenderManager())
+				*decoration = render_manager->MakeGeometry(std::move(mesh));
 		}
 	}
 }
@@ -443,32 +444,37 @@ bool ElementText::UpdateFontEffects()
 	return false;
 }
 
-void ElementText::GenerateGeometry(const FontFaceHandle font_face_handle)
+void ElementText::GenerateGeometry(RenderManager& render_manager, const FontFaceHandle font_face_handle)
 {
 	RMLUI_ZoneScopedC(0xD2691E);
 
-	// Release the old geometry ...
-	for (size_t i = 0; i < geometry.size(); ++i)
-		geometry[i].Release(true);
+	const float letter_spacing = GetComputedValues().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);
 
-	// ... and generate it all again!
+	// Generate the new geometry, one line at a time.
 	for (size_t i = 0; i < lines.size(); ++i)
-		GenerateGeometry(font_face_handle, lines[i]);
+	{
+		lines[i].width = GetFontEngineInterface()->GenerateString(render_manager, font_face_handle, font_effects_handle, lines[i].text,
+			lines[i].position, colour, opacity, letter_spacing, mesh_list);
+	}
 
-	generated_decoration = Style::TextDecoration::None;
+	// Apply the new geometry and textures.
+	geometry.resize(mesh_list.size());
+	for (size_t i = 0; i < geometry.size(); i++)
+	{
+		geometry[i].geometry = render_manager.MakeGeometry(std::move(mesh_list[i].mesh));
+		geometry[i].texture = mesh_list[i].texture;
+	}
 
+	generated_decoration = Style::TextDecoration::None;
 	geometry_dirty = false;
 }
 
-void ElementText::GenerateGeometry(const FontFaceHandle font_face_handle, Line& line)
-{
-	const float letter_spacing = GetComputedValues().letter_spacing();
-
-	line.width = GetFontEngineInterface()->GenerateString(font_face_handle, font_effects_handle, line.text, line.position, colour, opacity,
-		letter_spacing, geometry);
-}
-
-void ElementText::GenerateDecoration(const FontFaceHandle font_face_handle)
+void ElementText::GenerateDecoration(Mesh& mesh, const FontFaceHandle font_face_handle)
 {
 	RMLUI_ZoneScopedC(0xA52A2A);
 	RMLUI_ASSERT(decoration);
@@ -488,7 +494,7 @@ void ElementText::GenerateDecoration(const FontFaceHandle font_face_handle)
 	{
 		const Vector2f position = {line.position.x, line.position.y + offset};
 		const Vector2f size = {(float)line.width, metrics.underline_thickness};
-		GeometryUtilities::GenerateLine(decoration.get(), position, size, colour);
+		MeshUtilities::GenerateLine(mesh, position, size, colour);
 	}
 }
 

+ 1 - 2
Source/Core/ElementUtilities.cpp

@@ -27,6 +27,7 @@
  */
 
 #include "../../Include/RmlUi/Core/ElementUtilities.h"
+#include "../../Include/RmlUi/Core/ComputedValues.h"
 #include "../../Include/RmlUi/Core/Context.h"
 #include "../../Include/RmlUi/Core/Core.h"
 #include "../../Include/RmlUi/Core/DecorationTypes.h"
@@ -35,13 +36,11 @@
 #include "../../Include/RmlUi/Core/Factory.h"
 #include "../../Include/RmlUi/Core/FontEngineInterface.h"
 #include "../../Include/RmlUi/Core/Math.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
 #include "../../Include/RmlUi/Core/RenderManager.h"
 #include "DataController.h"
 #include "DataModel.h"
 #include "DataView.h"
 #include "ElementBackgroundBorder.h"
-#include "ElementStyle.h"
 #include "Layout/LayoutDetails.h"
 #include "Layout/LayoutEngine.h"
 #include "TransformState.h"

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

@@ -30,9 +30,10 @@
 #include "../../../Include/RmlUi/Core/ComputedValues.h"
 #include "../../../Include/RmlUi/Core/ElementDocument.h"
 #include "../../../Include/RmlUi/Core/ElementUtilities.h"
-#include "../../../Include/RmlUi/Core/GeometryUtilities.h"
+#include "../../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "../../../Include/RmlUi/Core/StyleSheet.h"
+#include "../../../Include/RmlUi/Core/Texture.h"
 #include "../../../Include/RmlUi/Core/URL.h"
 #include "../TextureDatabase.h"
 
@@ -86,7 +87,7 @@ void ElementImage::OnRender()
 		GenerateGeometry();
 
 	// Render the geometry beginning at this element's content region.
-	geometry.Render(GetAbsoluteOffset(BoxArea::Content).Round());
+	geometry.Render(GetAbsoluteOffset(BoxArea::Content).Round(), texture);
 }
 
 void ElementImage::OnAttributeChange(const ElementAttributes& changed_attributes)
@@ -141,9 +142,7 @@ void ElementImage::OnChildAdd(Element* child)
 	// texture won't actually be loaded from the backend before it is shown. However, only do this if we have an active context so that the dp-ratio
 	// can be retrieved. If there is no context now the texture loading will be deferred until the next layout update.
 	if (child == this && texture_dirty && GetContext())
-	{
 		LoadTexture();
-	}
 }
 
 void ElementImage::OnResize()
@@ -169,13 +168,7 @@ void ElementImage::OnStyleSheetChange()
 void ElementImage::GenerateGeometry()
 {
 	// Release the old geometry before specifying the new vertices.
-	geometry.Release(true);
-
-	Vector<Vertex>& vertices = geometry.GetVertices();
-	Vector<int>& indices = geometry.GetIndices();
-
-	vertices.resize(4);
-	indices.resize(6);
+	Mesh mesh = geometry.Release(Geometry::ReleaseMode::ClearMesh);
 
 	// Generate the texture coordinates.
 	Vector2f texcoords[2];
@@ -195,7 +188,10 @@ void ElementImage::GenerateGeometry()
 	ColourbPremultiplied quad_colour = computed.image_color().ToPremultiplied(computed.opacity());
 	Vector2f quad_size = GetBox().GetSize(BoxArea::Content).Round();
 
-	GeometryUtilities::GenerateQuad(&vertices[0], &indices[0], Vector2f(0, 0), quad_size, quad_colour, texcoords[0], texcoords[1]);
+	MeshUtilities::GenerateQuad(mesh, Vector2f(0, 0), quad_size, quad_colour, texcoords[0], texcoords[1]);
+
+	if (RenderManager* render_manager = GetRenderManager())
+		geometry = render_manager->MakeGeometry(std::move(mesh));
 
 	geometry_dirty = false;
 }
@@ -206,6 +202,13 @@ bool ElementImage::LoadTexture()
 	geometry_dirty = true;
 	dimensions_scale = 1.0f;
 
+	RenderManager* render_manager = GetRenderManager();
+	if (!render_manager)
+	{
+		texture = {};
+		return false;
+	}
+
 	const float dp_ratio = ElementUtilities::GetDensityIndependentPixelRatio(this);
 
 	// Check for a sprite first, this takes precedence.
@@ -223,7 +226,7 @@ bool ElementImage::LoadTexture()
 				{
 					rect = sprite->rectangle;
 					rect_source = RectSource::Sprite;
-					texture = sprite->sprite_sheet->texture;
+					texture = sprite->sprite_sheet->texture_source.GetTexture(*render_manager);
 					dimensions_scale = sprite->sprite_sheet->display_scale * dp_ratio;
 					valid_sprite = true;
 				}
@@ -232,7 +235,7 @@ bool ElementImage::LoadTexture()
 
 		if (!valid_sprite)
 		{
-			texture = Texture();
+			texture = {};
 			rect_source = RectSource::None;
 			UpdateRect();
 			Log::Message(Log::LT_WARNING, "Could not find sprite '%s' specified in img element %s", sprite_name.c_str(), GetAddress().c_str());
@@ -245,7 +248,7 @@ bool ElementImage::LoadTexture()
 		const String source_name = GetAttribute<String>("src", "");
 		if (source_name.empty())
 		{
-			texture = Texture();
+			texture = {};
 			rect_source = RectSource::None;
 			return false;
 		}
@@ -255,14 +258,11 @@ bool ElementImage::LoadTexture()
 		if (ElementDocument* document = GetOwnerDocument())
 			source_url.SetURL(document->GetSourceURL());
 
-		texture.Set(source_name, source_url.GetPath());
+		texture = render_manager->LoadTexture(source_name, source_url.GetPath());
 
 		dimensions_scale = dp_ratio;
 	}
 
-	// Set the texture onto our geometry object.
-	geometry.SetTexture(&texture);
-
 	return true;
 }
 

+ 16 - 14
Source/Core/Elements/ElementProgress.cpp

@@ -31,8 +31,8 @@
 #include "../../../Include/RmlUi/Core/ElementDocument.h"
 #include "../../../Include/RmlUi/Core/ElementUtilities.h"
 #include "../../../Include/RmlUi/Core/Factory.h"
-#include "../../../Include/RmlUi/Core/GeometryUtilities.h"
 #include "../../../Include/RmlUi/Core/Math.h"
+#include "../../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../../Include/RmlUi/Core/PropertyIdSet.h"
 #include "../../../Include/RmlUi/Core/StyleSheet.h"
 #include "../../../Include/RmlUi/Core/URL.h"
@@ -92,7 +92,7 @@ void ElementProgress::OnRender()
 		GenerateGeometry();
 
 	// Render the geometry at the fill element's content region.
-	geometry.Render(fill->GetAbsoluteOffset());
+	geometry.Render(fill->GetAbsoluteOffset(), texture);
 }
 
 void ElementProgress::OnAttributeChange(const ElementAttributes& changed_attributes)
@@ -179,6 +179,8 @@ void ElementProgress::OnResize()
 
 void ElementProgress::GenerateGeometry()
 {
+	geometry_dirty = false;
+
 	// Warn the user when using the old approach of adding the 'fill-image' property to the 'fill' element.
 	if (fill->GetLocalProperty(PropertyId::FillImage))
 		Log::Message(Log::LT_WARNING,
@@ -218,8 +220,7 @@ void ElementProgress::GenerateGeometry()
 		fill->SetOffset(offset, this);
 	}
 
-	geometry.Release(true);
-	geometry_dirty = false;
+	Mesh mesh = geometry.Release(Geometry::ReleaseMode::ClearMesh);
 
 	// If we don't have a fill texture, then there is no need to generate manual geometry, and we are done here.
 	// Instead, users can style the fill element eg. by decorators.
@@ -227,8 +228,8 @@ void ElementProgress::GenerateGeometry()
 		return;
 
 	// Otherwise, the 'fill-image' property is set, let's generate its geometry.
-	auto& vertices = geometry.GetVertices();
-	auto& indices = geometry.GetIndices();
+	Vector<Vertex>& vertices = mesh.vertices;
+	Vector<int>& indices = mesh.indices;
 
 	Vector2f texcoords[2];
 	if (rect_set)
@@ -329,10 +330,10 @@ void ElementProgress::GenerateGeometry()
 
 	if (!is_circular)
 	{
-		vertices.resize(4);
-		indices.resize(6);
-		GeometryUtilities::GenerateQuad(&vertices[0], &indices[0], Vector2f(0), render_size, quad_colour, texcoords[0], texcoords[1]);
+		MeshUtilities::GenerateQuad(mesh, Vector2f(0), render_size, quad_colour, texcoords[0], texcoords[1]);
 	}
+
+	geometry = GetRenderManager()->MakeGeometry(std::move(mesh));
 }
 
 bool ElementProgress::LoadTexture()
@@ -345,6 +346,10 @@ bool ElementProgress::LoadTexture()
 	if (const Property* property = GetLocalProperty(PropertyId::FillImage))
 		name = property->Get<String>();
 
+	RenderManager* render_manager = GetRenderManager();
+	if (!render_manager)
+		return false;
+
 	ElementDocument* document = GetOwnerDocument();
 
 	bool texture_set = false;
@@ -358,7 +363,7 @@ bool ElementProgress::LoadTexture()
 			{
 				rect = sprite->rectangle;
 				rect_set = true;
-				texture = sprite->sprite_sheet->texture;
+				texture = sprite->sprite_sheet->texture_source.GetTexture(*render_manager);
 				texture_set = true;
 			}
 		}
@@ -368,7 +373,7 @@ bool ElementProgress::LoadTexture()
 		{
 			URL source_url;
 			source_url.SetURL(document->GetSourceURL());
-			texture.Set(name, source_url.GetPath());
+			texture = render_manager->LoadTexture(name, source_url.GetPath());
 			texture_set = true;
 		}
 	}
@@ -379,9 +384,6 @@ bool ElementProgress::LoadTexture()
 		rect = {};
 	}
 
-	// Set the texture onto our geometry object.
-	geometry.SetTexture(&texture);
-
 	return true;
 }
 

+ 10 - 21
Source/Core/Elements/WidgetTextInput.cpp

@@ -36,9 +36,9 @@
 #include "../../../Include/RmlUi/Core/Elements/ElementFormControl.h"
 #include "../../../Include/RmlUi/Core/Factory.h"
 #include "../../../Include/RmlUi/Core/FontEngineInterface.h"
-#include "../../../Include/RmlUi/Core/GeometryUtilities.h"
 #include "../../../Include/RmlUi/Core/Input.h"
 #include "../../../Include/RmlUi/Core/Math.h"
+#include "../../../Include/RmlUi/Core/MeshUtilities.h"
 #include "../../../Include/RmlUi/Core/StringUtilities.h"
 #include "../../../Include/RmlUi/Core/SystemInterface.h"
 #include "../Clock.h"
@@ -1065,12 +1065,6 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 	text_element->ClearLines();
 	selected_text_element->ClearLines();
 
-	// Clear the selection background geometry, and get the vertices and indices so the new geo can
-	// be generated.
-	selection_geometry.Release(true);
-	Vector<Vertex>& selection_vertices = selection_geometry.GetVertices();
-	Vector<int>& selection_indices = selection_geometry.GetIndices();
-
 	// Determine the line-height of the text element.
 	const float line_height = parent->GetLineHeight();
 	const float font_baseline = GetFontEngineInterface()->GetFontMetrics(font_handle).ascent;
@@ -1220,6 +1214,9 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 	// Clamp the cursor to a valid range.
 	absolute_cursor_index = Math::Min(absolute_cursor_index, (int)GetValue().size());
 
+	// Clear the selection background geometry, and get the vertices and indices so the new geometry can be generated.
+	Mesh selection_mesh = selection_geometry.Release(Geometry::ReleaseMode::ClearMesh);
+
 	// Transform segments according to text alignment
 	for (auto& it : segments)
 	{
@@ -1234,10 +1231,7 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 			const bool selection_contains_endline = (selection_begin_index + selection_length > line_begin + lines[it.line_index].editable_length);
 			const Vector2f selection_size(float(it.width + (selection_contains_endline ? endline_selection_width : 0)), line_height);
 
-			selection_vertices.resize(selection_vertices.size() + 4);
-			selection_indices.resize(selection_indices.size() + 6);
-			GeometryUtilities::GenerateQuad(&selection_vertices[selection_vertices.size() - 4], &selection_indices[selection_indices.size() - 6],
-				it.position - Vector2f(0, font_baseline), selection_size, selection_colour, (int)selection_vertices.size() - 4);
+			MeshUtilities::GenerateQuad(selection_mesh, it.position - Vector2f(0, font_baseline), selection_size, selection_colour);
 
 			selected_text_element->AddLine(it.position, it.content);
 		}
@@ -1245,20 +1239,13 @@ Vector2f WidgetTextInput::FormatText(float height_constraint)
 			text_element->AddLine(it.position, it.content);
 	}
 
+	selection_geometry = parent->GetRenderManager()->MakeGeometry(std::move(selection_mesh));
+
 	return content_area;
 }
 
 void WidgetTextInput::GenerateCursor()
 {
-	// Generates the cursor.
-	cursor_geometry.Release();
-
-	Vector<Vertex>& vertices = cursor_geometry.GetVertices();
-	vertices.resize(4);
-
-	Vector<int>& indices = cursor_geometry.GetIndices();
-	indices.resize(6);
-
 	cursor_size.x = Math::Round(ElementUtilities::GetDensityIndependentPixelRatio(text_element));
 	cursor_size.y = text_element->GetLineHeight() + 2.0f;
 
@@ -1270,7 +1257,9 @@ void WidgetTextInput::GenerateCursor()
 			color = property->Get<Colourb>();
 	}
 
-	GeometryUtilities::GenerateQuad(&vertices[0], &indices[0], Vector2f(0, 0), cursor_size, color.ToPremultiplied());
+	Mesh mesh = cursor_geometry.Release(Geometry::ReleaseMode::ClearMesh);
+	MeshUtilities::GenerateQuad(mesh, Vector2f(0, 0), cursor_size, color.ToPremultiplied());
+	cursor_geometry = parent->GetRenderManager()->MakeGeometry(std::move(mesh));
 }
 
 void WidgetTextInput::ForceFormattingOnNextLayout()

+ 2 - 2
Source/Core/Factory.cpp

@@ -342,9 +342,9 @@ void Factory::RegisterContextInstancer(ContextInstancer* instancer)
 	context_instancer = instancer;
 }
 
-ContextPtr Factory::InstanceContext(const String& name)
+ContextPtr Factory::InstanceContext(const String& name, RenderManager* render_manager)
 {
-	ContextPtr new_context = context_instancer->InstanceContext(name);
+	ContextPtr new_context = context_instancer->InstanceContext(name, render_manager);
 	if (new_context)
 		new_context->SetInstancer(context_instancer);
 	return new_context;

+ 1 - 0
Source/Core/Filter.cpp

@@ -27,6 +27,7 @@
  */
 
 #include "../../Include/RmlUi/Core/Filter.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 

+ 5 - 10
Source/Core/FilterBasic.cpp

@@ -27,10 +27,11 @@
  */
 
 #include "FilterBasic.h"
-#include "../../Include/RmlUi/Core/Core.h"
+#include "../../Include/RmlUi/Core/CompiledFilterShader.h"
+#include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "../../Include/RmlUi/Core/PropertyDictionary.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -41,15 +42,9 @@ bool FilterBasic::Initialise(const String& in_name, float in_value)
 	return true;
 }
 
-CompiledFilterHandle FilterBasic::CompileFilter(Element* /*element*/) const
+CompiledFilter FilterBasic::CompileFilter(Element* element) const
 {
-	CompiledFilterHandle handle = GetRenderInterface()->CompileFilter(name, Dictionary{{"value", Variant(value)}});
-	return handle;
-}
-
-void FilterBasic::ReleaseCompiledFilter(Element* /*element*/, CompiledFilterHandle filter_handle) const
-{
-	GetRenderInterface()->ReleaseCompiledFilter(filter_handle);
+	return element->GetRenderManager()->CompileFilter(name, Dictionary{{"value", Variant(value)}});
 }
 
 FilterBasicInstancer::FilterBasicInstancer(ValueType value_type, const char* default_value)

+ 1 - 3
Source/Core/FilterBasic.h

@@ -38,9 +38,7 @@ class FilterBasic : public Filter {
 public:
 	bool Initialise(const String& name, float value);
 
-	CompiledFilterHandle CompileFilter(Element* element) const override;
-
-	void ReleaseCompiledFilter(Element* element, CompiledFilterHandle filter_handle) const override;
+	CompiledFilter CompileFilter(Element* element) const override;
 
 private:
 	String name;

+ 4 - 9
Source/Core/FilterBlur.cpp

@@ -27,10 +27,11 @@
  */
 
 #include "FilterBlur.h"
+#include "../../Include/RmlUi/Core/CompiledFilterShader.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "../../Include/RmlUi/Core/PropertyDictionary.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -40,16 +41,10 @@ bool FilterBlur::Initialise(NumericValue in_radius)
 	return Any(in_radius.unit & Unit::LENGTH);
 }
 
-CompiledFilterHandle FilterBlur::CompileFilter(Element* element) const
+CompiledFilter FilterBlur::CompileFilter(Element* element) const
 {
 	const float radius = element->ResolveLength(radius_value);
-	CompiledFilterHandle handle = GetRenderInterface()->CompileFilter("blur", Dictionary{{"radius", Variant(radius)}});
-	return handle;
-}
-
-void FilterBlur::ReleaseCompiledFilter(Element* /*element*/, CompiledFilterHandle filter_handle) const
-{
-	GetRenderInterface()->ReleaseCompiledFilter(filter_handle);
+	return element->GetRenderManager()->CompileFilter("blur", Dictionary{{"radius", Variant(radius)}});
 }
 
 void FilterBlur::ExtendInkOverflow(Element* element, Rectanglef& scissor_region) const

+ 1 - 3
Source/Core/FilterBlur.h

@@ -39,9 +39,7 @@ class FilterBlur : public Filter {
 public:
 	bool Initialise(NumericValue radius);
 
-	CompiledFilterHandle CompileFilter(Element* element) const override;
-
-	void ReleaseCompiledFilter(Element* element, CompiledFilterHandle filter_handle) const override;
+	CompiledFilter CompileFilter(Element* element) const override;
 
 	void ExtendInkOverflow(Element* element, Rectanglef& scissor_region) const override;
 

+ 5 - 9
Source/Core/FilterDropShadow.cpp

@@ -27,10 +27,11 @@
  */
 
 #include "FilterDropShadow.h"
+#include "../../Include/RmlUi/Core/CompiledFilterShader.h"
 #include "../../Include/RmlUi/Core/Element.h"
 #include "../../Include/RmlUi/Core/PropertyDefinition.h"
 #include "../../Include/RmlUi/Core/PropertyDictionary.h"
-#include "../../Include/RmlUi/Core/RenderInterface.h"
+#include "../../Include/RmlUi/Core/RenderManager.h"
 
 namespace Rml {
 
@@ -43,7 +44,7 @@ bool FilterDropShadow::Initialise(Colourb in_color, NumericValue in_offset_x, Nu
 	return Any(in_offset_x.unit & Unit::LENGTH) && Any(in_offset_y.unit & Unit::LENGTH) && Any(in_sigma.unit & Unit::LENGTH);
 }
 
-CompiledFilterHandle FilterDropShadow::CompileFilter(Element* element) const
+CompiledFilter FilterDropShadow::CompileFilter(Element* element) const
 {
 	const float sigma = element->ResolveLength(value_sigma);
 	const Vector2f offset = {
@@ -51,15 +52,10 @@ CompiledFilterHandle FilterDropShadow::CompileFilter(Element* element) const
 		element->ResolveLength(value_offset_y),
 	};
 
-	CompiledFilterHandle handle = GetRenderInterface()->CompileFilter("drop-shadow",
+	CompiledFilter filter = element->GetRenderManager()->CompileFilter("drop-shadow",
 		Dictionary{{"color", Variant(color)}, {"offset", Variant(offset)}, {"sigma", Variant(sigma)}});
 
-	return handle;
-}
-
-void FilterDropShadow::ReleaseCompiledFilter(Element* /*element*/, CompiledFilterHandle filter_handle) const
-{
-	GetRenderInterface()->ReleaseCompiledFilter(filter_handle);
+	return filter;
 }
 
 void FilterDropShadow::ExtendInkOverflow(Element* element, Rectanglef& scissor_region) const

+ 1 - 3
Source/Core/FilterDropShadow.h

@@ -39,9 +39,7 @@ class FilterDropShadow : public Filter {
 public:
 	bool Initialise(Colourb color, NumericValue offset_x, NumericValue offset_y, NumericValue sigma);
 
-	CompiledFilterHandle CompileFilter(Element* element) const override;
-
-	void ReleaseCompiledFilter(Element* element, CompiledFilterHandle filter_handle) const override;
+	CompiledFilter CompileFilter(Element* element) const override;
 
 	void ExtendInkOverflow(Element* element, Rectanglef& scissor_region) const override;
 

+ 3 - 3
Source/Core/FontEngineDefault/FontEngineInterfaceDefault.cpp

@@ -77,11 +77,11 @@ int FontEngineInterfaceDefault::GetStringWidth(FontFaceHandle handle, const Stri
 	return handle_default->GetStringWidth(string, letter_spacing, prior_character);
 }
 
-int FontEngineInterfaceDefault::GenerateString(FontFaceHandle handle, FontEffectsHandle font_effects_handle, const String& string,
-	const Vector2f& position, ColourbPremultiplied colour, float opacity, float letter_spacing, GeometryList& geometry)
+int FontEngineInterfaceDefault::GenerateString(RenderManager& render_manager, FontFaceHandle handle, FontEffectsHandle font_effects_handle,
+	const String& string, const Vector2f& position, ColourbPremultiplied colour, float opacity, float letter_spacing, TexturedMeshList& mesh_list)
 {
 	auto handle_default = reinterpret_cast<FontFaceHandleDefault*>(handle);
-	return handle_default->GenerateString(geometry, string, position, colour, opacity, letter_spacing, (int)font_effects_handle);
+	return handle_default->GenerateString(render_manager, mesh_list, string, position, colour, opacity, letter_spacing, (int)font_effects_handle);
 }
 
 int FontEngineInterfaceDefault::GetVersion(FontFaceHandle handle)

+ 4 - 4
Source/Core/FontEngineDefault/FontEngineInterfaceDefault.h

@@ -50,17 +50,17 @@ public:
 	FontFaceHandle GetFontFaceHandle(const String& family, Style::FontStyle style, Style::FontWeight weight, int size) override;
 
 	/// Prepares for font effects by configuring a new, or returning an existing, layer configuration.
-	FontEffectsHandle PrepareFontEffects(FontFaceHandle, const FontEffectList& font_effects) override;
+	FontEffectsHandle PrepareFontEffects(FontFaceHandle handle, const FontEffectList& font_effects) override;
 
 	/// Returns the font metrics of the given font face.
 	const FontMetrics& GetFontMetrics(FontFaceHandle handle) override;
 
 	/// Returns the width a string will take up if rendered with this handle.
-	int GetStringWidth(FontFaceHandle, const String& string, float letter_spacing, Character prior_character) override;
+	int GetStringWidth(FontFaceHandle handle, const String& string, float letter_spacing, Character prior_character) override;
 
 	/// Generates the geometry required to render a single line of text.
-	int GenerateString(FontFaceHandle, FontEffectsHandle, const String& string, const Vector2f& position, ColourbPremultiplied colour, float opacity,
-		float letter_spacing, GeometryList& geometry) override;
+	int GenerateString(RenderManager& render_manager, FontFaceHandle face_handle, FontEffectsHandle effects_handle, const String& string,
+		const Vector2f& position, ColourbPremultiplied colour, float opacity, float letter_spacing, TexturedMeshList& mesh_list) override;
 
 	/// Returns the current version of the font face.
 	int GetVersion(FontFaceHandle handle) override;

+ 18 - 22
Source/Core/FontEngineDefault/FontFaceHandleDefault.cpp

@@ -33,6 +33,7 @@
 #include "FontProvider.h"
 #include "FreeTypeInterface.h"
 #include <algorithm>
+#include <numeric>
 
 namespace Rml {
 
@@ -188,8 +189,8 @@ bool FontFaceHandleDefault::GenerateLayerTexture(UniquePtr<const byte[]>& textur
 	return it->layer->GenerateTexture(texture_data, texture_dimensions, texture_id, glyphs);
 }
 
-int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String& string, const Vector2f position, const ColourbPremultiplied colour,
-	const float opacity, const float letter_spacing, const int layer_configuration_index)
+int FontFaceHandleDefault::GenerateString(RenderManager& render_manager, TexturedMeshList& mesh_list, const String& string, const Vector2f position,
+	const ColourbPremultiplied colour, const float opacity, const float letter_spacing, const int layer_configuration_index)
 {
 	int geometry_index = 0;
 	int line_width = 0;
@@ -202,12 +203,15 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 	// Fetch the requested configuration and generate the geometry for each one.
 	const LayerConfiguration& layer_configuration = layer_configurations[layer_configuration_index];
 
-	// Reserve for the common case of one texture per layer.
-	geometry.reserve(layer_configuration.size());
+	// Each texture represents one geometry.
+	const int num_geometries = std::accumulate(layer_configuration.begin(), layer_configuration.end(), 0,
+		[](int sum, const FontFaceLayer* layer) { return sum + layer->GetNumTextures(); });
 
-	for (size_t i = 0; i < layer_configuration.size(); ++i)
+	mesh_list.resize(num_geometries);
+
+	for (size_t layer_index = 0; layer_index < layer_configuration.size(); ++layer_index)
 	{
-		FontFaceLayer* layer = layer_configuration[i];
+		FontFaceLayer* layer = layer_configuration[layer_index];
 
 		ColourbPremultiplied layer_colour;
 		if (layer == base_layer)
@@ -216,25 +220,20 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 			layer_colour = layer->GetColour(opacity);
 
 		const int num_textures = layer->GetNumTextures();
-
 		if (num_textures == 0)
 			continue;
 
-		// Resize the geometry list if required.
-		if ((int)geometry.size() < geometry_index + num_textures)
-			geometry.resize(geometry_index + num_textures);
-
-		RMLUI_ASSERT(geometry_index < (int)geometry.size());
-
-		// Bind the textures to the geometries.
-		for (int tex_index = 0; tex_index < num_textures; ++tex_index)
-			geometry[geometry_index + tex_index].SetTexture(layer->GetTexture(tex_index));
+		RMLUI_ASSERT(geometry_index + num_textures <= (int)mesh_list.size());
 
 		line_width = 0;
 		Character prior_character = Character::Null;
 
-		geometry[geometry_index].GetIndices().reserve(string.size() * 6);
-		geometry[geometry_index].GetVertices().reserve(string.size() * 4);
+		// Set the mesh and textures to the geometries.
+		for (int tex_index = 0; tex_index < num_textures; ++tex_index)
+			mesh_list[geometry_index + tex_index].texture = layer->GetTexture(render_manager, tex_index);
+
+		mesh_list[geometry_index].mesh.indices.reserve(string.size() * 6);
+		mesh_list[geometry_index].mesh.vertices.reserve(string.size() * 4);
 
 		for (auto it_string = StringIteratorU8(string); it_string; ++it_string)
 		{
@@ -252,7 +251,7 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 			if (layer == base_layer && glyph->color_format == ColorFormat::RGBA8)
 				glyph_color = ColourbPremultiplied(layer_colour.alpha, layer_colour.alpha);
 
-			layer->GenerateGeometry(&geometry[geometry_index], character, Vector2f(position.x + line_width, position.y), glyph_color);
+			layer->GenerateGeometry(&mesh_list[geometry_index], character, Vector2f(position.x + line_width, position.y), glyph_color);
 
 			line_width += glyph->advance;
 			line_width += (int)letter_spacing;
@@ -262,9 +261,6 @@ int FontFaceHandleDefault::GenerateString(GeometryList& geometry, const String&
 		geometry_index += num_textures;
 	}
 
-	// Cull any excess geometry from a previous generation.
-	geometry.resize(geometry_index);
-
 	return Math::Max(line_width, 0);
 }
 

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini