DebugHud.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. //
  2. // Copyright (c) 2017 the Atomic project.
  3. // Copyright (c) 2008-2015 the Urho3D project.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include <UI/UI.h>
  24. #include "../../Core/CoreEvents.h"
  25. #include "../../Core/Profiler.h"
  26. #include "../../Engine/Engine.h"
  27. #include "../../Graphics/Graphics.h"
  28. #include "../../Graphics/Renderer.h"
  29. #include "../../IO/Log.h"
  30. #include "../../UI/SystemUI/SystemUI.h"
  31. #include "../../UI/SystemUI/DebugHud.h"
  32. #include "../../DebugNew.h"
  33. namespace Atomic
  34. {
  35. static const char* qualityTexts[] =
  36. {
  37. "Low",
  38. "Med",
  39. "High"
  40. };
  41. static const char* shadowQualityTexts[] =
  42. {
  43. "16bit Low",
  44. "24bit Low",
  45. "16bit High",
  46. "24bit High"
  47. };
  48. static const float FPS_UPDATE_INTERVAL = 0.5f;
  49. DebugHud::DebugHud(Context* context) :
  50. Object(context),
  51. profilerMaxDepth_(M_MAX_UNSIGNED),
  52. profilerMode_(DEBUG_HUD_PROFILE_PERFORMANCE),
  53. profilerInterval_(1000),
  54. useRendererStats_(true),
  55. mode_(DEBUGHUD_SHOW_NONE),
  56. fpsTimeSinceUpdate_(FPS_UPDATE_INTERVAL),
  57. fpsFramesSinceUpdate_(0),
  58. fps_(0)
  59. {
  60. ResetExtents();
  61. RecalculateWindowPositions();
  62. SubscribeToEvent(E_SYSTEMUIFRAME, ATOMIC_HANDLER(DebugHud, RenderUi));
  63. }
  64. DebugHud::~DebugHud()
  65. {
  66. UnsubscribeFromAllEvents();
  67. }
  68. // TODO: get rid of `useRootExtents` and replace calls to SetExtents(true, ...) with ResetExtents().
  69. void DebugHud::SetExtents(bool useRootExtents, const IntVector2& position, const IntVector2& size)
  70. {
  71. if (useRootExtents)
  72. ResetExtents();
  73. else
  74. {
  75. auto bottomRight = position + size;
  76. extents_ = IntRect(position.x_, position.y_, bottomRight.x_, bottomRight.y_);
  77. }
  78. RecalculateWindowPositions();
  79. }
  80. void DebugHud::ResetExtents()
  81. {
  82. // auto uiRoot = GetSubsystem<UI>()->GetRootWidget();
  83. // auto topLeft = uiRoot->GetPosition();
  84. // auto bottomRight = uiRoot->GetPosition() + uiRoot->GetSize();
  85. // extents_ = IntRect(topLeft.x_, topLeft.y_, bottomRight.x_, bottomRight.y_);
  86. auto graphics = GetSubsystem<Graphics>();
  87. extents_ = IntRect(0, 0, graphics->GetWidth(), graphics->GetHeight());
  88. }
  89. void DebugHud::RecalculateWindowPositions()
  90. {
  91. auto withinExtents = [&](IntVector2 pos) -> IntVector2
  92. {
  93. if (pos.x_ < 0)
  94. pos.x_ += extents_.right_;
  95. else if (pos.x_ > 0)
  96. pos.x_ += extents_.left_;
  97. else
  98. pos.x_ = extents_.left_;
  99. if (pos.y_ < 0)
  100. pos.y_ += extents_.bottom_;
  101. else if (pos.y_ > 0)
  102. pos.y_ += extents_.top_;
  103. else
  104. pos.y_ = extents_.top_;
  105. return pos;
  106. };
  107. posMode_ = withinExtents({0, -30});
  108. posStats_ = withinExtents({0, 0});
  109. posProfiler_ = withinExtents({-530, 0});
  110. }
  111. void DebugHud::SetProfilerMode(DebugHudProfileMode mode)
  112. {
  113. profilerMode_ = mode;
  114. }
  115. void DebugHud::SetMode(unsigned mode)
  116. {
  117. mode_ = mode;
  118. }
  119. void DebugHud::CycleMode()
  120. {
  121. switch (mode_)
  122. {
  123. case DEBUGHUD_SHOW_NONE:
  124. SetMode(DEBUGHUD_SHOW_STATS);
  125. break;
  126. case DEBUGHUD_SHOW_STATS:
  127. SetMode(DEBUGHUD_SHOW_MODE);
  128. break;
  129. case DEBUGHUD_SHOW_MODE:
  130. SetMode(DEBUGHUD_SHOW_PROFILER);
  131. break;
  132. case DEBUGHUD_SHOW_PROFILER:
  133. SetMode(DEBUGHUD_SHOW_ALL);
  134. break;
  135. case DEBUGHUD_SHOW_ALL:
  136. default:
  137. SetMode(DEBUGHUD_SHOW_NONE);
  138. break;
  139. }
  140. }
  141. void DebugHud::SetProfilerMaxDepth(unsigned depth)
  142. {
  143. profilerMaxDepth_ = depth;
  144. }
  145. void DebugHud::SetProfilerInterval(float interval)
  146. {
  147. profilerInterval_ = (unsigned)Max((int)(interval * 1000.0f), 0);
  148. }
  149. void DebugHud::SetUseRendererStats(bool enable)
  150. {
  151. useRendererStats_ = enable;
  152. }
  153. void DebugHud::Toggle(unsigned mode)
  154. {
  155. SetMode(GetMode() ^ mode);
  156. }
  157. void DebugHud::ToggleAll()
  158. {
  159. Toggle(DEBUGHUD_SHOW_ALL);
  160. }
  161. float DebugHud::GetProfilerInterval() const
  162. {
  163. return (float)profilerInterval_ / 1000.0f;
  164. }
  165. void DebugHud::SetAppStats(const String& label, const Variant& stats)
  166. {
  167. SetAppStats(label, stats.ToString());
  168. }
  169. void DebugHud::SetAppStats(const String& label, const String& stats)
  170. {
  171. bool newLabel = !appStats_.Contains(label);
  172. appStats_[label] = stats;
  173. if (newLabel)
  174. appStats_.Sort();
  175. }
  176. bool DebugHud::ResetAppStats(const String& label)
  177. {
  178. return appStats_.Erase(label);
  179. }
  180. void DebugHud::ClearAppStats()
  181. {
  182. appStats_.Clear();
  183. }
  184. void DebugHud::RenderUi(StringHash eventType, VariantMap& eventData)
  185. {
  186. Renderer* renderer = GetSubsystem<Renderer>();
  187. Graphics* graphics = GetSubsystem<Graphics>();
  188. const auto backgroundTextWindowFlags = ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|
  189. ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoInputs;
  190. if (mode_ & DEBUGHUD_SHOW_MODE)
  191. {
  192. ImGui::SetNextWindowPos(ImVec2(posMode_.x_, posMode_.y_));
  193. if (ImGui::Begin("DebugHud mode", 0, ImVec2(1024, 30), 0, backgroundTextWindowFlags))
  194. {
  195. ImGui::Text("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s API:%s",
  196. qualityTexts[renderer->GetTextureQuality()],
  197. qualityTexts[renderer->GetMaterialQuality()],
  198. renderer->GetSpecularLighting() ? "On" : "Off",
  199. renderer->GetDrawShadows() ? "On" : "Off",
  200. renderer->GetShadowMapSize(),
  201. shadowQualityTexts[renderer->GetShadowQuality()],
  202. renderer->GetMaxOccluderTriangles() > 0 ? "On" : "Off",
  203. renderer->GetDynamicInstancing() ? "On" : "Off",
  204. graphics->GetApiName().CString());
  205. }
  206. ImGui::End();
  207. }
  208. if (mode_ & DEBUGHUD_SHOW_STATS)
  209. {
  210. fpsTimeSinceUpdate_ += GetSubsystem<Time>()->GetTimeStep();
  211. ++fpsFramesSinceUpdate_;
  212. if (fpsTimeSinceUpdate_ > FPS_UPDATE_INTERVAL)
  213. {
  214. fps_ = (int)(fpsFramesSinceUpdate_ / fpsTimeSinceUpdate_);
  215. fpsFramesSinceUpdate_ = 0;
  216. fpsTimeSinceUpdate_ = 0;
  217. }
  218. unsigned primitives, batches;
  219. if (!useRendererStats_)
  220. {
  221. primitives = graphics->GetNumPrimitives();
  222. batches = graphics->GetNumBatches();
  223. }
  224. else
  225. {
  226. primitives = renderer->GetNumPrimitives();
  227. batches = renderer->GetNumBatches();
  228. }
  229. String stats;
  230. unsigned singlePassPrimitives = graphics->GetSinglePassPrimitives();
  231. unsigned editorPrimitives = graphics->GetNumPrimitives() - renderer->GetNumPrimitives();
  232. ImGui::SetNextWindowPos(ImVec2(posStats_.x_, posStats_.y_));
  233. if (ImGui::Begin("DebugHud Stats", 0, ImVec2(0, 0), 0,
  234. backgroundTextWindowFlags))
  235. {
  236. ImGui::Text("FPS %d", fps_);
  237. if (singlePassPrimitives)
  238. {
  239. ImGui::Text("Triangles (All passes) %u", primitives);
  240. ImGui::Text("Triangles (Single pass) %u", singlePassPrimitives);
  241. ImGui::Text("Triangles (Editor) %u", editorPrimitives);
  242. }
  243. else
  244. ImGui::Text("Triangles %u", primitives);
  245. ImGui::Text("Batches %u", batches);
  246. ImGui::Text("Views %u", renderer->GetNumViews());
  247. ImGui::Text("Lights %u", renderer->GetNumLights(true));
  248. ImGui::Text("Shadowmaps %u", renderer->GetNumShadowMaps(true));
  249. ImGui::Text("Occluders %u", renderer->GetNumOccluders(true));
  250. for (HashMap<String, String>::ConstIterator i = appStats_.Begin(); i != appStats_.End(); ++i)
  251. ImGui::Text("%s %s", i->first_.CString(), i->second_.CString());
  252. }
  253. ImGui::End();
  254. }
  255. if (mode_ & DEBUGHUD_SHOW_PROFILER)
  256. {
  257. ImGui::SetNextWindowPos(ImVec2(posProfiler_.x_, posProfiler_.y_));
  258. if (ImGui::Begin("DebugHud Metrics", 0, ImVec2(530, 1080), 0, backgroundTextWindowFlags))
  259. {
  260. if (profilerMode_ == DEBUG_HUD_PROFILE_PERFORMANCE)
  261. {
  262. if (profilerTimer_.GetMSec(false) >= profilerInterval_)
  263. {
  264. profilerTimer_.Reset();
  265. Profiler* profiler = GetSubsystem<Profiler>();
  266. if (profiler)
  267. {
  268. profilerOutput_ = profiler->PrintData(false, false, profilerMaxDepth_);
  269. profiler->BeginInterval();
  270. }
  271. }
  272. ImGui::Text(profilerOutput_.CString());
  273. }
  274. else
  275. {
  276. Metrics* metrics = GetSubsystem<Metrics>();
  277. if (metrics)
  278. {
  279. if (metrics->GetEnabled())
  280. {
  281. metricsSnapshot_.Clear();
  282. metrics->Capture(&metricsSnapshot_);
  283. ImGui::Text(metricsSnapshot_.PrintData(2).CString());
  284. }
  285. else
  286. ImGui::Text("Metrics system not enabled");
  287. }
  288. else
  289. ImGui::Text("Metrics subsystem not found");
  290. }
  291. }
  292. ImGui::End();
  293. }
  294. }
  295. }