Ver Fonte

Add data binding benchmark

Michael Ragazzon há 3 anos atrás
pai
commit
9a6be7b0bb
1 ficheiros alterados com 248 adições e 0 exclusões
  1. 248 0
      Tests/Source/Benchmarks/DataBinding.cpp

+ 248 - 0
Tests/Source/Benchmarks/DataBinding.cpp

@@ -0,0 +1,248 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "../Common/TestsShell.h"
+#include <RmlUi/Core/Context.h>
+#include <RmlUi/Core/DataModelHandle.h>
+#include <RmlUi/Core/Element.h>
+#include <RmlUi/Core/ElementDocument.h>
+#include <doctest.h>
+#include <nanobench.h>
+
+using namespace Rml;
+using namespace ankerl;
+
+static const String document_rml = R"(
+<rml>
+<head>
+	<title>Test</title>
+	<link type="text/rcss" href="/assets/rml.rcss"/>
+	<link type="text/template" href="/assets/window.rml"/>
+	<style>
+		body.window
+		{
+			left: 50px;
+			right: 50px;
+			top: 30px;
+			bottom: 30px;
+			max-width: -1px;
+			max-height: -1px;
+		}
+		div#content
+		{
+			text-align: left;
+			padding: 50px;
+			box-sizing: border-box;
+		}
+	</style>
+</head>
+
+<body template="window">
+<div data-model="basics">
+<h1>Globals</h1>
+<p id="i">0</p>
+
+<p id="i0">{{ i0 }}</p>
+<p id="i1">{{ i1 }}</p>
+<p id="i2">{{ i2 }}</p>
+<p id="i3">{{ i3 }}</p>
+
+<h1>Basic</h1>
+<p id="b_a">{{ basic.a }}</p>
+<p id="b_b">{{ basic.b }}</p>
+<p id="b_c">{{ basic.c.val }}</p>
+
+<h1>Arrays</h1>
+<p id="array"><span>10 </span><span>20 </span><span>30 </span></p>
+<p><span data-for="arrays.a">{{ it }} </span></p>
+<p><span data-for="arrays.b">{{ it }} </span></p>
+<p><span data-for="arrays.c">{{ it.val }} </span></p>
+<p><span data-for="arrays.d">{{ 'a: ' + it.a + ', b: ' + it.b + ', c: ' + it.c.val + ' :: ' }}</span></p>
+</div>
+</body>
+</rml>
+)";
+
+struct StringWrap {
+	StringWrap(String val = "wrap_default") : val(val) {}
+	String val;
+};
+
+struct Globals {
+	int i0 = 0;
+	int* i1 = new int(1);
+	UniquePtr<int> i2 = MakeUnique<int>(2);
+	SharedPtr<int> i3 = MakeShared<int>(3);
+} globals;
+
+struct Basic {
+	int a = 1;
+	int* b = new int(2);
+	StringWrap* c = new StringWrap("basic.c");
+};
+
+struct Arrays {
+	Vector<int> a = {10, 11, 12};
+	Vector<int*> b = {new int(20), new int(21), new int(22)};
+	Vector<StringWrap> c = {StringWrap("c1"), StringWrap("c2"), StringWrap("c3")};
+	Vector<Basic> d = {Basic{10}, Basic{20}, Basic{30}};
+};
+
+static UniquePtr<Basic> basic;
+static UniquePtr<Arrays> arrays;
+
+static DataModelHandle InitializeDataBindings(Context* context)
+{
+	Rml::DataModelConstructor constructor = context->CreateDataModel("basics");
+	if (!constructor)
+		return DataModelHandle();
+
+	if (auto handle = constructor.RegisterStruct<StringWrap>())
+		handle.RegisterMember("val", &StringWrap::val);
+
+	constructor.Bind("i0", &globals.i0);
+	constructor.Bind("i1", &globals.i1);
+	constructor.Bind("i2", &globals.i2);
+	constructor.Bind("i3", &globals.i3);
+
+	if (auto handle = constructor.RegisterStruct<Basic>())
+	{
+		handle.RegisterMember("a", &Basic::a);
+		handle.RegisterMember("b", &Basic::b);
+		handle.RegisterMember("c", &Basic::c);
+	}
+	basic = MakeUnique<Basic>();
+	constructor.Bind("basic", basic.get());
+
+	constructor.RegisterArray<decltype(Arrays::a)>();
+	constructor.RegisterArray<decltype(Arrays::b)>();
+	constructor.RegisterArray<decltype(Arrays::c)>();
+	constructor.RegisterArray<decltype(Arrays::d)>();
+
+	if (auto handle = constructor.RegisterStruct<Arrays>())
+	{
+		handle.RegisterMember("a", &Arrays::a);
+		handle.RegisterMember("b", &Arrays::b);
+		handle.RegisterMember("c", &Arrays::c);
+		handle.RegisterMember("d", &Arrays::d);
+	}
+	arrays = MakeUnique<Arrays>();
+	constructor.Bind("arrays", arrays.get());
+
+	DataModelHandle model_handle = constructor.GetModelHandle();
+
+	return model_handle;
+}
+
+TEST_CASE("data_binding")
+{
+	Context* context = TestsShell::GetContext();
+	REQUIRE(context);
+
+	DataModelHandle model_handle = InitializeDataBindings(context);
+	REQUIRE(model_handle);
+
+	model_handle.DirtyAllVariables();
+
+	ElementDocument* document = context->LoadDocumentFromMemory(document_rml);
+	REQUIRE(document);
+	document->Show();
+
+	context->Update();
+	context->Render();
+
+	SUBCASE("dirty")
+	{
+		nanobench::Bench bench;
+		bench.title("Data bindings: Dirty variables");
+		bench.relative(true);
+
+		bench.run("Reference (Update)", [&] { context->Update(); });
+		bench.run("Dirty one variable", [&] {
+			model_handle.DirtyVariable("i0");
+			context->Update();
+		});
+		bench.run("Dirty big variable", [&] {
+			model_handle.DirtyVariable("arrays");
+			context->Update();
+		});
+		bench.run("Dirty all variables", [&] {
+			model_handle.DirtyAllVariables();
+			context->Update();
+		});
+	}
+
+	SUBCASE("update")
+	{
+		Element* element_i = document->GetElementById("i");
+		Element* element_array = document->GetElementById("array");
+
+		nanobench::Rng rng;
+		nanobench::Bench bench;
+		bench.title("Data bindings: Update");
+		bench.relative(true);
+
+		bench.run("Reference (Integer)", [&] {
+			element_i->SetInnerRML(Rml::ToString(rng.bounded(1000)));
+			context->Update();
+		});
+
+		bench.run("Integer", [&] {
+			globals.i0 = rng.bounded(1000);
+			model_handle.DirtyVariable("i0");
+			context->Update();
+		});
+
+		bench.run("Basic", [&] {
+			basic->a = rng.bounded(2000);
+			*basic->b = rng.bounded(3000);
+			basic->c->val = String("abc") + String(5, char('a' + rng.bounded('z' - 'a')));
+			model_handle.DirtyVariable("basic");
+			context->Update();
+		});
+
+		bench.run("Reference (Arrays)", [&] {
+			element_array->SetInnerRML(
+				Rml::CreateString(128, "<span>%d </span><span>%d </span><span>%d </span>", rng.bounded(5000), rng.bounded(5000), rng.bounded(5000)));
+			context->Update();
+		});
+
+		bench.run("Arrays", [&] {
+			for (auto& v : arrays->a)
+				v = rng.bounded(5000);
+			model_handle.DirtyVariable("arrays");
+			context->Update();
+		});
+	}
+
+	TestsShell::RenderLoop();
+
+	document->Close();
+
+	TestsShell::ShutdownShell();
+}