Permalink
Browse files

Rewrote profile graph drawing code.

Graphing is now reasonably generic to allow for future graphs to be fairly
easily added without too much replication of code.
  • Loading branch information...
1 parent 023a4f8 commit 0b65a2bc99ea926111c0ff19723bd5e2ac84827d James Benton committed Sep 7, 2012
View
@@ -10,7 +10,6 @@ set(qapitrace_SRCS
apitracemodel.cpp
argumentseditor.cpp
glsledit.cpp
- graphwidget.cpp
imageviewer.cpp
jumpwidget.cpp
mainwindow.cpp
@@ -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})
@@ -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
@@ -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());
+ }
+ }
+}
@@ -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.

0 comments on commit 0b65a2b

Please sign in to comment.