Skip to content

Commit

Permalink
Rewrote profile graph drawing code.
Browse files Browse the repository at this point in the history
Graphing is now reasonably generic to allow for future graphs to be fairly
easily added without too much replication of code.
  • Loading branch information
James Benton committed Sep 7, 2012
1 parent 023a4f8 commit 0b65a2b
Show file tree
Hide file tree
Showing 30 changed files with 3,306 additions and 2,507 deletions.
10 changes: 8 additions & 2 deletions gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ set(qapitrace_SRCS
apitracemodel.cpp
argumentseditor.cpp
glsledit.cpp
graphwidget.cpp
imageviewer.cpp
jumpwidget.cpp
mainwindow.cpp
Expand All @@ -26,8 +25,15 @@ set(qapitrace_SRCS
traceloader.cpp
traceprocess.cpp
trimprocess.cpp
timelinewidget.cpp
vertexdatainterpreter.cpp
graphing/frameaxiswidget.cpp
graphing/graphwidget.cpp
graphing/graphaxiswidget.cpp
graphing/graphview.cpp
graphing/heatmapview.cpp
graphing/heatmapverticalaxiswidget.cpp
graphing/histogramview.cpp
graphing/timeaxiswidget.cpp
)

qt4_automoc(${qapitrace_SRCS})
Expand Down
173 changes: 173 additions & 0 deletions gui/calldurationgraph.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#ifndef CALLDURATIONGRAPH_H
#define CALLDURATIONGRAPH_H

#include "graphing/graphwidget.h"
#include "trace_profiler.hpp"
#include "profiling.h"

/**
* Wrapper for call duration graphs.
*
* This implements the transformSelectionIn and transformSelectionOut to
* allow sharing the selection between the graphs and the heatmap as they
* are using different scales. The duration graphs have call.no on the X-axis
* whereas the heatmap has time on the X axis.
*/
class CallDurationGraph : public GraphWidget {
public:
CallDurationGraph(QWidget* parent = 0) :
GraphWidget(parent),
m_profile(NULL)
{
}

void setProfile(const trace::Profile* profile)
{
m_profile = profile;
}

protected:
/* Transform from time-based horizontal selection to call no based. */
virtual SelectionState transformSelectionIn(SelectionState state)
{
if (!m_profile || state.type != SelectionState::Horizontal) {
return state;
}

qint64 timeStart = state.start;
qint64 timeEnd = state.end;

std::vector<trace::Profile::Call>::const_iterator itr;

itr =
Profiling::binarySearchTimespan<
trace::Profile::Call,
&trace::Profile::Call::cpuStart,
&trace::Profile::Call::cpuDuration>
(m_profile->calls.begin(), m_profile->calls.end(), timeStart, true);

state.start = itr - m_profile->calls.begin();

itr =
Profiling::binarySearchTimespan<
trace::Profile::Call,
&trace::Profile::Call::cpuStart,
&trace::Profile::Call::cpuDuration>
(m_profile->calls.begin(), m_profile->calls.end(), timeEnd, true);

state.end = itr - m_profile->calls.begin();

return state;
}

virtual SelectionState transformSelectionOut(SelectionState state)
{
if (!m_profile || state.type != SelectionState::Horizontal) {
return state;
}

qint64 start = qMax<qint64>(0, state.start);
qint64 end = qMin<qint64>(state.end, m_profile->calls.size());

/* Call based -> time based */
state.start = m_profile->calls[start].cpuStart;
state.end = m_profile->calls[end].cpuStart + m_profile->calls[end].cpuDuration;

return state;
}

private:
const trace::Profile* m_profile;
};

/* Data provider for call duration graphs */
class CallDurationDataProvider : public GraphDataProvider {
public:
CallDurationDataProvider(const trace::Profile* profile, bool gpu) :
m_gpu(gpu),
m_profile(profile),
m_selectionState(NULL)
{
}

virtual qint64 size() const
{
return m_profile ? m_profile->calls.size() : 0;
}

virtual bool selected(qint64 index) const
{
if (m_selectionState) {
if (m_selectionState->type == SelectionState::Horizontal) {
if (m_selectionState->start <= index && index < m_selectionState->end) {
return true;
}
} else if (m_selectionState->type == SelectionState::Vertical) {
return m_profile->calls[index].program == m_selectionState->start;
}
}

return false;
}

virtual void setSelectionState(SelectionState* state)
{
m_selectionState = state;
}

virtual qint64 value(qint64 index) const
{
if (m_gpu) {
return m_profile->calls[index].gpuDuration;
} else {
return m_profile->calls[index].cpuDuration;
}
}

virtual void itemDoubleClicked(qint64 index) const
{
if (!m_profile) {
return;
}

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 (!m_profile) {
return QString();
}

if (index < 0 || 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 Duration: %1").arg(Profiling::getTimeString(call.cpuDuration));

if (call.pixels >= 0) {
text += QString("\nGPU Duration: %1").arg(Profiling::getTimeString(call.gpuDuration));
text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call.pixels));
text += QString("\nProgram: %1").arg(call.program);
}

return text;
}

private:
bool m_gpu;
const trace::Profile* m_profile;
SelectionState* m_selectionState;
};

