-
Notifications
You must be signed in to change notification settings - Fork 484
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
Showing
30 changed files
with
3,306 additions
and
2,507 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.