]> git.cworth.org Git - apitrace/blob - gui/profileheatmap.h
Use skiplist-based FastCallSet within trace::CallSet
[apitrace] / gui / profileheatmap.h
1 #ifndef PROFILEHEATMAP_H
2 #define PROFILEHEATMAP_H
3
4 #include "graphing/heatmapview.h"
5 #include "profiling.h"
6
7 /**
8  * Data providers for a heatmap based off the trace::Profile call data
9  */
10
11 class ProfileHeatmapRowIterator : public HeatmapRowIterator {
12 public:
13     ProfileHeatmapRowIterator(const trace::Profile* profile, qint64 start, qint64 end, int steps, bool gpu, int program = -1) :
14         m_profile(profile),
15         m_step(-1),
16         m_stepWidth(1),
17         m_stepCount(steps),
18         m_index(0),
19         m_timeStart(start),
20         m_timeEnd(end),
21         m_useGpu(gpu),
22         m_program(program),
23         m_selected(false),
24         m_timeSelection(false),
25         m_programSelection(false)
26     {
27         m_timeWidth = m_timeEnd - m_timeStart;
28     }
29
30     virtual bool next()
31     {
32         unsigned maxIndex = m_program == -1 ? m_profile->calls.size() : m_profile->programs[m_program].calls.size();
33
34         if (m_index >= maxIndex) {
35             return false;
36         }
37
38         double dtds = m_timeWidth / (double)m_stepCount;
39
40         qint64 heatDuration = 0;
41         qint64 programHeatDuration = 0;
42         m_heat = 0.0f;
43         m_step += m_stepWidth;
44         m_stepWidth = 1;
45
46         m_selected = false;
47
48         /* Iterator through calls until step != lastStep */
49         for (; m_index < maxIndex; ++m_index)
50         {
51             const trace::Profile::Call* call;
52
53             if (m_program == -1) {
54                 call = &m_profile->calls[m_index];
55             } else {
56                 call = &m_profile->calls[ m_profile->programs[m_program].calls[m_index] ];
57             }
58
59             qint64 start, duration, end;
60
61             if (m_useGpu) {
62                 start = call->gpuStart;
63                 duration = call->gpuDuration;
64
65                 if (call->pixels < 0) {
66                     continue;
67                 }
68             } else {
69                 start = call->cpuStart;
70                 duration = call->cpuDuration;
71             }
72
73             end = start + duration;
74
75             if (end < m_timeStart) {
76                 continue;
77             }
78
79             if (start > m_timeEnd) {
80                 m_index = m_profile->calls.size();
81                 break;
82             }
83
84             double left  = timeToStep(start);
85             double right = timeToStep(end);
86
87             int leftStep = left;
88             int rightStep = right;
89
90             if (leftStep > m_step) {
91                 break;
92             }
93
94             if (m_programSelection && call->program == m_programSel) {
95                 m_selected = true;
96             }
97
98             if (rightStep - leftStep > 1) {
99                 m_label = QString::fromStdString(call->name);
100                 m_step = left;
101                 m_stepWidth = rightStep - leftStep;
102                 heatDuration = dtds;
103                 ++m_index;
104                 break;
105             }
106
107             if (leftStep < m_step) {
108                 qint64 rightTime = stepToTime(rightStep);
109                 heatDuration += end - rightTime;
110
111                 if (m_programSelection && call->program == m_programSel) {
112                     programHeatDuration += end - rightTime;
113                 }
114             } else if (leftStep == rightStep) {
115                 heatDuration += duration;
116
117                 if (m_programSelection && call->program == m_programSel) {
118                     programHeatDuration += duration;
119                 }
120             } else if (rightStep - leftStep == 1) {
121                 qint64 rightTime = stepToTime(rightStep);
122                 heatDuration += rightTime - start;
123
124                 if (m_programSelection && call->program == m_programSel) {
125                     programHeatDuration += rightTime - start;
126                 }
127
128                 break;
129             }
130         }
131
132         m_heat = heatDuration / dtds;
133         m_programHeat = programHeatDuration / dtds;
134
135         if (m_timeSelection) {
136             qint64 time = stepToTime(m_step);
137
138             if (time >= m_timeSelStart && time <= m_timeSelEnd) {
139                 m_programHeat = 1.0;
140             }
141         }
142
143         if (m_programSelection && (m_program == m_programSel || (m_selected && m_stepWidth > 1))) {
144             m_programHeat = 1.0;
145         }
146
147         if (m_programHeat > 0) {
148             m_selected = true;
149         }
150
151         return true;
152     }
153
154     virtual bool isGpu() const
155     {
156         return m_useGpu;
157     }
158
159     virtual float heat() const
160     {
161         return m_heat;
162     }
163
164     virtual float selectedHeat() const
165     {
166         return m_programHeat;
167     }
168
169     virtual int step() const
170     {
171         return m_step;
172     }
173
174     virtual int width() const
175     {
176         return m_stepWidth;
177     }
178
179     virtual QString label() const
180     {
181         return m_label;
182     }
183
184     void setProgramSelection(int program)
185     {
186         m_programSelection = true;
187         m_programSel = program;
188     }
189
190     void setTimeSelection(qint64 start, qint64 end)
191     {
192         m_timeSelection = true;
193         m_timeSelStart = start;
194         m_timeSelEnd = end;
195     }
196
197 private:
198     double timeToStep(qint64 time) const
199     {
200         double pos = time;
201         pos -= m_timeStart;
202         pos /= m_timeWidth;
203         pos *= m_stepCount;
204         return pos;
205     }
206
207     qint64 stepToTime(int pos) const
208     {
209         double time = pos;
210         time /= m_stepCount;
211         time *= m_timeWidth;
212         time += m_timeStart;
213         return (qint64)time;
214     }
215
216 private:
217     const trace::Profile* m_profile;
218
219     int m_step;
220     int m_stepWidth;
221     int m_stepCount;
222
223     unsigned m_index;
224
225     float m_heat;
226
227     qint64 m_timeStart;
228     qint64 m_timeEnd;
229     qint64 m_timeWidth;
230
231     bool m_useGpu;
232     int m_program;
233
234     QString m_label;
235
236     bool m_selected;
237
238     bool m_timeSelection;
239     qint64 m_timeSelStart;
240     qint64 m_timeSelEnd;
241
242     bool m_programSelection;
243     int m_programSel;
244
245     float m_programHeat;
246 };
247
248 class ProfileHeatmapDataProvider : public HeatmapDataProvider {
249 protected:
250     enum SelectionType {
251         None,
252         Time,
253         Program
254     };
255
256 public:
257     ProfileHeatmapDataProvider(trace::Profile* profile) :
258         m_profile(profile),
259         m_selectionState(NULL)
260     {
261         sortRows();
262     }
263
264     virtual qint64 start() const
265     {
266         return m_profile->frames.front().cpuStart;
267     }
268
269     virtual qint64 end() const
270     {
271         return m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
272     }
273
274     virtual unsigned dataRows() const
275     {
276         return m_rowPrograms.size();
277     }
278
279     virtual QString dataLabel(unsigned row) const
280     {
281         if (row >= m_rowPrograms.size()) {
282             return QString();
283         } else {
284             return QString("%1").arg(m_rowPrograms[row]);
285         }
286     }
287
288     virtual qint64 dataRowAt(unsigned row) const
289     {
290         if (row >= m_rowPrograms.size()) {
291             return 0;
292         } else {
293             return m_rowPrograms[row];
294         }
295     }
296
297     virtual HeatmapRowIterator* dataRowIterator(int row, qint64 start, qint64 end, int steps) const
298     {
299         ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, true, m_rowPrograms[row]);
300
301         if (m_selectionState) {
302             if (m_selectionState->type == SelectionState::Horizontal) {
303                 itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
304             } else if (m_selectionState->type == SelectionState::Vertical) {
305                 itr->setProgramSelection(m_selectionState->start);
306             }
307         }
308
309         return itr;
310     }
311
312     virtual unsigned headerRows() const
313     {
314         return 2;
315     }
316
317     virtual qint64 headerRowAt(unsigned row) const
318     {
319         return row;
320     }
321
322     virtual QString headerLabel(unsigned row) const
323     {
324         if (row == 0) {
325             return "CPU";
326         } else if (row == 1) {
327             return "GPU";
328         } else {
329             return QString();
330         }
331     }
332
333     virtual HeatmapRowIterator* headerRowIterator(int row, qint64 start, qint64 end, int steps) const
334     {
335         ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, row != 0);
336
337         if (m_selectionState) {
338             if (m_selectionState->type == SelectionState::Horizontal) {
339                 itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
340             } else if (m_selectionState->type == SelectionState::Vertical) {
341                 itr->setProgramSelection(m_selectionState->start);
342             }
343         }
344
345         return itr;
346     }
347
348     virtual qint64 dataItemAt(unsigned row, qint64 time) const
349     {
350         if (row >= m_rowPrograms.size()) {
351             return -1;
352         }
353
354         unsigned program = m_rowPrograms[row];
355
356         std::vector<unsigned>::const_iterator item =
357                 Profiling::binarySearchTimespanIndexed
358                     (m_profile->calls, m_profile->programs[program].calls.begin(), m_profile->programs[program].calls.end(), time);
359
360         if (item == m_profile->programs[program].calls.end()) {
361             return -1;
362         }
363
364         return *item;
365     }
366
367     virtual qint64 headerItemAt(unsigned row, qint64 time) const
368     {
369         if (row >= m_rowPrograms.size()) {
370             return -1;
371         }
372
373         if (row == 0) {
374             /* CPU */
375             std::vector<trace::Profile::Call>::const_iterator item =
376                     Profiling::binarySearchTimespan<trace::Profile::Call,
377                                         &trace::Profile::Call::cpuStart,
378                                         &trace::Profile::Call::cpuDuration>
379                     (m_profile->calls.begin(), m_profile->calls.end(), time);
380
381             if (item != m_profile->calls.end()) {
382                 return item - m_profile->calls.begin();
383             }
384         } else if (row == 1) {
385             /* GPU */
386             for (unsigned i = 0; i < m_rowPrograms.size(); ++i) {
387                 qint64 index = dataItemAt(i, time);
388
389                 if (index != -1) {
390                     return index;
391                 }
392             }
393         }
394
395         return -1;
396     }
397
398     virtual void itemDoubleClicked(qint64 index) const
399     {
400         if (index < 0 || index >= m_profile->calls.size()) {
401             return;
402         }
403
404         const trace::Profile::Call& call = m_profile->calls[index];
405         Profiling::jumpToCall(call.no);
406     }
407
408     virtual QString itemTooltip(qint64 index) const
409     {
410         if (index >= m_profile->calls.size()) {
411             return QString();
412         }
413
414         const trace::Profile::Call& call = m_profile->calls[index];
415
416         QString text;
417         text  = QString::fromStdString(call.name);
418
419         text += QString("\nCall: %1").arg(call.no);
420         text += QString("\nCPU Start: %1").arg(Profiling::getTimeString(call.cpuStart));
421         text += QString("\nCPU Duration: %1").arg(Profiling::getTimeString(call.cpuDuration));
422
423         if (call.pixels >= 0) {
424             text += QString("\nGPU Start: %1").arg(Profiling::getTimeString(call.gpuStart));
425             text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration));
426             text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
427         }
428
429         return text;
430     }
431
432     virtual void setSelectionState(SelectionState* state)
433     {
434         m_selectionState = state;
435     }
436
437 private:
438     void sortRows()
439     {
440         typedef QPair<quint64, unsigned> Pair;
441         std::vector<Pair> gpu;
442
443         /* Map shader to visible row */
444         for (std::vector<trace::Profile::Program>::const_iterator itr = m_profile->programs.begin(); itr != m_profile->programs.end(); ++itr) {
445             const trace::Profile::Program& program = *itr;
446             unsigned no = itr -  m_profile->programs.begin();
447
448             if (program.gpuTotal > 0) {
449                 gpu.push_back(Pair(program.gpuTotal, no));
450             }
451         }
452
453         /* Sort the shaders by most used gpu */
454         qSort(gpu);
455
456         /* Create row order */
457         m_rowPrograms.clear();
458
459         for (std::vector<Pair>::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) {
460             m_rowPrograms.push_back(itr->second);
461         }
462     }
463
464 protected:
465     trace::Profile* m_profile;
466     std::vector<int> m_rowPrograms;
467     SelectionState* m_selectionState;
468 };
469
470 #endif