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