1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsProfilerOverlay.h"
4#include "Scene/BsSceneObject.h"
5#include "GUI/BsCGUIWidget.h"
6#include "GUI/BsGUILayout.h"
7#include "GUI/BsGUILayoutX.h"
8#include "GUI/BsGUILayoutY.h"
9#include "GUI/BsGUIPanel.h"
10#include "GUI/BsGUIElement.h"
11#include "GUI/BsGUILabel.h"
12#include "GUI/BsGUISpace.h"
13#include "RenderAPI/BsViewport.h"
14#include "Utility/BsTime.h"
15#include "Resources/BsBuiltinResources.h"
16#include "Profiling/BsProfilingManager.h"
17#include "RenderAPI/BsRenderTarget.h"
18#include "Renderer/BsCamera.h"
19#include "Localization/BsHEString.h"
20
21#define BS_SHOW_PRECISE_PROFILING 0
22
23namespace bs
24{
25 constexpr UINT32 MAX_DEPTH = 4;
26
27 class BasicRowFiller
28 {
29 public:
30 UINT32 curIdx;
31 GUILayout& labelLayout;
32 GUILayout& contentLayout;
33 GUIWidget& widget;
34 Vector<ProfilerOverlay::BasicRow>& rows;
35
36 BasicRowFiller(Vector<ProfilerOverlay::BasicRow>& _rows, GUILayout& _labelLayout, GUILayout& _contentLayout, GUIWidget& _widget)
37 :curIdx(0), labelLayout(_labelLayout), contentLayout(_contentLayout), widget(_widget), rows(_rows)
38 { }
39
40 ~BasicRowFiller()
41 {
42 UINT32 excessEntries = (UINT32)rows.size() - curIdx;
43 for(UINT32 i = 0; i < excessEntries; i++)
44 {
45 ProfilerOverlay::BasicRow& row = rows[curIdx + i];
46
47 if (!row.disabled)
48 {
49 row.labelLayout->setVisible(false);
50 row.contentLayout->setVisible(false);
51 row.disabled = true;
52 }
53 }
54
55 rows.resize(curIdx);
56 }
57
58 void addData(UINT32 depth, const String& name, float pctOfParent, UINT32 numCalls, UINT64 numAllocs,
59 UINT64 numFrees, double avgTime, double totalTime, double avgSelfTime, double totalSelfTime)
60 {
61 if(curIdx >= rows.size())
62 {
63 rows.push_back(ProfilerOverlay::BasicRow());
64
65 ProfilerOverlay::BasicRow& newRow = rows.back();
66
67 newRow.disabled = false;
68 newRow.name = HEString(u8"{0}");
69 newRow.pctOfParent = HEString(u8"{0} %");
70 newRow.numCalls = HEString(u8"{0}");
71 newRow.numAllocs = HEString(u8"{0}");
72 newRow.numFrees = HEString(u8"{0}");
73 newRow.avgTime = HEString(u8"{0}");
74 newRow.totalTime = HEString(u8"{0}");
75 newRow.avgTimeSelf = HEString(u8"{0}");
76 newRow.totalTimeSelf = HEString(u8"{0}");
77
78 newRow.labelLayout = labelLayout.insertNewElement<GUILayoutX>(labelLayout.getNumChildren() - 1); // Insert before flexible space
79 newRow.contentLayout = contentLayout.insertNewElement<GUILayoutX>(contentLayout.getNumChildren() - 1); // Insert before flexible space
80
81 newRow.labelSpace = newRow.labelLayout->addNewElement<GUIFixedSpace>(0);
82 newRow.guiName = newRow.labelLayout->addNewElement<GUILabel>(newRow.name, GUIOptions(GUIOption::fixedWidth(200)));
83
84 newRow.guiPctOfParent = newRow.contentLayout->addNewElement<GUILabel>(newRow.pctOfParent, GUIOptions(GUIOption::fixedWidth(50)));
85 newRow.guiNumCalls = newRow.contentLayout->addNewElement<GUILabel>(newRow.numCalls, GUIOptions(GUIOption::fixedWidth(50)));
86 newRow.guiNumAllocs = newRow.contentLayout->addNewElement<GUILabel>(newRow.numAllocs, GUIOptions(GUIOption::fixedWidth(50)));
87 newRow.guiNumFrees = newRow.contentLayout->addNewElement<GUILabel>(newRow.numFrees, GUIOptions(GUIOption::fixedWidth(50)));
88 newRow.guiAvgTime = newRow.contentLayout->addNewElement<GUILabel>(newRow.avgTime, GUIOptions(GUIOption::fixedWidth(60)));
89 newRow.guiTotalTime = newRow.contentLayout->addNewElement<GUILabel>(newRow.totalTime, GUIOptions(GUIOption::fixedWidth(60)));
90 newRow.guiAvgTimeSelf = newRow.contentLayout->addNewElement<GUILabel>(newRow.avgTimeSelf, GUIOptions(GUIOption::fixedWidth(100)));
91 newRow.guiTotalTimeSelf = newRow.contentLayout->addNewElement<GUILabel>(newRow.totalTimeSelf, GUIOptions(GUIOption::fixedWidth(100)));
92 }
93
94 ProfilerOverlay::BasicRow& row = rows[curIdx];
95
96 row.labelSpace->setSize(depth * 20);
97 row.name.setParameter(0, name);
98 row.pctOfParent.setParameter(0, toString(pctOfParent * 100.0f, 2, 0, ' ', std::ios::fixed));
99 row.numCalls.setParameter(0, toString(numCalls));
100 row.numAllocs.setParameter(0, toString(numAllocs));
101 row.numFrees.setParameter(0, toString(numFrees));
102 row.avgTime.setParameter(0, toString(avgTime, 2, 0, ' ', std::ios::fixed));
103 row.totalTime.setParameter(0, toString(totalTime, 2, 0, ' ', std::ios::fixed));
104 row.avgTimeSelf.setParameter(0, toString(avgSelfTime, 2, 0, ' ', std::ios::fixed));
105 row.totalTimeSelf.setParameter(0, toString(totalSelfTime, 2, 0, ' ', std::ios::fixed));
106
107 row.guiName->setContent(row.name);
108 row.guiPctOfParent->setContent(row.pctOfParent);
109 row.guiNumCalls->setContent(row.numCalls);
110 row.guiNumAllocs->setContent(row.numAllocs);
111 row.guiNumFrees->setContent(row.numFrees);
112 row.guiAvgTime->setContent(row.avgTime);
113 row.guiTotalTime->setContent(row.totalTime);
114 row.guiAvgTimeSelf->setContent(row.avgTimeSelf);
115 row.guiTotalTimeSelf->setContent(row.totalTimeSelf);
116
117 if (row.disabled)
118 {
119 row.labelLayout->setVisible(true);
120 row.contentLayout->setVisible(true);
121 row.disabled = false;
122 }
123
124 curIdx++;
125 }
126 };
127
128 class PreciseRowFiller
129 {
130 public:
131 UINT32 curIdx;
132 GUILayout& labelLayout;
133 GUILayout& contentLayout;
134 GUIWidget& widget;
135 Vector<ProfilerOverlay::PreciseRow>& rows;
136
137 PreciseRowFiller(Vector<ProfilerOverlay::PreciseRow>& _rows, GUILayout& _labelLayout, GUILayout& _contentLayout, GUIWidget& _widget)
138 :curIdx(0), labelLayout(_labelLayout), contentLayout(_contentLayout), widget(_widget), rows(_rows)
139 { }
140
141 ~PreciseRowFiller()
142 {
143 UINT32 excessEntries = (UINT32)rows.size() - curIdx;
144 for(UINT32 i = 0; i < excessEntries; i++)
145 {
146 ProfilerOverlay::PreciseRow& row = rows[curIdx + i];
147
148 if (!row.disabled)
149 {
150 row.labelLayout->setVisible(false);
151 row.contentLayout->setVisible(false);
152 row.disabled = true;
153 }
154 }
155
156 rows.resize(curIdx);
157 }
158
159 void addData(UINT32 depth, const String& name, float pctOfParent, UINT32 numCalls, UINT64 numAllocs,
160 UINT64 numFrees, UINT64 avgCycles, UINT64 totalCycles, UINT64 avgSelfCycles, UINT64 totalSelfCycles)
161 {
162 if(curIdx >= rows.size())
163 {
164 rows.push_back(ProfilerOverlay::PreciseRow());
165
166 ProfilerOverlay::PreciseRow& newRow = rows.back();
167
168 newRow.disabled = false;
169 newRow.name = HEString(u8"{0}");
170 newRow.pctOfParent = HEString(u8"{0}");
171 newRow.numCalls = HEString(u8"{0}");
172 newRow.numAllocs = HEString(u8"{0}");
173 newRow.numFrees = HEString(u8"{0}");
174 newRow.avgCycles = HEString(u8"{0}");
175 newRow.totalCycles = HEString(u8"{0}");
176 newRow.avgCyclesSelf = HEString(u8"{0}");
177 newRow.totalCyclesSelf = HEString(u8"{0}");
178
179 newRow.labelLayout = labelLayout.insertNewElement<GUILayoutX>(labelLayout.getNumChildren() - 1); // Insert before flexible space
180 newRow.contentLayout = contentLayout.insertNewElement<GUILayoutX>(contentLayout.getNumChildren() - 1); // Insert before flexible space
181
182 newRow.labelSpace = newRow.labelLayout->addNewElement<GUIFixedSpace>(0);
183 newRow.guiName = newRow.labelLayout->addNewElement<GUILabel>(newRow.name, GUIOptions(GUIOption::fixedWidth(200)));
184
185 newRow.guiPctOfParent = newRow.contentLayout->addNewElement<GUILabel>(newRow.pctOfParent, GUIOptions(GUIOption::fixedWidth(50)));
186 newRow.guiNumCalls = newRow.contentLayout->addNewElement<GUILabel>(newRow.numCalls, GUIOptions(GUIOption::fixedWidth(50)));
187 newRow.guiNumAllocs = newRow.contentLayout->addNewElement<GUILabel>(newRow.numAllocs, GUIOptions(GUIOption::fixedWidth(50)));
188 newRow.guiNumFrees = newRow.contentLayout->addNewElement<GUILabel>(newRow.numFrees, GUIOptions(GUIOption::fixedWidth(50)));
189 newRow.guiAvgCycles = newRow.contentLayout->addNewElement<GUILabel>(newRow.avgCycles, GUIOptions(GUIOption::fixedWidth(60)));
190 newRow.guiTotalCycles = newRow.contentLayout->addNewElement<GUILabel>(newRow.totalCycles, GUIOptions(GUIOption::fixedWidth(60)));
191 newRow.guiAvgCyclesSelf = newRow.contentLayout->addNewElement<GUILabel>(newRow.avgCyclesSelf, GUIOptions(GUIOption::fixedWidth(100)));
192 newRow.guiTotalCyclesSelf = newRow.contentLayout->addNewElement<GUILabel>(newRow.totalCyclesSelf, GUIOptions(GUIOption::fixedWidth(100)));
193 }
194
195 ProfilerOverlay::PreciseRow& row = rows[curIdx];
196
197 row.labelSpace->setSize(depth * 20);
198 row.name.setParameter(0, name);
199 row.pctOfParent.setParameter(0, toString(pctOfParent * 100.0f, 2, 0, ' ', std::ios::fixed));
200 row.numCalls.setParameter(0, toString(numCalls));
201 row.numAllocs.setParameter(0, toString(numAllocs));
202 row.numFrees.setParameter(0, toString(numFrees));
203 row.avgCycles.setParameter(0, toString(avgCycles));
204 row.totalCycles.setParameter(0, toString(totalCycles));
205 row.avgCyclesSelf.setParameter(0, toString(avgSelfCycles));
206 row.totalCyclesSelf.setParameter(0, toString(totalSelfCycles));
207
208 row.guiName->setContent(row.name);
209 row.guiPctOfParent->setContent(row.pctOfParent);
210 row.guiNumCalls->setContent(row.numCalls);
211 row.guiNumAllocs->setContent(row.numAllocs);
212 row.guiNumFrees->setContent(row.numFrees);
213 row.guiAvgCycles->setContent(row.avgCycles);
214 row.guiTotalCycles->setContent(row.totalCycles);
215 row.guiAvgCyclesSelf->setContent(row.avgCyclesSelf);
216 row.guiTotalCyclesSelf->setContent(row.totalCyclesSelf);
217
218 if (row.disabled)
219 {
220 row.labelLayout->setVisible(true);
221 row.contentLayout->setVisible(true);
222 row.disabled = false;
223 }
224
225 curIdx++;
226 }
227 };
228
229 class GPUSampleRowFiller
230 {
231 public:
232 UINT32 curIdx;
233 GUILayout& labelLayout;
234 GUILayout& contentLayout;
235 GUIWidget& widget;
236 Vector<ProfilerOverlay::GPUSampleRow>& rows;
237
238 GPUSampleRowFiller(Vector<ProfilerOverlay::GPUSampleRow>& rows, GUILayout& labelLayout, GUILayout& contentLayout,
239 GUIWidget& _widget)
240 :curIdx(0), labelLayout(labelLayout), contentLayout(contentLayout), widget(_widget), rows(rows)
241 { }
242
243 ~GPUSampleRowFiller()
244 {
245 UINT32 excessEntries = (UINT32)rows.size() - curIdx;
246 for (UINT32 i = 0; i < excessEntries; i++)
247 {
248 ProfilerOverlay::GPUSampleRow& row = rows[curIdx + i];
249
250 if (!row.disabled)
251 {
252 row.labelLayout->setVisible(false);
253 row.contentLayout->setVisible(false);
254 row.disabled = true;
255 }
256 }
257
258 rows.resize(curIdx);
259 }
260
261 void addData(UINT32 depth, const String& name, float timeMs)
262 {
263 if (curIdx >= rows.size())
264 {
265 rows.push_back(ProfilerOverlay::GPUSampleRow());
266
267 ProfilerOverlay::GPUSampleRow& newRow = rows.back();
268
269 newRow.disabled = false;
270 newRow.name = HEString(u8"{1}");
271 newRow.time = HEString(u8"{0}");
272
273 newRow.labelLayout = labelLayout.insertNewElement<GUILayoutX>(labelLayout.getNumChildren() - 1); // Insert before flexible space
274 newRow.contentLayout = contentLayout.insertNewElement<GUILayoutX>(contentLayout.getNumChildren() - 1); // Insert before flexible space
275
276 newRow.labelSpace = newRow.labelLayout->addNewElement<GUIFixedSpace>(0);
277 newRow.guiName = newRow.labelLayout->addNewElement<GUILabel>(newRow.name, GUIOptions(GUIOption::fixedWidth(200)));
278
279 newRow.guiTime = newRow.contentLayout->addNewElement<GUILabel>(newRow.time, GUIOptions(GUIOption::fixedWidth(100)));
280 }
281
282 ProfilerOverlay::GPUSampleRow& row = rows[curIdx];
283
284 row.labelSpace->setSize(depth * 20);
285 row.name.setParameter(0, name);
286 row.time.setParameter(0, toString(timeMs));
287
288 row.guiName->setContent(row.name);
289 row.guiTime->setContent(row.time);
290
291 if (row.disabled)
292 {
293 row.labelLayout->setVisible(false);
294 row.contentLayout->setVisible(false);
295 row.disabled = false;
296 }
297
298 curIdx++;
299 }
300 };
301
302 ProfilerOverlay::ProfilerOverlay(const SPtr<Camera>& camera)
303 :mType(ProfilerOverlayType::CPUSamples), mIsShown(true)
304 {
305 setTarget(camera);
306 }
307
308 ProfilerOverlay::~ProfilerOverlay()
309 {
310 if(mTarget != nullptr)
311 mTargetResizedConn.disconnect();
312
313 if(mWidgetSO)
314 mWidgetSO->destroy();
315 }
316
317 void ProfilerOverlay::setTarget(const SPtr<Camera>& camera)
318 {
319 if(mTarget != nullptr)
320 mTargetResizedConn.disconnect();
321
322 mTarget = camera->getViewport();
323
324 mTargetResizedConn = mTarget->getTarget()->onResized.connect(std::bind(&ProfilerOverlay::targetResized, this));
325
326 if(mWidgetSO)
327 mWidgetSO->destroy();
328
329 mWidgetSO = SceneObject::create("ProfilerOverlay", SOF_Internal | SOF_Persistent | SOF_DontSave);
330 mWidget = mWidgetSO->addComponent<CGUIWidget>(camera);
331 mWidget->setDepth(127);
332 mWidget->setSkin(BuiltinResources::instance().getGUISkin());
333
334 // Set up CPU sample areas
335 mBasicLayoutLabels = mWidget->getPanel()->addNewElement<GUILayoutY>();
336 mPreciseLayoutLabels = mWidget->getPanel()->addNewElement<GUILayoutY>();
337 mBasicLayoutContents = mWidget->getPanel()->addNewElement<GUILayoutY>();
338 mPreciseLayoutContents = mWidget->getPanel()->addNewElement<GUILayoutY>();
339
340 // Set up CPU sample title bars
341 mTitleBasicName = GUILabel::create(HEString(u8"Name"), GUIOptions(GUIOption::fixedWidth(200)));
342 mTitleBasicPctOfParent = GUILabel::create(HEString(u8"% parent"), GUIOptions(GUIOption::fixedWidth(50)));
343 mTitleBasicNumCalls = GUILabel::create(HEString(u8"# calls"), GUIOptions(GUIOption::fixedWidth(50)));
344 mTitleBasicNumAllocs = GUILabel::create(HEString(u8"# allocs"), GUIOptions(GUIOption::fixedWidth(50)));
345 mTitleBasicNumFrees = GUILabel::create(HEString(u8"# frees"), GUIOptions(GUIOption::fixedWidth(50)));
346 mTitleBasicAvgTime = GUILabel::create(HEString(u8"Avg. time"), GUIOptions(GUIOption::fixedWidth(60)));
347 mTitleBasicTotalTime = GUILabel::create(HEString(u8"Total time"), GUIOptions(GUIOption::fixedWidth(60)));
348 mTitleBasicAvgTitleSelf = GUILabel::create(HEString(u8"Avg. self time"), GUIOptions(GUIOption::fixedWidth(100)));
349 mTitleBasicTotalTimeSelf = GUILabel::create(HEString(u8"Total self time"), GUIOptions(GUIOption::fixedWidth(100)));
350
351 mTitlePreciseName = GUILabel::create(HEString(u8"Name"), GUIOptions(GUIOption::fixedWidth(200)));
352 mTitlePrecisePctOfParent = GUILabel::create(HEString(u8"% parent"), GUIOptions(GUIOption::fixedWidth(50)));
353 mTitlePreciseNumCalls = GUILabel::create(HEString(u8"# calls"), GUIOptions(GUIOption::fixedWidth(50)));
354 mTitlePreciseNumAllocs = GUILabel::create(HEString(u8"# allocs"), GUIOptions(GUIOption::fixedWidth(50)));
355 mTitlePreciseNumFrees = GUILabel::create(HEString(u8"# frees"), GUIOptions(GUIOption::fixedWidth(50)));
356 mTitlePreciseAvgCycles = GUILabel::create(HEString(u8"Avg. cycles"), GUIOptions(GUIOption::fixedWidth(60)));
357 mTitlePreciseTotalCycles = GUILabel::create(HEString(u8"Total cycles"), GUIOptions(GUIOption::fixedWidth(60)));
358 mTitlePreciseAvgCyclesSelf = GUILabel::create(HEString(u8"Avg. self cycles"), GUIOptions(GUIOption::fixedWidth(100)));
359 mTitlePreciseTotalCyclesSelf = GUILabel::create(HEString(u8"Total self cycles"), GUIOptions(GUIOption::fixedWidth(100)));
360
361 GUILayout* basicTitleLabelLayout = mBasicLayoutLabels->addNewElement<GUILayoutX>();
362 GUILayout* preciseTitleLabelLayout = mPreciseLayoutLabels->addNewElement<GUILayoutX>();
363 GUILayout* basicTitleContentLayout = mBasicLayoutContents->addNewElement<GUILayoutX>();
364 GUILayout* preciseTitleContentLayout = mPreciseLayoutContents->addNewElement<GUILayoutX>();
365
366 basicTitleLabelLayout->addElement(mTitleBasicName);
367 basicTitleContentLayout->addElement(mTitleBasicPctOfParent);
368 basicTitleContentLayout->addElement(mTitleBasicNumCalls);
369 basicTitleContentLayout->addElement(mTitleBasicNumAllocs);
370 basicTitleContentLayout->addElement(mTitleBasicNumFrees);
371 basicTitleContentLayout->addElement(mTitleBasicAvgTime);
372 basicTitleContentLayout->addElement(mTitleBasicTotalTime);
373 basicTitleContentLayout->addElement(mTitleBasicAvgTitleSelf);
374 basicTitleContentLayout->addElement(mTitleBasicTotalTimeSelf);
375
376 preciseTitleLabelLayout->addElement(mTitlePreciseName);
377 preciseTitleContentLayout->addElement(mTitlePrecisePctOfParent);
378 preciseTitleContentLayout->addElement(mTitlePreciseNumCalls);
379 preciseTitleContentLayout->addElement(mTitlePreciseNumAllocs);
380 preciseTitleContentLayout->addElement(mTitlePreciseNumFrees);
381 preciseTitleContentLayout->addElement(mTitlePreciseAvgCycles);
382 preciseTitleContentLayout->addElement(mTitlePreciseTotalCycles);
383 preciseTitleContentLayout->addElement(mTitlePreciseAvgCyclesSelf);
384 preciseTitleContentLayout->addElement(mTitlePreciseTotalCyclesSelf);
385
386 mBasicLayoutLabels->addNewElement<GUIFlexibleSpace>();
387 mPreciseLayoutLabels->addNewElement<GUIFlexibleSpace>();
388 mBasicLayoutContents->addNewElement<GUIFlexibleSpace>();
389 mPreciseLayoutContents->addNewElement<GUIFlexibleSpace>();
390
391#if BS_SHOW_PRECISE_PROFILING == 0
392 mPreciseLayoutLabels->setActive(false);
393 mPreciseLayoutContents->setActive(false);
394#endif
395
396 // Set up GPU sample areas
397 mGPULayoutFrameContents = mWidget->getPanel()->addNewElement<GUILayoutX>();
398 mGPULayoutFrameContentsLeft = mGPULayoutFrameContents->addNewElement<GUILayoutY>();
399 mGPULayoutFrameContentsRight = mGPULayoutFrameContents->addNewElement<GUILayoutY>();
400
401 mGPULayoutSamples = mWidget->getPanel()->addNewElement<GUIPanel>();
402
403 HString gpuSamplesStr(u8"__ProfOvGPUSamples", u8"Samples");
404 mGPULayoutSamples->addNewElement<GUILabel>(gpuSamplesStr);
405
406 for(UINT32 i = 0; i < GPU_NUM_SAMPLE_COLUMNS; i++)
407 {
408 mGPULayoutSampleLabels[i] = mGPULayoutSamples->addNewElement<GUILayoutY>();
409 mGPULayoutSampleContents[i] = mGPULayoutSamples->addNewElement<GUILayoutY>();
410
411 HString gpuSamplesNameStr(u8"__ProfOvGPUSampName", u8"Name");
412 HString gpuSamplesTimeStr(u8"__ProfOvGPUSampTime", u8"Time");
413 mGPULayoutSampleLabels[i]->addElement(GUILabel::create(gpuSamplesNameStr, GUIOptions(GUIOption::fixedWidth(200))));
414 mGPULayoutSampleContents[i]->addElement(GUILabel::create(gpuSamplesTimeStr, GUIOptions(GUIOption::fixedWidth(100))));
415
416 mGPULayoutSampleLabels[i]->addNewElement<GUIFlexibleSpace>();
417 mGPULayoutSampleContents[i]->addNewElement<GUIFlexibleSpace>();
418 }
419
420 mGPUFrameNumStr = HEString(u8"__ProfOvFrame", u8"Frame #{0}");
421 mGPUTimeStr = HEString(u8"__ProfOvTime", u8"Time: {0}ms");
422 mGPUDrawCallsStr = HEString(u8"__ProfOvDrawCalls", u8"Draw calls: {0}");
423 mGPURenTargetChangesStr = HEString(u8"__ProfOvRTChanges", u8"Render target changes: {0}");
424 mGPUPresentsStr = HEString(u8"__ProfOvPresents", u8"Presents: {0}");
425 mGPUClearsStr = HEString(u8"__ProfOvClears", u8"Clears: {0}");
426 mGPUVerticesStr = HEString(u8"__ProfOvVertices", u8"Num. vertices: {0}");
427 mGPUPrimitivesStr = HEString(u8"__ProfOvPrimitives", u8"Num. primitives: {0}");
428 mGPUSamplesStr = HEString(u8"__ProfOvSamples", u8"Samples drawn: {0}");
429 mGPUPipelineStateChangesStr = HEString(u8"__ProfOvPSChanges", u8"Pipeline state changes: {0}");
430
431 mGPUObjectsCreatedStr = HEString(u8"__ProfOvObjsCreated", u8"Objects created: {0}");
432 mGPUObjectsDestroyedStr = HEString(u8"__ProfOvObjsDestroyed", u8"Objects destroyed: {0}");
433 mGPUResourceWritesStr = HEString(u8"__ProfOvResWrites", u8"Resource writes: {0}");
434 mGPUResourceReadsStr = HEString(u8"__ProfOvResReads", u8"Resource reads: {0}");
435 mGPUParamBindsStr = HEString(u8"__ProfOvGpuParamBinds", u8"GPU parameter binds: {0}");
436 mGPUVertexBufferBindsStr = HEString(u8"__ProfOvVBBinds", u8"VB binds: {0}");
437 mGPUIndexBufferBindsStr = HEString(u8"__ProfOvIBBinds", u8"IB binds: {0}");
438
439 mGPUFrameNumLbl = GUILabel::create(mGPUFrameNumStr, GUIOptions(GUIOption::fixedWidth(200)));
440 mGPUTimeLbl = GUILabel::create(mGPUTimeStr, GUIOptions(GUIOption::fixedWidth(200)));
441 mGPUDrawCallsLbl = GUILabel::create(mGPUDrawCallsStr, GUIOptions(GUIOption::fixedWidth(200)));
442 mGPURenTargetChangesLbl = GUILabel::create(mGPURenTargetChangesStr, GUIOptions(GUIOption::fixedWidth(200)));
443 mGPUPresentsLbl = GUILabel::create(mGPUPresentsStr, GUIOptions(GUIOption::fixedWidth(200)));
444 mGPUClearsLbl = GUILabel::create(mGPUClearsStr, GUIOptions(GUIOption::fixedWidth(200)));
445 mGPUVerticesLbl = GUILabel::create(mGPUVerticesStr, GUIOptions(GUIOption::fixedWidth(200)));
446 mGPUPrimitivesLbl = GUILabel::create(mGPUPrimitivesStr, GUIOptions(GUIOption::fixedWidth(200)));
447 mGPUSamplesLbl = GUILabel::create(mGPUSamplesStr, GUIOptions(GUIOption::fixedWidth(200)));
448 mGPUPipelineStateChangesLbl = GUILabel::create(mGPUPipelineStateChangesStr, GUIOptions(GUIOption::fixedWidth(200)));
449
450 mGPUObjectsCreatedLbl = GUILabel::create(mGPUObjectsCreatedStr, GUIOptions(GUIOption::fixedWidth(200)));
451 mGPUObjectsDestroyedLbl = GUILabel::create(mGPUObjectsDestroyedStr, GUIOptions(GUIOption::fixedWidth(200)));
452 mGPUResourceWritesLbl = GUILabel::create(mGPUResourceWritesStr, GUIOptions(GUIOption::fixedWidth(200)));
453 mGPUResourceReadsLbl = GUILabel::create(mGPUResourceReadsStr, GUIOptions(GUIOption::fixedWidth(200)));
454 mGPUParamBindsLbl = GUILabel::create(mGPUParamBindsStr, GUIOptions(GUIOption::fixedWidth(200)));
455 mGPUVertexBufferBindsLbl = GUILabel::create(mGPUVertexBufferBindsStr, GUIOptions(GUIOption::fixedWidth(200)));
456 mGPUIndexBufferBindsLbl = GUILabel::create(mGPUIndexBufferBindsStr, GUIOptions(GUIOption::fixedWidth(200)));
457
458 mGPULayoutFrameContentsLeft->addElement(mGPUFrameNumLbl);
459 mGPULayoutFrameContentsLeft->addElement(mGPUTimeLbl);
460 mGPULayoutFrameContentsLeft->addElement(mGPUDrawCallsLbl);
461 mGPULayoutFrameContentsLeft->addElement(mGPURenTargetChangesLbl);
462 mGPULayoutFrameContentsLeft->addElement(mGPUPresentsLbl);
463 mGPULayoutFrameContentsLeft->addElement(mGPUClearsLbl);
464 mGPULayoutFrameContentsLeft->addElement(mGPUVerticesLbl);
465 mGPULayoutFrameContentsLeft->addElement(mGPUPrimitivesLbl);
466 mGPULayoutFrameContentsLeft->addElement(mGPUSamplesLbl);
467 mGPULayoutFrameContentsLeft->addElement(mGPUPipelineStateChangesLbl);
468 mGPULayoutFrameContentsLeft->addNewElement<GUIFlexibleSpace>();
469
470 mGPULayoutFrameContentsRight->addElement(mGPUObjectsCreatedLbl);
471 mGPULayoutFrameContentsRight->addElement(mGPUObjectsDestroyedLbl);
472 mGPULayoutFrameContentsRight->addElement(mGPUResourceWritesLbl);
473 mGPULayoutFrameContentsRight->addElement(mGPUResourceReadsLbl);
474 mGPULayoutFrameContentsRight->addElement(mGPUParamBindsLbl);
475 mGPULayoutFrameContentsRight->addElement(mGPUVertexBufferBindsLbl);
476 mGPULayoutFrameContentsRight->addElement(mGPUIndexBufferBindsLbl);
477 mGPULayoutFrameContentsRight->addNewElement<GUIFlexibleSpace>();
478
479 updateCPUSampleAreaSizes();
480 updateGPUSampleAreaSizes();
481
482 if (!mIsShown)
483 hide();
484 else
485 {
486 if (mType == ProfilerOverlayType::CPUSamples)
487 show(ProfilerOverlayType::CPUSamples);
488 else
489 show(ProfilerOverlayType::GPUSamples);
490 }
491 }
492
493 void ProfilerOverlay::show(ProfilerOverlayType type)
494 {
495 if (type == ProfilerOverlayType::CPUSamples)
496 {
497 mBasicLayoutLabels->setVisible(true);
498 mPreciseLayoutLabels->setVisible(true);
499 mBasicLayoutContents->setVisible(true);
500 mPreciseLayoutContents->setVisible(true);
501 mGPULayoutFrameContents->setVisible(false);
502 mGPULayoutSamples->setVisible(false);
503 }
504 else
505 {
506 mGPULayoutFrameContents->setVisible(true);
507 mGPULayoutSamples->setVisible(true);
508 mBasicLayoutLabels->setVisible(false);
509 mPreciseLayoutLabels->setVisible(false);
510 mBasicLayoutContents->setVisible(false);
511 mPreciseLayoutContents->setVisible(false);
512 }
513
514 mType = type;
515 mIsShown = true;
516 }
517
518 void ProfilerOverlay::hide()
519 {
520 mBasicLayoutLabels->setVisible(false);
521 mPreciseLayoutLabels->setVisible(false);
522 mBasicLayoutContents->setVisible(false);
523 mPreciseLayoutContents->setVisible(false);
524 mGPULayoutFrameContents->setVisible(false);
525 mGPULayoutSamples->setVisible(false);
526 mIsShown = false;
527 }
528
529 void ProfilerOverlay::update()
530 {
531 const ProfilerReport& latestSimReport = ProfilingManager::instance().getReport(ProfiledThread::Sim);
532 const ProfilerReport& latestCoreReport = ProfilingManager::instance().getReport(ProfiledThread::Core);
533
534 updateCPUSampleContents(latestSimReport, latestCoreReport);
535
536 while (ProfilerGPU::instance().getNumAvailableReports() > 1)
537 ProfilerGPU::instance().getNextReport(); // Drop any extra reports, we only want the latest
538
539 if (ProfilerGPU::instance().getNumAvailableReports() > 0)
540 {
541 updateGPUSampleContents(ProfilerGPU::instance().getNextReport());
542 }
543 }
544
545 void ProfilerOverlay::targetResized()
546 {
547 updateCPUSampleAreaSizes();
548 updateGPUSampleAreaSizes();
549 }
550
551 void ProfilerOverlay::updateCPUSampleAreaSizes()
552 {
553 static const INT32 PADDING = 10;
554 static const float LABELS_CONTENT_RATIO = 0.3f;
555
556 UINT32 width = (UINT32)std::max(0, (INT32)mTarget->getPixelArea().width - PADDING * 2);
557 UINT32 height = (UINT32)std::max(0, (INT32)(mTarget->getPixelArea().height - PADDING * 3));
558
559 UINT32 labelsWidth = Math::ceilToInt(width * LABELS_CONTENT_RATIO);
560 UINT32 contentWidth = width - labelsWidth;
561
562 mBasicLayoutLabels->setPosition(PADDING, PADDING);
563 mBasicLayoutLabels->setWidth(labelsWidth);
564 mBasicLayoutLabels->setHeight(height);
565
566 mPreciseLayoutLabels->setPosition(PADDING, height + PADDING * 2);
567 mPreciseLayoutLabels->setWidth(labelsWidth);
568 mPreciseLayoutLabels->setHeight(height);
569
570 mBasicLayoutContents->setPosition(PADDING + labelsWidth, PADDING);
571 mBasicLayoutContents->setWidth(contentWidth);
572 mBasicLayoutContents->setHeight(height);
573
574 mPreciseLayoutContents->setPosition(PADDING + labelsWidth, height + PADDING * 2);
575 mPreciseLayoutContents->setWidth(contentWidth);
576 mPreciseLayoutContents->setHeight(height);
577 }
578
579 void ProfilerOverlay::updateGPUSampleAreaSizes()
580 {
581 static const INT32 PADDING = 10;
582 static const float SAMPLES_FRAME_RATIO = 0.25f;
583 static const INT32 HEADER_HEIGHT = 20;
584 static const INT32 NUM_COLUMNS = 3;
585 static const INT32 HEIGHT_PER_ENTRY = 15;
586
587 UINT32 width = (UINT32)std::max(0, (INT32)mTarget->getPixelArea().width - PADDING * 2);
588 UINT32 height = (UINT32)std::max(0, (INT32)(mTarget->getPixelArea().height - PADDING * 3));
589
590 UINT32 frameHeight = Math::ceilToInt(height * SAMPLES_FRAME_RATIO);
591 UINT32 samplesHeight = height - frameHeight;
592
593 mGPULayoutFrameContents->setPosition(PADDING, PADDING);
594 mGPULayoutFrameContents->setWidth(width);
595 mGPULayoutFrameContents->setHeight(frameHeight);
596
597 mGPULayoutSamples->setPosition(PADDING, PADDING + frameHeight + PADDING);
598 mGPULayoutSamples->setWidth(width);
599 mGPULayoutSamples->setHeight(samplesHeight);
600
601 UINT32 columnWidth = width / NUM_COLUMNS;
602 UINT32 columnHeight = samplesHeight - HEADER_HEIGHT;
603 for(UINT32 i = 0; i < NUM_COLUMNS; i++)
604 {
605 mGPULayoutSampleLabels[i]->setPosition(columnWidth * i, HEADER_HEIGHT);
606 mGPULayoutSampleLabels[i]->setWidth(columnWidth / 2);
607 mGPULayoutSampleLabels[i]->setHeight(columnHeight);
608
609 mGPULayoutSampleContents[i]->setPosition(columnWidth * i + columnWidth / 2, HEADER_HEIGHT);
610 mGPULayoutSampleContents[i]->setWidth(columnWidth / 2);
611 mGPULayoutSampleContents[i]->setHeight(columnHeight);
612 }
613
614 mNumGPUSamplesPerColumn = columnHeight / HEIGHT_PER_ENTRY;
615 }
616
617 void ProfilerOverlay::updateCPUSampleContents(const ProfilerReport& simReport, const ProfilerReport& coreReport)
618 {
619 static const UINT32 NUM_ROOT_ENTRIES = 2;
620
621 const CPUProfilerBasicSamplingEntry& simBasicRootEntry = simReport.cpuReport.getBasicSamplingData();
622 const CPUProfilerPreciseSamplingEntry& simPreciseRootEntry = simReport.cpuReport.getPreciseSamplingData();
623
624 const CPUProfilerBasicSamplingEntry& coreBasicRootEntry = coreReport.cpuReport.getBasicSamplingData();
625 const CPUProfilerPreciseSamplingEntry& corePreciseRootEntry = coreReport.cpuReport.getPreciseSamplingData();
626
627 struct TodoBasic
628 {
629 TodoBasic(const CPUProfilerBasicSamplingEntry& _entry, UINT32 _depth)
630 :entry(_entry), depth(_depth)
631 { }
632
633 const CPUProfilerBasicSamplingEntry& entry;
634 UINT32 depth;
635 };
636
637 struct TodoPrecise
638 {
639 TodoPrecise(const CPUProfilerPreciseSamplingEntry& _entry, UINT32 _depth)
640 :entry(_entry), depth(_depth)
641 { }
642
643 const CPUProfilerPreciseSamplingEntry& entry;
644 UINT32 depth;
645 };
646
647 BasicRowFiller basicRowFiller(mBasicRows, *mBasicLayoutLabels, *mBasicLayoutContents, *mWidget->_getInternal());
648 Stack<TodoBasic> todoBasic;
649
650 const CPUProfilerBasicSamplingEntry* basicRootEntries[NUM_ROOT_ENTRIES];
651 basicRootEntries[0] = &simBasicRootEntry;
652 basicRootEntries[1] = &coreBasicRootEntry;
653
654 for(UINT32 i = 0; i < NUM_ROOT_ENTRIES; i++)
655 {
656 todoBasic.push(TodoBasic(*basicRootEntries[i], 0));
657
658 while(!todoBasic.empty())
659 {
660 TodoBasic curEntry = todoBasic.top();
661 todoBasic.pop();
662
663 const CPUProfilerBasicSamplingEntry::Data& data = curEntry.entry.data;
664 basicRowFiller.addData(curEntry.depth, data.name, data.pctOfParent, data.numCalls, data.memAllocs, data.memFrees,
665 data.avgTimeMs, data.totalTimeMs, data.avgSelfTimeMs, data.totalSelfTimeMs);
666
667 if(curEntry.depth <= MAX_DEPTH)
668 {
669 for(auto iter = curEntry.entry.childEntries.rbegin(); iter != curEntry.entry.childEntries.rend(); ++iter)
670 {
671 todoBasic.push(TodoBasic(*iter, curEntry.depth + 1));
672 }
673 }
674 }
675 }
676
677 PreciseRowFiller preciseRowFiller(mPreciseRows, *mBasicLayoutLabels, *mBasicLayoutContents, *mWidget->_getInternal());
678 Stack<TodoPrecise> todoPrecise;
679
680 const CPUProfilerPreciseSamplingEntry* preciseRootEntries[NUM_ROOT_ENTRIES];
681 preciseRootEntries[0] = &simPreciseRootEntry;
682 preciseRootEntries[1] = &corePreciseRootEntry;
683
684 for(UINT32 i = 0; i < NUM_ROOT_ENTRIES; i++)
685 {
686 todoPrecise.push(TodoPrecise(*preciseRootEntries[i], 0));
687
688 while(!todoBasic.empty())
689 {
690 TodoPrecise curEntry = todoPrecise.top();
691 todoPrecise.pop();
692
693 const CPUProfilerPreciseSamplingEntry::Data& data = curEntry.entry.data;
694 preciseRowFiller.addData(curEntry.depth, data.name, data.pctOfParent, data.numCalls, data.memAllocs, data.memFrees,
695 data.avgCycles, data.totalCycles, data.avgSelfCycles, data.totalSelfCycles);
696
697 if(curEntry.depth <= MAX_DEPTH)
698 {
699 for(auto iter = curEntry.entry.childEntries.rbegin(); iter != curEntry.entry.childEntries.rend(); ++iter)
700 {
701 todoPrecise.push(TodoPrecise(*iter, curEntry.depth + 1));
702 }
703 }
704 }
705 }
706 }
707
708 void ProfilerOverlay::updateGPUSampleContents(const GPUProfilerReport& gpuReport)
709 {
710 mGPUFrameNumStr.setParameter(0, toString((UINT64)gTime().getFrameIdx()));
711 mGPUTimeStr.setParameter(0, toString(gpuReport.frameSample.timeMs));
712 mGPUDrawCallsStr.setParameter(0, toString(gpuReport.frameSample.numDrawCalls));
713 mGPURenTargetChangesStr.setParameter(0, toString(gpuReport.frameSample.numRenderTargetChanges));
714 mGPUPresentsStr.setParameter(0, toString(gpuReport.frameSample.numPresents));
715 mGPUClearsStr.setParameter(0, toString(gpuReport.frameSample.numClears));
716 mGPUVerticesStr.setParameter(0, toString(gpuReport.frameSample.numVertices));
717 mGPUPrimitivesStr.setParameter(0, toString(gpuReport.frameSample.numPrimitives));
718 mGPUSamplesStr.setParameter(0, toString(gpuReport.frameSample.numDrawnSamples));
719 mGPUPipelineStateChangesStr.setParameter(0, toString(gpuReport.frameSample.numPipelineStateChanges));
720
721 mGPUObjectsCreatedStr.setParameter(0, toString(gpuReport.frameSample.numObjectsCreated));
722 mGPUObjectsDestroyedStr.setParameter(0, toString(gpuReport.frameSample.numObjectsDestroyed));
723 mGPUResourceWritesStr.setParameter(0, toString(gpuReport.frameSample.numResourceWrites));
724 mGPUResourceReadsStr.setParameter(0, toString(gpuReport.frameSample.numResourceReads));
725 mGPUParamBindsStr.setParameter(0, toString(gpuReport.frameSample.numGpuParamBinds));
726 mGPUVertexBufferBindsStr.setParameter(0, toString(gpuReport.frameSample.numVertexBufferBinds));
727 mGPUIndexBufferBindsStr.setParameter(0, toString(gpuReport.frameSample.numIndexBufferBinds));
728
729 mGPUFrameNumLbl->setContent(mGPUFrameNumStr);
730 mGPUTimeLbl->setContent(mGPUTimeStr);
731 mGPUDrawCallsLbl->setContent(mGPUDrawCallsStr);
732 mGPURenTargetChangesLbl->setContent(mGPURenTargetChangesStr);
733 mGPUPresentsLbl->setContent(mGPUPresentsStr);
734 mGPUClearsLbl->setContent(mGPUClearsStr);
735 mGPUVerticesLbl->setContent(mGPUVerticesStr);
736 mGPUPrimitivesLbl->setContent(mGPUPrimitivesStr);
737 mGPUSamplesLbl->setContent(mGPUSamplesStr);
738 mGPUPipelineStateChangesLbl->setContent(mGPUPipelineStateChangesStr);
739
740 mGPUObjectsCreatedLbl->setContent(mGPUObjectsCreatedStr);
741 mGPUObjectsDestroyedLbl->setContent(mGPUObjectsDestroyedStr);
742 mGPUResourceWritesLbl->setContent(mGPUResourceWritesStr);
743 mGPUResourceReadsLbl->setContent(mGPUResourceReadsStr);
744 mGPUParamBindsLbl->setContent(mGPUParamBindsStr);
745 mGPUVertexBufferBindsLbl->setContent(mGPUVertexBufferBindsStr);
746 mGPUIndexBufferBindsLbl->setContent(mGPUIndexBufferBindsStr);
747
748 GPUSampleRowFiller sampleRowFillers[GPU_NUM_SAMPLE_COLUMNS] =
749 {
750 GPUSampleRowFiller(mGPUSampleRows[0], *mGPULayoutSampleLabels[0], *mGPULayoutSampleContents[0], *mWidget->_getInternal()),
751 GPUSampleRowFiller(mGPUSampleRows[1], *mGPULayoutSampleLabels[1], *mGPULayoutSampleContents[1], *mWidget->_getInternal()),
752 GPUSampleRowFiller(mGPUSampleRows[2], *mGPULayoutSampleLabels[2], *mGPULayoutSampleContents[2], *mWidget->_getInternal())
753 };
754
755 struct Todo
756 {
757 Todo(const GPUProfileSample& entry, UINT32 depth)
758 :entry(entry), depth(depth)
759 { }
760
761 const GPUProfileSample& entry;
762 UINT32 depth;
763 };
764
765 UINT32 column = 0;
766 UINT32 currentCount = 0;
767
768 Stack<Todo> todo;
769 todo.push(Todo(gpuReport.frameSample, 0));
770
771 while (!todo.empty())
772 {
773 Todo curEntry = todo.top();
774 todo.pop();
775
776 const GPUProfileSample& data = curEntry.entry;
777
778 if(column < GPU_NUM_SAMPLE_COLUMNS)
779 sampleRowFillers[column].addData(curEntry.depth, data.name, data.timeMs);
780
781 currentCount++;
782 if (currentCount % mNumGPUSamplesPerColumn == 0)
783 column++;
784
785 if (curEntry.depth <= MAX_DEPTH)
786 {
787 for (auto iter = curEntry.entry.children.rbegin(); iter != curEntry.entry.children.rend(); ++iter)
788 todo.push(Todo(*iter, curEntry.depth + 1));
789 }
790 }
791 }
792}