DebugHud.cpp 11 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 "../../Core/CoreEvents.h"
  24. #include "../../Core/Profiler.h"
  25. #include "../../Engine/Engine.h"
  26. #include "../../Graphics/Graphics.h"
  27. #include "../../Graphics/Renderer.h"
  28. #include "../../IO/Log.h"
  29. #include "../../UI/UI.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. posMode_ = WithinExtents({0, -30});
  92. posStats_ = WithinExtents({0, 0});
  93. posProfiler_ = WithinExtents({-530, 0});
  94. }
  95. void DebugHud::SetProfilerMode(DebugHudProfileMode mode)
  96. {
  97. profilerMode_ = mode;
  98. }
  99. void DebugHud::SetMode(unsigned mode)
  100. {
  101. mode_ = mode;
  102. }
  103. void DebugHud::CycleMode()
  104. {
  105. switch (mode_)
  106. {
  107. case DEBUGHUD_SHOW_NONE:
  108. SetMode(DEBUGHUD_SHOW_STATS);
  109. break;
  110. case DEBUGHUD_SHOW_STATS:
  111. SetMode(DEBUGHUD_SHOW_MODE);
  112. break;
  113. case DEBUGHUD_SHOW_MODE:
  114. SetMode(DEBUGHUD_SHOW_PROFILER);
  115. break;
  116. case DEBUGHUD_SHOW_PROFILER:
  117. SetMode(DEBUGHUD_SHOW_ALL);
  118. break;
  119. case DEBUGHUD_SHOW_ALL:
  120. default:
  121. SetMode(DEBUGHUD_SHOW_NONE);
  122. break;
  123. }
  124. }
  125. void DebugHud::SetProfilerMaxDepth(unsigned depth)
  126. {
  127. profilerMaxDepth_ = depth;
  128. }
  129. void DebugHud::SetProfilerInterval(float interval)
  130. {
  131. profilerInterval_ = (unsigned)Max((int)(interval * 1000.0f), 0);
  132. }
  133. void DebugHud::SetUseRendererStats(bool enable)
  134. {
  135. useRendererStats_ = enable;
  136. }
  137. void DebugHud::Toggle(unsigned mode)
  138. {
  139. SetMode(GetMode() ^ mode);
  140. }
  141. void DebugHud::ToggleAll()
  142. {
  143. Toggle(DEBUGHUD_SHOW_ALL);
  144. }
  145. float DebugHud::GetProfilerInterval() const
  146. {
  147. return (float)profilerInterval_ / 1000.0f;
  148. }
  149. void DebugHud::SetAppStats(const String& label, const Variant& stats)
  150. {
  151. SetAppStats(label, stats.ToString());
  152. }
  153. void DebugHud::SetAppStats(const String& label, const String& stats)
  154. {
  155. bool newLabel = !appStats_.Contains(label);
  156. appStats_[label] = stats;
  157. if (newLabel)
  158. appStats_.Sort();
  159. }
  160. bool DebugHud::ResetAppStats(const String& label)
  161. {
  162. return appStats_.Erase(label);
  163. }
  164. void DebugHud::ClearAppStats()
  165. {
  166. appStats_.Clear();
  167. }
  168. IntVector2 DebugHud::WithinExtents(IntVector2 pos)
  169. {
  170. if (pos.x_ < 0)
  171. pos.x_ += extents_.right_;
  172. else if (pos.x_ > 0)
  173. pos.x_ += extents_.left_;
  174. else
  175. pos.x_ = extents_.left_;
  176. if (pos.y_ < 0)
  177. pos.y_ += extents_.bottom_;
  178. else if (pos.y_ > 0)
  179. pos.y_ += extents_.top_;
  180. else
  181. pos.y_ = extents_.top_;
  182. return pos;
  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. ImGui::SetNextWindowSize(ImVec2(sizeProfiler_.x_, sizeProfiler_.y_));
  259. if (ImGui::Begin("DebugHud Metrics", 0, ImVec2(0, 0), 0, backgroundTextWindowFlags))
  260. {
  261. if (profilerTimer_.GetMSec(false) >= profilerInterval_)
  262. {
  263. profilerTimer_.Reset();
  264. if (profilerMode_ == DEBUG_HUD_PROFILE_PERFORMANCE)
  265. {
  266. // Maybe implement some on-screen performance later.
  267. profilerOutput_ = "Performance metrics may be inspected using Profiler tool.";
  268. }
  269. else
  270. {
  271. Metrics* metrics = GetSubsystem<Metrics>();
  272. if (metrics)
  273. {
  274. if (metrics->GetEnabled())
  275. {
  276. metricsSnapshot_.Clear();
  277. metrics->Capture(&metricsSnapshot_);
  278. profilerOutput_ = metricsSnapshot_.PrintData(2);
  279. }
  280. else
  281. profilerOutput_ = "Metrics system not enabled";
  282. }
  283. else
  284. profilerOutput_ = "Metrics subsystem not found";
  285. }
  286. auto size = ImGui::CalcTextSize(profilerOutput_.CString());
  287. sizeProfiler_ = IntVector2((int)size.x + 20, (int)size.y + 20);
  288. posProfiler_ = WithinExtents({-((int)size.x + 20), 0});
  289. }
  290. ImGui::TextUnformatted(profilerOutput_.CString());
  291. }
  292. ImGui::End();
  293. }
  294. }
  295. }