#endif
99 changes: 99 additions & 0 deletions gui/graphing/frameaxiswidget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include "frameaxiswidget.h"

#include <QPainter>

FrameAxisWidget::FrameAxisWidget(QWidget* parent) :
GraphAxisWidget(parent),
m_data(NULL)
{
setSelectable(GraphAxisWidget::Range);
}

void FrameAxisWidget::setDataProvider(FrameDataProvider* data)
{
delete m_data;
m_data = data;
}

void FrameAxisWidget::paintEvent(QPaintEvent *)
{
if (!m_data || m_orientation != GraphAxisWidget::Horizontal) {
/* TODO: Vertical axis support */
return;
}

QPainter painter(this);
painter.setPen(Qt::black);
painter.setBrush(Qt::lightGray);
painter.drawRect(0, 0, width() - 1, height() - 1);

qint64 range = m_valueEnd - m_valueBegin;
double dxdv = width() / (double)range;
double scroll = dxdv * m_valueBegin;
int lastLabel = -9999;

/* Iterate over frames, drawing a label when there is space to do so */
for (unsigned i = 0; i < m_data->size(); ++i) {
static const int padding = 4;
qint64 start = m_data->frameStart(i);
qint64 end = m_data->frameEnd(i);
bool visible = false;

if (start > m_valueEnd) {
break;
}

if (end < m_valueBegin) {
visible = false;
}

double left = dxdv * start;
double right = dxdv * end;
QString text = QString("%1").arg(i);

int width = painter.fontMetrics().width(text) + padding * 2;

if (right > scroll) {
visible = true;
}

if (left - lastLabel > width) {
lastLabel = left + width;

if (visible) {
int textX;

if (left < scroll && right - left > width) {
if (right - scroll > width) {
textX = 0;
} else {
textX = right - scroll - width;
}
} else {
textX = left - scroll;
}

painter.drawText(textX + padding, 0, width - padding, height() - 5, Qt::AlignLeft | Qt::AlignVCenter, text);
painter.drawLine(left - scroll, height() / 2, left - scroll, height() - 1);
}
} else if (visible) {
painter.drawLine(left - scroll, height() * 3/4.0, left - scroll, height() - 1);
}
}

/* Draw selection */
if (hasSelection()) {
double left = (dxdv * m_selectionState->start) - scroll;
double right = (dxdv * m_selectionState->end) - scroll;

painter.setPen(Qt::green);

if (left >= 0 && left <= width()) {
painter.drawLine(left, 0, left, height());
}

if (right >= 0 && right <= width()) {
painter.drawLine(right, 0, right, height());
}
}
}
32 changes: 32 additions & 0 deletions gui/graphing/frameaxiswidget.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef FRAMEAXISWIDGET_H
#define FRAMEAXISWIDGET_H

#include "graphaxiswidget.h"

class FrameDataProvider {
public:
/* Number of frames */
virtual unsigned size() const = 0;

/* Start and end values of frame */
virtual qint64 frameStart(unsigned frame) const = 0;
virtual qint64 frameEnd(unsigned frame) const = 0;
};

/**
* A generic axis which will draw frame numbers over a period of values.
* Frames designated by start / end values.
*/
class FrameAxisWidget : public GraphAxisWidget {
public:
FrameAxisWidget(QWidget* parent = 0);

void setDataProvider(FrameDataProvider* data);

virtual void paintEvent(QPaintEvent *e);

protected:
FrameDataProvider* m_data;
};

#endif
Loading

0 comments on commit 0b65a2b

Please sign in to comment.