main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  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 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 <RmlUi/Core.h>
  29. #include <RmlUi/Controls.h>
  30. #include <RmlUi/Debugger.h>
  31. #include <Input.h>
  32. #include <Shell.h>
  33. #include <ShellRenderInterfaceOpenGL.h>
  34. class DemoWindow
  35. {
  36. public:
  37. DemoWindow(const Rml::Core::String &title, const Rml::Core::Vector2f &position, Rml::Core::Context *context)
  38. {
  39. using namespace Rml::Core;
  40. document = context->LoadDocument("basic/benchmark/data/benchmark.rml");
  41. if (document != nullptr)
  42. {
  43. {
  44. document->GetElementById("title")->SetInnerRML(title);
  45. document->SetProperty(PropertyId::Left, Property(position.x, Property::PX));
  46. document->SetProperty(PropertyId::Top, Property(position.y, Property::PX));
  47. }
  48. document->Show();
  49. }
  50. }
  51. void performance_test()
  52. {
  53. /*
  54. FPS values
  55. Original: 18.5 [957f723]
  56. Without property counter: 22.0
  57. With std::string: 23.0 [603fd40]
  58. robin_hood unordered_flat_map: 24.0 [709852f]
  59. Avoid dirtying em's: 27.5
  60. Restructuring update loop: 34.5 [f9892a9]
  61. Element constructor, remove geometry database, remove update() from Context::render: 38.0 [1aab59e]
  62. Replace Dictionary with unordered_flat_map: 40.0 [b04b4e5]
  63. Dirty flag for structure changes: 43.0 [fdf6f53]
  64. Replacing containers: 46.0 [c307140]
  65. Replace 'resize' event with virtual function call: 53.0 [7ad658f]
  66. Use all_properties_dirty flag when constructing elements: 55.0 [fa6bd0a]
  67. Don't double create input elements: 58.0 [e162637]
  68. Memory pool for ElementMeta: 59.0 [ece191a]
  69. Include chobo flat containers: 65.0 [1696aa5]
  70. Move benchmark to its own sample (no code change, fps increase because of removal of animation elements): 68.0 [2433880]
  71. Keep the element's main sizing box local: 69.0 [cf928b2]
  72. Improved hashing of element definition: 70.0 [5cb9e1d]
  73. First usage of computed values (font): 74.0 [04dc275]
  74. Computed values, clipping: 77.0
  75. Computed values, background-color, image-color, opacity: 77.0
  76. Computed values, padding, margin border++: 81.0 [bb70d38]
  77. Computing all the values (only using a few of them yet): 83.0 [9fe9bdf]
  78. Computed transform and other optimizations: 86.0 [654fa09]
  79. Computed layout engine: 90.0 [e18ac30]
  80. Replace style cache by computed values: 96.0
  81. More computed values: 100.0 [edc78bb] !Woo!
  82. Avoid duplicate ToLower++: 103.0 [dec4ef6]
  83. Cleanup and smaller changes: 105.0 [38a559d]
  84. Move dirty properties to elementstyle: 113.0 [0bba316]
  85. (After Windows feature update and MSVC update, no code change): 109.0 [0bba316]
  86. Fixes and element style iterators: 108.0 [0bba316]
  87. Update definition speedup: 115.0 [5d138fa]
  88. (Full release mode, no code change): 135.0 [5d138fa]
  89. EventIDs: 139.0 [d2c3956]
  90. More on events and EventIDs: 146.0 [fd44d9c]
  91. New decorator and font-effect syntax and misc (on a considerably lower clocked CPU): 156 [6619ab4]
  92. Removal of manual reference counting: 170 [cb347e1]
  93. */
  94. if (!document)
  95. return;
  96. Rml::Core::String rml;
  97. for (int i = 0; i < 50; i++)
  98. {
  99. int index = rand() % 1000;
  100. int route = rand() % 50;
  101. int max = (rand() % 40) + 10;
  102. int value = rand() % max;
  103. Rml::Core::String rml_row = Rml::Core::CreateString(1000, R"(
  104. <div class="row">
  105. <div class="col col1"><button class="expand" index="%d">+</button>&nbsp;<a>Route %d</a></div>
  106. <div class="col col23"><input type="range" class="assign_range" min="0" max="%d" value="%d"/></div>
  107. <div class="col col4">Assigned</div>
  108. <div class="inrow unmark_collapse">
  109. <div class="col col123 assign_text">Assign to route</div>
  110. <div class="col col4">
  111. <input type="submit" class="vehicle_depot_assign_confirm" quantity="0">Confirm</input>
  112. </div>
  113. </div>
  114. </div>)",
  115. index,
  116. route,
  117. max,
  118. value
  119. );
  120. rml += rml_row;
  121. }
  122. if (auto el = document->GetElementById("performance"))
  123. el->SetInnerRML(rml);
  124. }
  125. class SimpleEventListener : public Rml::Core::EventListener {
  126. public:
  127. void ProcessEvent(Rml::Core::Event& event) override {
  128. static int i = 0;
  129. event.GetTargetElement()->SetProperty("background-color", i++ % 2 == 0 ? "green" : "orange");
  130. }
  131. } simple_event_listener;
  132. void click_test()
  133. {
  134. if (!document)
  135. return;
  136. Rml::Core::String rml;
  137. static int i = 0;
  138. if(i++ % 2 == 0)
  139. rml = Rml::Core::CreateString(1000, R"( <div style="width: 100px; height: 100px; background-color: #c33;"/> )");
  140. else
  141. rml = "<p>Wohooo!!!</p>";
  142. if (auto el = document->GetElementById("click_test"))
  143. {
  144. el->SetInnerRML(rml);
  145. if (auto child = el->GetChild(0))
  146. child->AddEventListener(Rml::Core::EventId::Mouseup, &simple_event_listener);
  147. }
  148. }
  149. ~DemoWindow()
  150. {
  151. if (document)
  152. {
  153. document->Close();
  154. }
  155. }
  156. Rml::Core::ElementDocument * GetDocument() {
  157. return document;
  158. }
  159. private:
  160. Rml::Core::ElementDocument *document;
  161. };
  162. Rml::Core::Context* context = nullptr;
  163. ShellRenderInterfaceExtensions *shell_renderer;
  164. DemoWindow* window = nullptr;
  165. bool run_loop = true;
  166. bool single_loop = true;
  167. bool run_update = true;
  168. bool single_loop_update = true;
  169. void GameLoop()
  170. {
  171. if (run_update || single_loop_update)
  172. {
  173. window->performance_test();
  174. //window->click_test();
  175. single_loop_update = false;
  176. }
  177. if (run_loop || single_loop)
  178. {
  179. context->Update();
  180. shell_renderer->PrepareRenderBuffer();
  181. context->Render();
  182. shell_renderer->PresentRenderBuffer();
  183. single_loop = false;
  184. }
  185. static constexpr int buffer_size = 200;
  186. static float fps_buffer[buffer_size] = {};
  187. static int buffer_index = 0;
  188. static double t_prev = 0.0f;
  189. double t = Shell::GetElapsedTime();
  190. float dt = float(t - t_prev);
  191. t_prev = t;
  192. static int count_frames = 0;
  193. count_frames += 1;
  194. float fps = 1.0f / dt;
  195. fps_buffer[buffer_index] = fps;
  196. buffer_index = (++buffer_index % buffer_size);
  197. if (window && count_frames > buffer_size / 8)
  198. {
  199. float fps_mean = 0;
  200. for (int i = 0; i < buffer_size; i++)
  201. fps_mean += fps_buffer[(buffer_index + i) % buffer_size];
  202. fps_mean = fps_mean / (float)buffer_size;
  203. auto el = window->GetDocument()->GetElementById("fps");
  204. count_frames = 0;
  205. el->SetInnerRML(Rml::Core::CreateString( 20, "FPS: %f", fps_mean ));
  206. }
  207. }
  208. class Event : public Rml::Core::EventListener
  209. {
  210. public:
  211. Event(const Rml::Core::String& value) : value(value) {}
  212. void ProcessEvent(Rml::Core::Event& event) override
  213. {
  214. using namespace Rml::Core;
  215. if(value == "exit")
  216. Shell::RequestExit();
  217. if (event == "keydown")
  218. {
  219. auto key_identifier = (Rml::Core::Input::KeyIdentifier)event.GetParameter< int >("key_identifier", 0);
  220. if (key_identifier == Rml::Core::Input::KI_SPACE)
  221. {
  222. run_loop = !run_loop;
  223. }
  224. else if (key_identifier == Rml::Core::Input::KI_DOWN)
  225. {
  226. run_loop = false;
  227. single_loop = true;
  228. }
  229. else if (key_identifier == Rml::Core::Input::KI_RIGHT)
  230. {
  231. run_update = false;
  232. single_loop_update = true;
  233. }
  234. else if (key_identifier == Rml::Core::Input::KI_RETURN)
  235. {
  236. run_update = !run_update;
  237. }
  238. else if (key_identifier == Rml::Core::Input::KI_ESCAPE)
  239. {
  240. Shell::RequestExit();
  241. }
  242. else if (key_identifier == Rml::Core::Input::KI_F8)
  243. {
  244. Rml::Debugger::SetVisible(!Rml::Debugger::IsVisible());
  245. }
  246. }
  247. }
  248. void OnDetach(Rml::Core::Element* element) override { delete this; }
  249. private:
  250. Rml::Core::String value;
  251. };
  252. class EventInstancer : public Rml::Core::EventListenerInstancer
  253. {
  254. public:
  255. /// Instances a new event handle for Invaders.
  256. Rml::Core::EventListener* InstanceEventListener(const Rml::Core::String& value, Rml::Core::Element* element) override
  257. {
  258. return new Event(value);
  259. }
  260. };
  261. #if defined RMLUI_PLATFORM_WIN32
  262. #include <windows.h>
  263. int APIENTRY WinMain(HINSTANCE RMLUI_UNUSED_PARAMETER(instance_handle), HINSTANCE RMLUI_UNUSED_PARAMETER(previous_instance_handle), char* RMLUI_UNUSED_PARAMETER(command_line), int RMLUI_UNUSED_PARAMETER(command_show))
  264. #else
  265. int main(int RMLUI_UNUSED_PARAMETER(argc), char** RMLUI_UNUSED_PARAMETER(argv))
  266. #endif
  267. {
  268. #ifdef RMLUI_PLATFORM_WIN32
  269. RMLUI_UNUSED(instance_handle);
  270. RMLUI_UNUSED(previous_instance_handle);
  271. RMLUI_UNUSED(command_line);
  272. RMLUI_UNUSED(command_show);
  273. #else
  274. RMLUI_UNUSED(argc);
  275. RMLUI_UNUSED(argv);
  276. #endif
  277. const int width = 1800;
  278. const int height = 1000;
  279. ShellRenderInterfaceOpenGL opengl_renderer;
  280. shell_renderer = &opengl_renderer;
  281. // Generic OS initialisation, creates a window and attaches OpenGL.
  282. if (!Shell::Initialise() ||
  283. !Shell::OpenWindow("Benchmark Sample", shell_renderer, width, height, true))
  284. {
  285. Shell::Shutdown();
  286. return -1;
  287. }
  288. // RmlUi initialisation.
  289. Rml::Core::SetRenderInterface(&opengl_renderer);
  290. opengl_renderer.SetViewport(width, height);
  291. ShellSystemInterface system_interface;
  292. Rml::Core::SetSystemInterface(&system_interface);
  293. Rml::Core::Initialise();
  294. // Create the main RmlUi context and set it on the shell's input layer.
  295. context = Rml::Core::CreateContext("main", Rml::Core::Vector2i(width, height));
  296. if (context == nullptr)
  297. {
  298. Rml::Core::Shutdown();
  299. Shell::Shutdown();
  300. return -1;
  301. }
  302. Rml::Controls::Initialise();
  303. Rml::Debugger::Initialise(context);
  304. Input::SetContext(context);
  305. shell_renderer->SetContext(context);
  306. EventInstancer event_listener_instancer;
  307. Rml::Core::Factory::RegisterEventListenerInstancer(&event_listener_instancer);
  308. Shell::LoadFonts("assets/");
  309. window = new DemoWindow("Benchmark sample", Rml::Core::Vector2f(81, 100), context);
  310. window->GetDocument()->AddEventListener(Rml::Core::EventId::Keydown, new Event("hello"));
  311. window->GetDocument()->AddEventListener(Rml::Core::EventId::Keyup, new Event("hello"));
  312. window->GetDocument()->AddEventListener(Rml::Core::EventId::Animationend, new Event("hello"));
  313. Shell::EventLoop(GameLoop);
  314. delete window;
  315. // Shutdown RmlUi.
  316. Rml::Core::Shutdown();
  317. Shell::CloseWindow();
  318. Shell::Shutdown();
  319. return 0;
  320. }