ElementDocument.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019-2023 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "../Common/TestsInterface.h"
  29. #include "../Common/TestsShell.h"
  30. #include <RmlUi/Core/Context.h>
  31. #include <RmlUi/Core/Element.h>
  32. #include <RmlUi/Core/ElementDocument.h>
  33. #include <RmlUi/Core/Factory.h>
  34. #include <RmlUi/Core/StringUtilities.h>
  35. #include <RmlUi/Core/Types.h>
  36. #include <doctest.h>
  37. #include <nanobench.h>
  38. using namespace ankerl;
  39. using namespace Rml;
  40. static const String document_rml = R"(
  41. <rml>
  42. <head>
  43. <link type="text/template" href="/assets/window.rml"/>
  44. <title>Benchmark Sample</title>
  45. <style>
  46. body.window
  47. {
  48. left: 50px;
  49. top: 50px;
  50. width: 800px;
  51. height: 200px;
  52. }
  53. #performance
  54. {
  55. width: 500px;
  56. height: 300px;
  57. }
  58. </style>
  59. </head>
  60. <body template="window">
  61. <div id="performance">
  62. <div class="row">
  63. <div class="col col1"><button class="expand" index="3">+</button>&nbsp;<a>Route 15</a></div>
  64. <div class="col col23"><input type="range" class="assign_range" min="0" max="20" value="3"/></div>
  65. <div class="col col4">Assigned</div>
  66. <select>
  67. <option>Red</option><option>Blue</option><option selected>Green</option><option style="background-color: yellow;">Yellow</option>
  68. </select>
  69. <div class="inrow unmark_collapse">
  70. <div class="col col123 assign_text">Assign to route</div>
  71. <div class="col col4">
  72. <input type="submit" class="vehicle_depot_assign_confirm" quantity="0">Confirm</input>
  73. </div>
  74. </div>
  75. </div>
  76. </div>
  77. </body>
  78. </rml>
  79. )";
  80. TEST_CASE("elementdocument")
  81. {
  82. Context* context = TestsShell::GetContext();
  83. REQUIRE(context);
  84. std::string title = "ElementDocument";
  85. String modified_document_rml = document_rml;
  86. bool clear_style_sheet_cache = false;
  87. SUBCASE("ElementDocument")
  88. {
  89. clear_style_sheet_cache = false;
  90. }
  91. SUBCASE("ElementDocument w/o Layout Cache")
  92. {
  93. title += " w/o Layout Cache";
  94. clear_style_sheet_cache = false;
  95. modified_document_rml = StringUtilities::Replace(document_rml, "<body", "<body rmlui-disable-layout-cache");
  96. }
  97. SUBCASE("ElementDocument w/o Style Sheet Cache")
  98. {
  99. title += " w/o Style Sheet Cache";
  100. clear_style_sheet_cache = true;
  101. }
  102. auto ConditionallyClearStyleSheetCache = [&]() {
  103. if (clear_style_sheet_cache)
  104. Factory::ClearStyleSheetCache();
  105. };
  106. nanobench::Bench bench;
  107. bench.title(title);
  108. bench.timeUnit(std::chrono::microseconds(1), "us");
  109. bench.relative(true);
  110. bench.warmup(10);
  111. bench.run("LoadDocument", [&] {
  112. ConditionallyClearStyleSheetCache();
  113. ElementDocument* document = context->LoadDocumentFromMemory(modified_document_rml);
  114. document->Close();
  115. context->Update();
  116. });
  117. bench.run("LoadDocument + Show", [&] {
  118. ConditionallyClearStyleSheetCache();
  119. ElementDocument* document = context->LoadDocumentFromMemory(modified_document_rml);
  120. document->Show();
  121. document->Close();
  122. context->Update();
  123. });
  124. bench.run("LoadDocument + Show + Update", [&] {
  125. ConditionallyClearStyleSheetCache();
  126. ElementDocument* document = context->LoadDocumentFromMemory(modified_document_rml);
  127. document->Show();
  128. context->Update();
  129. document->Close();
  130. context->Update();
  131. });
  132. bench.run("LoadDocument + Show + Update + Render", [&] {
  133. ConditionallyClearStyleSheetCache();
  134. ElementDocument* document = context->LoadDocumentFromMemory(modified_document_rml);
  135. document->Show();
  136. context->Update();
  137. TestsShell::BeginFrame();
  138. context->Render();
  139. TestsShell::PresentFrame();
  140. document->Close();
  141. context->Update();
  142. });
  143. }
  144. TEST_CASE("elementdocument.render_stats")
  145. {
  146. Context* context = TestsShell::GetContext();
  147. REQUIRE(context);
  148. ElementDocument* document = context->LoadDocumentFromMemory(document_rml);
  149. document->Show();
  150. const String stats = TestsShell::GetRenderStats();
  151. MESSAGE(stats);
  152. TestsShell::RenderLoop();
  153. document->Close();
  154. }
  155. static const String document_isolation_rml = R"(
  156. <rml>
  157. <head>
  158. <link type="text/rcss" href="/assets/rml.rcss"/>
  159. <style>
  160. body {
  161. width: 800px;
  162. height: 600px;
  163. background-color: #ddd;
  164. font-family: LatoLatin;
  165. font-size: 14px;
  166. }
  167. scrollbarvertical {
  168. width: 12px;
  169. cursor: arrow;
  170. margin-right: -1px;
  171. padding-right: 1px;
  172. }
  173. scrollbarvertical slidertrack {
  174. background-color: #f0f0f0;
  175. }
  176. scrollbarvertical sliderbar {
  177. background-color: #666;
  178. }
  179. .container {
  180. width: 200px;
  181. margin: 20px;
  182. padding: 10px;
  183. background-color: #bbb;
  184. }
  185. .container > div {
  186. background-color: #aaa;
  187. margin: 5px;
  188. padding: 10px;
  189. }
  190. #overflow-container {
  191. overflow: scroll;
  192. height: 150px;
  193. }
  194. #absolute-container {
  195. position: absolute;
  196. top: 300px;
  197. left: 300px;
  198. max-height: 300px;
  199. overflow: scroll;
  200. }
  201. </style>
  202. </head>
  203. <body %s>
  204. <div class="container" id="overflow-container">
  205. <div id="overflow-item">Overflow item</div>
  206. %s
  207. </div>
  208. <div class="container" id="absolute-container">
  209. <div id="absolute-item">Absolute item</div>
  210. %s
  211. </div>
  212. <div class="container" id="inflow-container">
  213. <div id="inflow-item">Inflow item</div>
  214. %s
  215. </div>
  216. </body>
  217. </rml>
  218. )";
  219. TEST_CASE("elementdocument.layout_cache")
  220. {
  221. Context* context = TestsShell::GetContext();
  222. constexpr int num_items = 20;
  223. auto CreateIsolationDocument = [&](bool disable_layout_cache) {
  224. return CreateString(document_isolation_rml.c_str(), disable_layout_cache ? "rmlui-disable-layout-cache" : "",
  225. StringUtilities::RepeatString("<div>Item text</div>", num_items).c_str(),
  226. StringUtilities::RepeatString("<div>Item text</div>", num_items).c_str(),
  227. StringUtilities::RepeatString("<div>Item text</div>", num_items).c_str());
  228. };
  229. {
  230. ElementDocument* document = context->LoadDocumentFromMemory(CreateIsolationDocument(false));
  231. document->Show();
  232. TestsShell::RenderLoop();
  233. document->Close();
  234. context->Update();
  235. }
  236. for (const String& modify_item_id : {"overflow-item", "absolute-item", "inflow-item"})
  237. {
  238. nanobench::Bench bench;
  239. bench.title("Layout cache (" + modify_item_id + ")");
  240. bench.timeUnit(std::chrono::microseconds(1), "us");
  241. bench.relative(true);
  242. {
  243. ElementDocument* document = context->LoadDocumentFromMemory(CreateIsolationDocument(false));
  244. document->Show();
  245. context->Update();
  246. Element* element = document->GetElementById(modify_item_id);
  247. bench.run("Enabled layout cache", [&] {
  248. element->SetInnerRML("Changed item");
  249. context->Update();
  250. });
  251. document->Close();
  252. context->Update();
  253. }
  254. {
  255. ElementDocument* document = context->LoadDocumentFromMemory(CreateIsolationDocument(true));
  256. document->Show();
  257. context->Update();
  258. Element* element = document->GetElementById(modify_item_id);
  259. bench.run("Disabled layout cache", [&] {
  260. element->SetInnerRML("Changed item");
  261. context->Update();
  262. });
  263. document->Close();
  264. context->Update();
  265. }
  266. }
  267. TestsShell::ShutdownShell();
  268. }