]> git.cworth.org Git - apitrace/blobdiff - gui/profileheatmap.h
Rewrote profile graph drawing code.
[apitrace] / gui / profileheatmap.h
diff --git a/gui/profileheatmap.h b/gui/profileheatmap.h
new file mode 100644 (file)
index 0000000..e50ea26
--- /dev/null
@@ -0,0 +1,470 @@
+#ifndef PROFILEHEATMAP_H
+#define PROFILEHEATMAP_H
+
+#include "graphing/heatmapview.h"
+#include "profiling.h"
+
+/**
+ * Data providers for a heatmap based off the trace::Profile call data
+ */
+
+class ProfileHeatmapRowIterator : public HeatmapRowIterator {
+public:
+    ProfileHeatmapRowIterator(const trace::Profile* profile, qint64 start, qint64 end, int steps, bool gpu, int program = -1) :
+        m_profile(profile),
+        m_step(-1),
+        m_stepWidth(1),
+        m_stepCount(steps),
+        m_index(0),
+        m_timeStart(start),
+        m_timeEnd(end),
+        m_useGpu(gpu),
+        m_program(program),
+        m_selected(false),
+        m_timeSelection(false),
+        m_programSelection(false)
+    {
+        m_timeWidth = m_timeEnd - m_timeStart;
+    }
+
+    virtual bool next()
+    {
+        unsigned maxIndex = m_program == -1 ? m_profile->calls.size() : m_profile->programs[m_program].calls.size();
+
+        if (m_index >= maxIndex) {
+            return false;
+        }
+
+        double dtds = m_timeWidth / (double)m_stepCount;
+
+        qint64 heatDuration = 0;
+        qint64 programHeatDuration = 0;
+        m_heat = 0.0f;
+        m_step += m_stepWidth;
+        m_stepWidth = 1;
+
+        m_selected = false;
+
+        /* Iterator through calls until step != lastStep */
+        for (; m_index < maxIndex; ++m_index)
+        {
+            const trace::Profile::Call* call;
+
+            if (m_program == -1) {
+                call = &m_profile->calls[m_index];
+            } else {
+                call = &m_profile->calls[ m_profile->programs[m_program].calls[m_index] ];
+            }
+
+            qint64 start, duration, end;
+
+            if (m_useGpu) {
+                start = call->gpuStart;
+                duration = call->gpuDuration;
+
+                if (call->pixels < 0) {
+                    continue;
+                }
+            } else {
+                start = call->cpuStart;
+                duration = call->cpuDuration;
+            }
+
+            end = start + duration;
+
+            if (end < m_timeStart) {
+                continue;
+            }
+
+            if (start > m_timeEnd) {
+                m_index = m_profile->calls.size();
+                break;
+            }
+
+            double left  = timeToStep(start);
+            double right = timeToStep(end);
+
+            int leftStep = left;
+            int rightStep = right;
+
+            if (leftStep > m_step) {
+                break;
+            }
+
+            if (m_programSelection && call->program == m_programSel) {
+                m_selected = true;
+            }
+
+            if (rightStep - leftStep > 1) {
+                m_label = QString::fromStdString(call->name);
+                m_step = left;
+                m_stepWidth = rightStep - leftStep;
+                heatDuration = dtds;
+                ++m_index;
+                break;
+            }
+
+            if (leftStep < m_step) {
+                qint64 rightTime = stepToTime(rightStep);
+                heatDuration += end - rightTime;
+
+                if (m_programSelection && call->program == m_programSel) {
+                    programHeatDuration += end - rightTime;
+                }
+            } else if (leftStep == rightStep) {
+                heatDuration += duration;
+
+                if (m_programSelection && call->program == m_programSel) {
+                    programHeatDuration += duration;
+                }
+            } else if (rightStep - leftStep == 1) {
+                qint64 rightTime = stepToTime(rightStep);
+                heatDuration += rightTime - start;
+
+                if (m_programSelection && call->program == m_programSel) {
+                    programHeatDuration += rightTime - start;
+                }
+
+                break;
+            }
+        }
+
+        m_heat = heatDuration / dtds;
+        m_programHeat = programHeatDuration / dtds;
+
+        if (m_timeSelection) {
+            qint64 time = stepToTime(m_step);
+
+            if (time >= m_timeSelStart && time <= m_timeSelEnd) {
+                m_programHeat = 1.0;
+            }
+        }
+
+        if (m_programSelection && (m_program == m_programSel || (m_selected && m_stepWidth > 1))) {
+            m_programHeat = 1.0;
+        }
+
+        if (m_programHeat > 0) {
+            m_selected = true;
+        }
+
+        return true;
+    }
+
+    virtual bool isGpu() const
+    {
+        return m_useGpu;
+    }
+
+    virtual float heat() const
+    {
+        return m_heat;
+    }
+
+    virtual float selectedHeat() const
+    {
+        return m_programHeat;
+    }
+
+    virtual int step() const
+    {
+        return m_step;
+    }
+
+    virtual int width() const
+    {
+        return m_stepWidth;
+    }
+
+    virtual QString label() const
+    {
+        return m_label;
+    }
+
+    void setProgramSelection(int program)
+    {
+        m_programSelection = true;
+        m_programSel = program;
+    }
+
+    void setTimeSelection(qint64 start, qint64 end)
+    {
+        m_timeSelection = true;
+        m_timeSelStart = start;
+        m_timeSelEnd = end;
+    }
+
+private:
+    double timeToStep(qint64 time) const
+    {
+        double pos = time;
+        pos -= m_timeStart;
+        pos /= m_timeWidth;
+        pos *= m_stepCount;
+        return pos;
+    }
+
+    qint64 stepToTime(int pos) const
+    {
+        double time = pos;
+        time /= m_stepCount;
+        time *= m_timeWidth;
+        time += m_timeStart;
+        return (qint64)time;
+    }
+
+private:
+    const trace::Profile* m_profile;
+
+    int m_step;
+    int m_stepWidth;
+    int m_stepCount;
+
+    unsigned m_index;
+
+    float m_heat;
+
+    qint64 m_timeStart;
+    qint64 m_timeEnd;
+    qint64 m_timeWidth;
+
+    bool m_useGpu;
+    int m_program;
+
+    QString m_label;
+
+    bool m_selected;
+
+    bool m_timeSelection;
+    qint64 m_timeSelStart;
+    qint64 m_timeSelEnd;
+
+    bool m_programSelection;
+    int m_programSel;
+
+    float m_programHeat;
+};
+
+class ProfileHeatmapDataProvider : public HeatmapDataProvider {
+protected:
+    enum SelectionType {
+        None,
+        Time,
+        Program
+    };
+
+public:
+    ProfileHeatmapDataProvider(trace::Profile* profile) :
+        m_profile(profile),
+        m_selectionState(NULL)
+    {
+        sortRows();
+    }
+
+    virtual qint64 start() const
+    {
+        return m_profile->frames.front().cpuStart;
+    }
+
+    virtual qint64 end() const
+    {
+        return m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
+    }
+
+    virtual unsigned dataRows() const
+    {
+        return m_rowPrograms.size();
+    }
+
+    virtual QString dataLabel(unsigned row) const
+    {
+        if (row >= m_rowPrograms.size()) {
+            return QString();
+        } else {
+            return QString("%1").arg(m_rowPrograms[row]);
+        }
+    }
+
+    virtual qint64 dataRowAt(unsigned row) const
+    {
+        if (row >= m_rowPrograms.size()) {
+            return 0;
+        } else {
+            return m_rowPrograms[row];
+        }
+    }
+
+    virtual HeatmapRowIterator* dataRowIterator(int row, qint64 start, qint64 end, int steps) const
+    {
+        ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, true, m_rowPrograms[row]);
+
+        if (m_selectionState) {
+            if (m_selectionState->type == SelectionState::Horizontal) {
+                itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
+            } else if (m_selectionState->type == SelectionState::Vertical) {
+                itr->setProgramSelection(m_selectionState->start);
+            }
+        }
+
+        return itr;
+    }
+
+    virtual unsigned headerRows() const
+    {
+        return 2;
+    }
+
+    virtual qint64 headerRowAt(unsigned row) const
+    {
+        return row;
+    }
+
+    virtual QString headerLabel(unsigned row) const
+    {
+        if (row == 0) {
+            return "CPU";
+        } else if (row == 1) {
+            return "GPU";
+        } else {
+            return QString();
+        }
+    }
+
+    virtual HeatmapRowIterator* headerRowIterator(int row, qint64 start, qint64 end, int steps) const
+    {
+        ProfileHeatmapRowIterator* itr = new ProfileHeatmapRowIterator(m_profile, start, end, steps, row != 0);
+
+        if (m_selectionState) {
+            if (m_selectionState->type == SelectionState::Horizontal) {
+                itr->setTimeSelection(m_selectionState->start, m_selectionState->end);
+            } else if (m_selectionState->type == SelectionState::Vertical) {
+                itr->setProgramSelection(m_selectionState->start);
+            }
+        }
+
+        return itr;
+    }
+
+    virtual qint64 dataItemAt(unsigned row, qint64 time) const
+    {
+        if (row >= m_rowPrograms.size()) {
+            return -1;
+        }
+
+        unsigned program = m_rowPrograms[row];
+
+        std::vector<unsigned>::const_iterator item =
+                Profiling::binarySearchTimespanIndexed
+                    (m_profile->calls, m_profile->programs[program].calls.begin(), m_profile->programs[program].calls.end(), time);
+
+        if (item == m_profile->programs[program].calls.end()) {
+            return -1;
+        }
+
+        return *item;
+    }
+
+    virtual qint64 headerItemAt(unsigned row, qint64 time) const
+    {
+        if (row >= m_rowPrograms.size()) {
+            return -1;
+        }
+
+        if (row == 0) {
+            /* CPU */
+            std::vector<trace::Profile::Call>::const_iterator item =
+                    Profiling::binarySearchTimespan<trace::Profile::Call,
+                                        &trace::Profile::Call::cpuStart,
+                                        &trace::Profile::Call::cpuDuration>
+                    (m_profile->calls.begin(), m_profile->calls.end(), time);
+
+            if (item != m_profile->calls.end()) {
+                return item - m_profile->calls.begin();
+            }
+        } else if (row == 1) {
+            /* GPU */
+            for (unsigned i = 0; i < m_rowPrograms.size(); ++i) {
+                qint64 index = dataItemAt(i, time);
+
+                if (index != -1) {
+                    return index;
+                }
+            }
+        }
+
+        return -1;
+    }
+
+    virtual void itemDoubleClicked(qint64 index) const
+    {
+        if (index < 0 || index >= m_profile->calls.size()) {
+            return;
+        }
+
+        const trace::Profile::Call& call = m_profile->calls[index];
+        Profiling::jumpToCall(call.no);
+    }
+
+    virtual QString itemTooltip(qint64 index) const
+    {
+        if (index >= m_profile->calls.size()) {
+            return QString();
+        }
+
+        const trace::Profile::Call& call = m_profile->calls[index];
+
+        QString text;
+        text  = QString::fromStdString(call.name);
+
+        text += QString("\nCall: %1").arg(call.no);
+        text += QString("\nCPU Start: %1").arg(Profiling::getTimeString(call.cpuStart));
+        text += QString("\nCPU Duration: %1").arg(Profiling::getTimeString(call.cpuDuration));
+
+        if (call.pixels >= 0) {
+            text += QString("\nGPU Start: %1").arg(Profiling::getTimeString(call.gpuStart));
+            text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration));
+            text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
+        }
+
+        return text;
+    }
+
+    virtual void setSelectionState(SelectionState* state)
+    {
+        m_selectionState = state;
+    }
+
+private:
+    void sortRows()
+    {
+        typedef QPair<quint64, unsigned> Pair;
+        std::vector<Pair> gpu;
+
+        /* Map shader to visible row */
+        for (std::vector<trace::Profile::Program>::const_iterator itr = m_profile->programs.begin(); itr != m_profile->programs.end(); ++itr) {
+            const trace::Profile::Program& program = *itr;
+            unsigned no = itr -  m_profile->programs.begin();
+
+            if (program.gpuTotal > 0) {
+                gpu.push_back(Pair(program.gpuTotal, no));
+            }
+        }
+
+        /* Sort the shaders by most used gpu */
+        qSort(gpu);
+
+        /* Create row order */
+        m_rowPrograms.clear();
+
+        for (std::vector<Pair>::const_reverse_iterator itr = gpu.rbegin(); itr != gpu.rend(); ++itr) {
+            m_rowPrograms.push_back(itr->second);
+        }
+    }
+
+protected:
+    trace::Profile* m_profile;
+    std::vector<int> m_rowPrograms;
+    SelectionState* m_selectionState;
+};
+
+#endif