blob: e4c4735017ee3b2af1c56bf4dec8ac174a1d9e92 [file] [log] [blame]
#pragma once
#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() override
{
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 override
{
return m_useGpu;
}
virtual float heat() const override
{
return m_heat;
}
virtual float selectedHeat() const override
{
return m_programHeat;
}
virtual int step() const override
{
return m_step;
}
virtual int width() const override
{
return m_stepWidth;
}
virtual QString label() const override
{
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 override
{
return m_profile->frames.front().cpuStart;
}
virtual qint64 end() const override
{
return m_profile->frames.back().cpuStart + m_profile->frames.back().cpuDuration;
}
virtual unsigned dataRows() const override
{
return m_rowPrograms.size();
}
virtual QString dataLabel(unsigned row) const override
{
if (row >= m_rowPrograms.size()) {
return QString();
} else {
return QString("%1").arg(m_rowPrograms[row]);
}
}
virtual qint64 dataRowAt(unsigned row) const override
{
if (row >= m_rowPrograms.size()) {
return 0;
} else {
return m_rowPrograms[row];
}
}
virtual HeatmapRowIterator* dataRowIterator(int row, qint64 start, qint64 end, int steps) const override
{
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 override
{
return 2;
}
virtual qint64 headerRowAt(unsigned row) const override
{
return row;
}
virtual QString headerLabel(unsigned row) const override
{
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 override
{
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 override
{
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 override
{
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 override
{
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 override
{
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, 1e3));
text += QString("\nCPU Duration: %1").arg(Profiling::getTimeString(call.cpuDuration, 1e3));
if (call.pixels >= 0) {
text += QString("\nGPU Start: %1").arg(Profiling::getTimeString(call.gpuStart, 1e3));
text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration, 1e3));
text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
}
return text;
}
virtual void setSelectionState(SelectionState* state) override
{
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;
};