1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/CoreEvents.h"
26 #include "../Core/Profiler.h"
27 #include "../Core/EventProfiler.h"
28 #include "../Core/Context.h"
29 #include "../Engine/DebugHud.h"
30 #include "../Engine/Engine.h"
31 #include "../Graphics/Graphics.h"
32 #include "../Graphics/Renderer.h"
33 #include "../Resource/ResourceCache.h"
34 #include "../IO/Log.h"
35 #include "../UI/Font.h"
36 #include "../UI/Text.h"
37 #include "../UI/UI.h"
38
39 #include "../DebugNew.h"
40
41 namespace Urho3D
42 {
43
44 static const char* qualityTexts[] =
45 {
46 "Low",
47 "Med",
48 "High",
49 "High+"
50 };
51
52 static const char* shadowQualityTexts[] =
53 {
54 "16bit Simple",
55 "24bit Simple",
56 "16bit PCF",
57 "24bit PCF",
58 "VSM",
59 "Blurred VSM"
60 };
61
DebugHud(Context * context)62 DebugHud::DebugHud(Context* context) :
63 Object(context),
64 profilerMaxDepth_(M_MAX_UNSIGNED),
65 profilerInterval_(1000),
66 useRendererStats_(false),
67 mode_(DEBUGHUD_SHOW_NONE)
68 {
69 UI* ui = GetSubsystem<UI>();
70 UIElement* uiRoot = ui->GetRoot();
71
72 statsText_ = new Text(context_);
73 statsText_->SetAlignment(HA_LEFT, VA_TOP);
74 statsText_->SetPriority(100);
75 statsText_->SetVisible(false);
76 uiRoot->AddChild(statsText_);
77
78 modeText_ = new Text(context_);
79 modeText_->SetAlignment(HA_LEFT, VA_BOTTOM);
80 modeText_->SetPriority(100);
81 modeText_->SetVisible(false);
82 uiRoot->AddChild(modeText_);
83
84 profilerText_ = new Text(context_);
85 profilerText_->SetAlignment(HA_RIGHT, VA_TOP);
86 profilerText_->SetPriority(100);
87 profilerText_->SetVisible(false);
88 uiRoot->AddChild(profilerText_);
89
90 memoryText_ = new Text(context_);
91 memoryText_->SetAlignment(HA_LEFT, VA_BOTTOM);
92 memoryText_->SetPriority(100);
93 memoryText_->SetVisible(false);
94 uiRoot->AddChild(memoryText_);
95
96 eventProfilerText_ = new Text(context_);
97 eventProfilerText_->SetAlignment(HA_RIGHT, VA_TOP);
98 eventProfilerText_->SetPriority(100);
99 eventProfilerText_->SetVisible(false);
100 uiRoot->AddChild(eventProfilerText_);
101
102 SubscribeToEvent(E_POSTUPDATE, URHO3D_HANDLER(DebugHud, HandlePostUpdate));
103 }
104
~DebugHud()105 DebugHud::~DebugHud()
106 {
107 statsText_->Remove();
108 modeText_->Remove();
109 profilerText_->Remove();
110 memoryText_->Remove();
111 eventProfilerText_->Remove();
112 }
113
Update()114 void DebugHud::Update()
115 {
116 Graphics* graphics = GetSubsystem<Graphics>();
117 Renderer* renderer = GetSubsystem<Renderer>();
118 if (!renderer || !graphics)
119 return;
120
121 // Ensure UI-elements are not detached
122 if (!statsText_->GetParent())
123 {
124 UI* ui = GetSubsystem<UI>();
125 UIElement* uiRoot = ui->GetRoot();
126 uiRoot->AddChild(statsText_);
127 uiRoot->AddChild(modeText_);
128 uiRoot->AddChild(profilerText_);
129 }
130
131 if (statsText_->IsVisible())
132 {
133 unsigned primitives, batches;
134 if (!useRendererStats_)
135 {
136 primitives = graphics->GetNumPrimitives();
137 batches = graphics->GetNumBatches();
138 }
139 else
140 {
141 primitives = renderer->GetNumPrimitives();
142 batches = renderer->GetNumBatches();
143 }
144
145 String stats;
146 stats.AppendWithFormat("Triangles %u\nBatches %u\nViews %u\nLights %u\nShadowmaps %u\nOccluders %u",
147 primitives,
148 batches,
149 renderer->GetNumViews(),
150 renderer->GetNumLights(true),
151 renderer->GetNumShadowMaps(true),
152 renderer->GetNumOccluders(true));
153
154 if (!appStats_.Empty())
155 {
156 stats.Append("\n");
157 for (HashMap<String, String>::ConstIterator i = appStats_.Begin(); i != appStats_.End(); ++i)
158 stats.AppendWithFormat("\n%s %s", i->first_.CString(), i->second_.CString());
159 }
160
161 statsText_->SetText(stats);
162 }
163
164 if (modeText_->IsVisible())
165 {
166 String mode;
167 mode.AppendWithFormat("Tex:%s Mat:%s Spec:%s Shadows:%s Size:%i Quality:%s Occlusion:%s Instancing:%s API:%s",
168 qualityTexts[renderer->GetTextureQuality()],
169 qualityTexts[Min(renderer->GetMaterialQuality(), 3)],
170 renderer->GetSpecularLighting() ? "On" : "Off",
171 renderer->GetDrawShadows() ? "On" : "Off",
172 renderer->GetShadowMapSize(),
173 shadowQualityTexts[renderer->GetShadowQuality()],
174 renderer->GetMaxOccluderTriangles() > 0 ? "On" : "Off",
175 renderer->GetDynamicInstancing() ? "On" : "Off",
176 graphics->GetApiName().CString());
177
178 modeText_->SetText(mode);
179 }
180
181 Profiler* profiler = GetSubsystem<Profiler>();
182 EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
183 if (profiler)
184 {
185 if (profilerTimer_.GetMSec(false) >= profilerInterval_)
186 {
187 profilerTimer_.Reset();
188
189 if (profilerText_->IsVisible())
190 profilerText_->SetText(profiler->PrintData(false, false, profilerMaxDepth_));
191
192 profiler->BeginInterval();
193
194 if (eventProfiler)
195 {
196 if (eventProfilerText_->IsVisible())
197 eventProfilerText_->SetText(eventProfiler->PrintData(false, false, profilerMaxDepth_));
198
199 eventProfiler->BeginInterval();
200 }
201 }
202 }
203
204 if (memoryText_->IsVisible())
205 memoryText_->SetText(GetSubsystem<ResourceCache>()->PrintMemoryUsage());
206 }
207
SetDefaultStyle(XMLFile * style)208 void DebugHud::SetDefaultStyle(XMLFile* style)
209 {
210 if (!style)
211 return;
212
213 statsText_->SetDefaultStyle(style);
214 statsText_->SetStyle("DebugHudText");
215 modeText_->SetDefaultStyle(style);
216 modeText_->SetStyle("DebugHudText");
217 profilerText_->SetDefaultStyle(style);
218 profilerText_->SetStyle("DebugHudText");
219 memoryText_->SetDefaultStyle(style);
220 memoryText_->SetStyle("DebugHudText");
221 eventProfilerText_->SetDefaultStyle(style);
222 eventProfilerText_->SetStyle("DebugHudText");
223 }
224
SetMode(unsigned mode)225 void DebugHud::SetMode(unsigned mode)
226 {
227 statsText_->SetVisible((mode & DEBUGHUD_SHOW_STATS) != 0);
228 modeText_->SetVisible((mode & DEBUGHUD_SHOW_MODE) != 0);
229 profilerText_->SetVisible((mode & DEBUGHUD_SHOW_PROFILER) != 0);
230 memoryText_->SetVisible((mode & DEBUGHUD_SHOW_MEMORY) != 0);
231 eventProfilerText_->SetVisible((mode & DEBUGHUD_SHOW_EVENTPROFILER) != 0);
232
233 memoryText_->SetPosition(0, modeText_->IsVisible() ? modeText_->GetHeight() * -2 : 0);
234
235 #ifdef URHO3D_PROFILING
236 // Event profiler is created on engine initialization if "EventProfiler" parameter is set
237 EventProfiler* eventProfiler = GetSubsystem<EventProfiler>();
238 if (eventProfiler)
239 EventProfiler::SetActive((mode & DEBUGHUD_SHOW_EVENTPROFILER) != 0);
240 #endif
241
242 mode_ = mode;
243 }
244
SetProfilerMaxDepth(unsigned depth)245 void DebugHud::SetProfilerMaxDepth(unsigned depth)
246 {
247 profilerMaxDepth_ = depth;
248 }
249
SetProfilerInterval(float interval)250 void DebugHud::SetProfilerInterval(float interval)
251 {
252 profilerInterval_ = Max((unsigned)(interval * 1000.0f), 0U);
253 }
254
SetUseRendererStats(bool enable)255 void DebugHud::SetUseRendererStats(bool enable)
256 {
257 useRendererStats_ = enable;
258 }
259
Toggle(unsigned mode)260 void DebugHud::Toggle(unsigned mode)
261 {
262 SetMode(GetMode() ^ mode);
263 }
264
ToggleAll()265 void DebugHud::ToggleAll()
266 {
267 Toggle(DEBUGHUD_SHOW_ALL);
268 }
269
GetDefaultStyle() const270 XMLFile* DebugHud::GetDefaultStyle() const
271 {
272 return statsText_->GetDefaultStyle(false);
273 }
274
GetProfilerInterval() const275 float DebugHud::GetProfilerInterval() const
276 {
277 return (float)profilerInterval_ / 1000.0f;
278 }
279
SetAppStats(const String & label,const Variant & stats)280 void DebugHud::SetAppStats(const String& label, const Variant& stats)
281 {
282 SetAppStats(label, stats.ToString());
283 }
284
SetAppStats(const String & label,const String & stats)285 void DebugHud::SetAppStats(const String& label, const String& stats)
286 {
287 bool newLabel = !appStats_.Contains(label);
288 appStats_[label] = stats;
289 if (newLabel)
290 appStats_.Sort();
291 }
292
ResetAppStats(const String & label)293 bool DebugHud::ResetAppStats(const String& label)
294 {
295 return appStats_.Erase(label);
296 }
297
ClearAppStats()298 void DebugHud::ClearAppStats()
299 {
300 appStats_.Clear();
301 }
302
HandlePostUpdate(StringHash eventType,VariantMap & eventData)303 void DebugHud::HandlePostUpdate(StringHash eventType, VariantMap& eventData)
304 {
305 using namespace PostUpdate;
306
307 Update();
308 }
309
310 }
311