]> git.cworth.org Git - apitrace/commitdiff
Combine timeline and histogram tabs.
authorJames Benton <jbenton@vmware.com>
Tue, 28 Aug 2012 17:41:43 +0000 (18:41 +0100)
committerJames Benton <jbenton@vmware.com>
Tue, 28 Aug 2012 17:41:43 +0000 (18:41 +0100)
Synchronise time / program selections between all profile data views.

common/trace_profiler.cpp
common/trace_profiler.hpp
gui/graphwidget.cpp
gui/graphwidget.h
gui/profiledialog.cpp
gui/profiledialog.h
gui/profiletablemodel.cpp
gui/profiletablemodel.h
gui/timelinewidget.cpp
gui/timelinewidget.h
gui/ui/profiledialog.ui

index 402cdacc025954115f3ca9d964c272a3f1904fac..0f90ee2766318c54f790fc110efd772b8126f309 100644 (file)
@@ -145,7 +145,6 @@ void Profiler::parseLine(const char* in, Profile* profile)
 
     if (type.compare("call") == 0) {
         Profile::Call call;
-        unsigned programNo;
 
         line >> call.no
              >> call.gpuStart
@@ -153,7 +152,7 @@ void Profiler::parseLine(const char* in, Profile* profile)
              >> call.cpuStart
              >> call.cpuDuration
              >> call.pixels
-             >> programNo
+             >> call.program
              >> call.name;
 
         if (lastGpuTime < call.gpuStart + call.gpuDuration) {
@@ -167,11 +166,11 @@ void Profiler::parseLine(const char* in, Profile* profile)
         profile->calls.push_back(call);
 
         if (call.pixels >= 0) {
-            if (profile->programs.size() <= programNo) {
-                profile->programs.resize(programNo + 1);
+            if (profile->programs.size() <= call.program) {
+                profile->programs.resize(call.program + 1);
             }
 
-            Profile::Program& program = profile->programs[programNo];
+            Profile::Program& program = profile->programs[call.program];
             program.cpuTotal += call.cpuDuration;
             program.gpuTotal += call.gpuDuration;
             program.pixelTotal += call.pixels;
index 8463c61f4ff5d06230b7b3217cf3bb723389276b..d8332420c63fc6ce622fd300315d275a85ba0db2 100644 (file)
@@ -37,6 +37,8 @@ struct Profile {
     struct Call {
         unsigned no;
 
+        unsigned program;
+
         int64_t gpuStart;
         int64_t gpuDuration;
 
index 401c03464efdcc1ecbd9071533b902e93d8c39d0..ea0f6a72e72ba418fba0f2af0eae34b217b26e42 100644 (file)
@@ -21,15 +21,20 @@ GraphWidget::GraphWidget(QWidget *parent)
       m_axisForeground(Qt::black),
       m_axisBackground(Qt::lightGray)
 {
-    setBackgroundRole(QPalette::Base);
-    setAutoFillBackground(true);
-    setMouseTracking(true);
+    m_selection.type = SelectNone;
+
+    m_graphGradientGpu.setColorAt(0.9, QColor(210, 0, 0));
+    m_graphGradientGpu.setColorAt(0.0, QColor(255, 130, 130));
 
-    m_graphGradientGpu.setColorAt(0.9, QColor(255, 0, 0));
-    m_graphGradientGpu.setColorAt(0.0, QColor(255, 200, 200));
+    m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 210));
+    m_graphGradientCpu.setColorAt(0.0, QColor(130, 130, 255));
 
-    m_graphGradientCpu.setColorAt(0.9, QColor(0, 0, 255));
-    m_graphGradientCpu.setColorAt(0.0, QColor(200, 200, 255));
+    m_graphGradientDeselected.setColorAt(0.9, QColor(200, 200, 200));
+    m_graphGradientDeselected.setColorAt(0.0, QColor(220, 220, 220));
+
+    setMouseTracking(true);
+    setAutoFillBackground(true);
+    setBackgroundRole(QPalette::Base);
 }
 
 
@@ -40,35 +45,84 @@ void GraphWidget::setProfile(trace::Profile* profile, GraphType type)
 {
     m_type = type;
     m_profile = profile;
-    m_maxTime = 0;
+    m_timeMax = 0;
 
     /* Find longest call to use as y axis */
     if (m_type == GraphGpu) {
         for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
             const Call& call = *itr;
 
-            if (call.gpuDuration > m_maxTime) {
-                m_maxTime = call.gpuDuration;
+            if (call.gpuDuration > m_timeMax) {
+                m_timeMax = call.gpuDuration;
             }
         }
     } else {
         for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
             const Call& call = *itr;
 
-            if (call.cpuDuration > m_maxTime) {
-                m_maxTime = call.cpuDuration;
+            if (call.cpuDuration > m_timeMax) {
+                m_timeMax = call.cpuDuration;
             }
         }
     }
 
-    m_minCall = 0;
-    m_maxCall = m_profile->calls.size();
+    m_callMin = 0;
+    m_callMax = m_profile->calls.size();
 
-    m_minCallWidth = 10;
-    m_maxCallWidth = m_maxCall - m_minCall;
+    m_callWidthMin = 10;
+    m_callWidthMax = m_callMax - m_callMin;
 
-    m_call = m_minCall;
-    m_callWidth = m_maxCallWidth;
+    m_call = m_callMin;
+    m_callWidth = m_callWidthMax;
+
+    selectNone();
+    update();
+}
+
+
+/**
+ * Set selection to nothing
+ */
+void GraphWidget::selectNone(bool notify)
+{
+    m_selection.type = SelectNone;
+
+    if (notify) {
+        emit selectedNone();
+    }
+
+    update();
+}
+
+
+/**
+ * Set selection to a time period
+ */
+void GraphWidget::selectTime(int64_t start, int64_t end, bool notify)
+{
+    m_selection.timeStart = start;
+    m_selection.timeEnd = end;
+    m_selection.type = (start == end) ? SelectNone : SelectTime;
+
+    if (notify) {
+        emit selectedTime(start, end);
+    }
+
+    update();
+}
+
+
+/**
+ * Set selection to a program
+ */
+void GraphWidget::selectProgram(unsigned program, bool notify)
+{
+    m_selection.program = program;
+    m_selection.type = SelectProgram;
+
+    if (notify) {
+        emit selectedProgram(program);
+    }
 
     update();
 }
@@ -90,22 +144,22 @@ void GraphWidget::changeView(int call, int width)
  */
 void GraphWidget::update()
 {
-    m_maxTime = 0;
+    m_timeMax = 0;
 
     if (m_type == GraphGpu) {
         for (int i = m_call; i < m_call + m_callWidth; ++i) {
             const Call& call =  m_profile->calls[i];
 
-            if (call.gpuDuration > m_maxTime) {
-                m_maxTime = call.gpuDuration;
+            if (call.gpuDuration > m_timeMax) {
+                m_timeMax = call.gpuDuration;
             }
         }
     } else {
         for (int i = m_call; i < m_call + m_callWidth; ++i) {
             const Call& call =  m_profile->calls[i];
 
-            if (call.cpuDuration > m_maxTime) {
-                m_maxTime = call.cpuDuration;
+            if (call.cpuDuration > m_timeMax) {
+                m_timeMax = call.cpuDuration;
             }
         }
     }
@@ -129,15 +183,15 @@ const Call* GraphWidget::callAtPosition(const QPoint& pos)
     int posX = qMax(0, pos.x() - m_axisWidth);
     int posY = qMax(0, pos.y() - m_axisHeight);
 
-    time  = ((m_graphHeight - posY) / (double)m_graphHeight) * m_maxTime;
-    time -= (2 * m_maxTime) / m_graphHeight;
+    time  = ((m_graphHeight - posY) / (double)m_graphHeight) * m_timeMax;
+    time -= (2 * m_timeMax) / m_graphHeight;
 
     size  = m_callWidth / (double)m_graphWidth;
 
     left  = m_call + (posX / (double)m_graphWidth) * m_callWidth;
-    left  = qMax(m_minCall, left - size);
+    left  = qMax(m_callMin, left - size);
 
-    right = qMin(m_maxCall - 1, left + size * 2);
+    right = qMin(m_callMax - 1, left + size * 2);
 
     if (m_type == GraphGpu) {
         const Call* longest = NULL;
@@ -184,26 +238,71 @@ void GraphWidget::mousePressEvent(QMouseEvent *e)
 }
 
 
-void GraphWidget::mouseMoveEvent(QMouseEvent *e)
+void GraphWidget::mouseReleaseEvent(QMouseEvent *e)
 {
     if (!m_profile) {
         return;
     }
 
-    if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) {
+    if (e->button() == Qt::LeftButton) {
+        int dxy = qAbs(m_mousePressPosition.x() - e->pos().x()) + qAbs(m_mousePressPosition.y() - e->pos().y());
+
+        if (dxy <= 2) {
+            int x = qMax(m_axisWidth, e->pos().x());
+            double dcdx = m_callWidth / (double)m_graphWidth;
+
+            int call = m_mousePressCall + dcdx * (x - m_axisWidth);
+
+            int64_t start = m_profile->calls[call].cpuStart;
+            int64_t end = m_profile->calls[call].cpuStart + m_profile->calls[call].cpuDuration;
+
+            if (start < m_selection.timeStart || end > m_selection.timeEnd) {
+                selectNone(true);
+            }
+        }
+    }
+}
+
+
+void GraphWidget::mouseDoubleClickEvent(QMouseEvent *e)
+{
+    const Call* call = callAtPosition(e->pos());
+
+    if (call) {
+        emit jumpToCall(call->no);
+    }
+}
+
+
+void GraphWidget::mouseMoveEvent(QMouseEvent *e)
+{
+    if (!m_profile) {
         return;
     }
 
     if (e->buttons().testFlag(Qt::LeftButton)) {
-        /* Horizontal scroll */
         double dcdx = m_callWidth / (double)m_graphWidth;
-        dcdx *= m_mousePressPosition.x() - e->pos().x();
 
-        m_call = m_mousePressCall + dcdx;
-        m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth);
+        if (m_mousePressPosition.y() > m_axisHeight) {
+            dcdx *= m_mousePressPosition.x() - e->pos().x();
+
+            /* Horizontal scroll */
+            m_call = m_mousePressCall + dcdx;
+            m_call = qBound(m_callMin, m_call, m_callMax - m_callWidth);
+
+            emit viewChanged(m_call, m_callWidth);
+            update();
+        } else {
+            int x = qMax(m_axisWidth, e->pos().x());
+
+            int down = m_mousePressCall + dcdx * (m_mousePressPosition.x() - m_axisWidth);
+            int up = m_mousePressCall + dcdx * (x - m_axisWidth);
 
-        emit viewChanged(m_call, m_callWidth);
-        update();
+            int left = qMax(qMin(down, up), 0);
+            int right = qMin<int>(qMax(down, up), m_profile->calls.size() - 1);
+
+            selectTime(m_profile->calls[left].cpuStart, m_profile->calls[right].cpuStart + m_profile->calls[right].cpuDuration, true);
+        }
     }
 
     const Call* call = callAtPosition(e->pos());
@@ -217,6 +316,7 @@ void GraphWidget::mouseMoveEvent(QMouseEvent *e)
         if (call->pixels >= 0) {
             text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
             text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
+            text += QString("\nProgram: %1").arg(call->program);
         }
 
         QToolTip::showText(e->globalPos(), text);
@@ -232,7 +332,7 @@ void GraphWidget::wheelEvent(QWheelEvent *e)
         return;
     }
 
-    if (e->pos().x() < m_axisWidth || e->pos().y() < m_axisHeight) {
+    if (e->pos().x() < m_axisWidth) {
         return;
     }
 
@@ -251,7 +351,7 @@ void GraphWidget::wheelEvent(QWheelEvent *e)
     size /= 120 * (100 / zoomPercent);
 
     m_callWidth += size;
-    m_callWidth = qBound(m_minCallWidth, m_callWidth, m_maxCallWidth);
+    m_callWidth = qBound(m_callWidthMin, m_callWidth, m_callWidthMax);
 
     /* Scroll view to zoom around mouse */
     dt -= m_callWidth;
@@ -259,23 +359,13 @@ void GraphWidget::wheelEvent(QWheelEvent *e)
     dt /= m_graphWidth;
 
     m_call = dt + m_call;
-    m_call = qBound(m_minCall, m_call, m_maxCall - m_callWidth);
+    m_call = qBound(m_callMin, m_call, m_callMax - m_callWidth);
 
     emit viewChanged(m_call, m_callWidth);
     update();
 }
 
 
-void GraphWidget::mouseDoubleClickEvent(QMouseEvent *e)
-{
-    const Call* call = callAtPosition(e->pos());
-
-    if (call) {
-        emit jumpToCall(call->no);
-    }
-}
-
-
 void GraphWidget::resizeEvent(QResizeEvent *e)
 {
     m_graphWidth = qMax(0, width() - m_axisWidth);
@@ -283,6 +373,7 @@ void GraphWidget::resizeEvent(QResizeEvent *e)
 
     m_graphGradientGpu.setStart(0, m_graphHeight);
     m_graphGradientCpu.setStart(0, m_graphHeight);
+    m_graphGradientDeselected.setStart(0, m_graphHeight);
 }
 
 
@@ -294,16 +385,20 @@ void GraphWidget::paintVerticalAxis(QPainter& painter)
     int height = painter.fontMetrics().height();
     int ticks  = m_graphHeight / (height * 2);
 
-    double step   = m_maxTime / (double)ticks;
+    double step   = m_timeMax / (double)ticks;
     double step10 = qPow(10.0, qFloor(qLn(step) / qLn(10.0)));
     step = qFloor((step / step10) * 2) * (step10 / 2);
 
+    if (step <= 0) {
+        return;
+    }
+
     painter.resetTransform();
     painter.translate(0, m_axisHeight);
     painter.setPen(m_axisForeground);
 
-    for (double tick = 0; tick <= m_maxTime; tick += step) {
-        int y = m_graphHeight - ((tick / m_maxTime) * m_graphHeight);
+    for (double tick = 0; tick <= m_timeMax; tick += step) {
+        int y = m_graphHeight - ((tick / m_timeMax) * m_graphHeight);
 
         painter.drawLine(m_axisWidth - 8, y, m_axisWidth - 1, y);
 
@@ -312,7 +407,7 @@ void GraphWidget::paintVerticalAxis(QPainter& painter)
                          m_axisWidth - 10,
                          height,
                          Qt::AlignRight | Qt::AlignVCenter,
-                         getTimeString(tick, m_maxTime));
+                         getTimeString(tick, m_timeMax));
     }
 }
 
@@ -395,7 +490,10 @@ void GraphWidget::paintEvent(QPaintEvent *e)
     }
 
     QPainter painter(this);
+    QBrush deselectBrush;
+    QPen deselectPen;
     QBrush brush;
+    QPen pen;
 
     /* Draw axes */
     paintHorizontalAxis(painter);
@@ -412,81 +510,156 @@ void GraphWidget::paintEvent(QPaintEvent *e)
     }
 
     /* Draw graph */
+    deselectBrush = QBrush(m_graphGradientDeselected);
+
     if (m_type == GraphGpu) {
         brush = QBrush(m_graphGradientGpu);
     } else {
         brush = QBrush(m_graphGradientCpu);
     }
 
+    pen = QPen(brush, 1);
+    deselectPen = QPen(deselectBrush, 1);
+
     painter.setBrush(brush);
-    painter.setPen(QPen(brush, 1));
+    painter.setPen(pen);
     painter.translate(m_axisWidth, m_axisHeight);
 
     double x = 0;
-    double dydt = m_graphHeight / (double)m_maxTime;
+    double dydt = m_graphHeight / (double)m_timeMax;
     double dxdc = m_graphWidth / (double)m_callWidth;
 
+    int selectLeft = m_call + m_callWidth;
+    int selectRight = -1;
+
+    if (m_selection.type == SelectProgram) {
+        painter.setPen(deselectPen);
+    }
+
     if (dxdc < 1.0) {
-        /* Less than 1 pixel per call, draw the longest call in a pixel */
-        int64_t longest = 0;
+        /* Draw the longest call in a pixel */
+        int64_t selectedLongest = 0;
+        int64_t pixelLongest = 0;
         int lastX = 0;
 
-        if (m_type == GraphGpu) {
-            for (int i = m_call; i < m_call + m_callWidth; ++i) {
-                const Call& call =  m_profile->calls[i];
+        for (int i = m_call; i < m_call + m_callWidth; ++i) {
+            const Call& call =  m_profile->calls[i];
+            int ix;
 
-                if (call.gpuDuration > longest) {
-                    longest = call.gpuDuration;
+            if (m_type == GraphGpu) {
+                if (call.gpuDuration > pixelLongest) {
+                    pixelLongest = call.gpuDuration;
                 }
 
-                x += dxdc;
+                if (m_selection.type == SelectProgram && call.program == m_selection.program) {
+                    if (call.gpuDuration > selectedLongest) {
+                        selectedLongest = call.gpuDuration;
+                    }
+                }
+            } else {
+                if (call.cpuDuration > pixelLongest) {
+                    pixelLongest = call.cpuDuration;
+                }
 
-                if (lastX != (int)x) {
-                    painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt));
-                    longest = 0;
-                    lastX = x;
+                if (m_selection.type == SelectProgram && call.program == m_selection.program) {
+                    if (call.cpuDuration > selectedLongest) {
+                        selectedLongest = call.cpuDuration;
+                    }
                 }
             }
-        } else {
-            for (int i = m_call; i < m_call + m_callWidth; ++i) {
-                const Call& call =  m_profile->calls[i];
 
-                if (call.cpuDuration > longest) {
-                    longest = call.cpuDuration;
+            x += dxdc;
+            ix = (int)x;
+
+            if (lastX != ix) {
+                if (m_selection.type == SelectTime) {
+                    if (call.cpuStart < m_selection.timeStart || call.cpuStart > m_selection.timeEnd) {
+                        painter.setPen(deselectPen);
+                    } else {
+                        if (ix < selectLeft) {
+                            selectLeft = ix;
+                        }
+
+                        if (ix > selectRight) {
+                            selectRight = ix;
+                        }
+
+                        painter.setPen(pen);
+                    }
                 }
 
-                x += dxdc;
+                painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (pixelLongest * dydt));
+                pixelLongest = 0;
 
-                if (lastX != (int)x) {
-                    painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (longest * dydt));
-                    longest = 0;
-                    lastX = x;
+                if (selectedLongest > 0) {
+                    painter.setPen(pen);
+                    painter.drawLine(lastX, m_graphHeight, lastX, m_graphHeight - (selectedLongest * dydt));
+                    painter.setPen(deselectPen);
+                    selectedLongest = 0;
                 }
+
+                lastX = ix;
             }
         }
     } else {
-        /* At least 1 pixel per call, draw rects */
-        if (m_type == GraphGpu) {
-            for (int i = m_call; i < m_call + m_callWidth; ++i) {
-                const Call& call =  m_profile->calls[i];
+        /* Draw rectangles for graph */
+        for (int i = m_call; i < m_call + m_callWidth; ++i, x += dxdc) {
+            const Call& call =  m_profile->calls[i];
+            int y;
 
-                if (call.pixels >= 0) {
-                    int y = qMax<int>(1, call.gpuDuration * dydt);
-                    painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
+            if (m_type == GraphGpu) {
+                y = qMax<int>(1, call.gpuDuration * dydt);
+            } else {
+                y = qMax<int>(1, call.cpuDuration * dydt);
+            }
+
+            if (m_selection.type == SelectTime) {
+                if (call.cpuStart < m_selection.timeStart || call.cpuStart > m_selection.timeEnd) {
+                    if (m_type == GraphCpu || call.pixels >= 0) {
+                        painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), deselectBrush);
+                    }
+
+                    continue;
+                } else {
+                    if (x < selectLeft) {
+                        selectLeft = x;
+                    }
+
+                    if (x + dxdc > selectRight) {
+                        selectRight = x + dxdc;
+                    }
                 }
+            }
 
-                x += dxdc;
+            if (m_type == GraphCpu || call.pixels >= 0) {
+                if (m_selection.type == SelectProgram && call.program != m_selection.program) {
+                    painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), deselectBrush);
+                } else {
+                    painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
+                }
             }
-        } else {
-            for (int i = m_call; i < m_call + m_callWidth; ++i) {
-                const Call& call =  m_profile->calls[i];
+        }
+    }
 
-                int y = qMax<int>(1, call.cpuDuration * dydt);
-                painter.fillRect(QRectF(x, m_graphHeight - y, dxdc, y), brush);
+    /* Draw the selection borders */
+    if (m_selection.type == SelectTime && selectLeft < selectRight) {
+        selectLeft += m_axisWidth;
+        selectRight += m_axisWidth;
 
-                x += dxdc;
-            }
+        painter.resetTransform();
+        painter.setPen(Qt::green);
+
+        if (m_profile->calls[m_call].cpuStart <= m_selection.timeStart) {
+            painter.drawLine(selectLeft, 0, selectLeft, height());
         }
+
+        if (m_profile->calls[m_call + m_callWidth - 1].cpuStart >= m_selection.timeEnd) {
+            painter.drawLine(selectRight, 0, selectRight, height());
+        }
+
+        selectLeft = qBound(m_axisWidth, selectLeft, width());
+        selectRight = qBound(m_axisWidth, selectRight, width());
+        painter.drawLine(selectLeft, m_axisHeight - 1, selectRight, m_axisHeight - 1);
     }
 }
 
index 266f6945a3ca6d3fb64e0bce1231abbb9fc79940..7ab17c65490001a59ce7ecb73427a54c8c91b0b5 100644 (file)
@@ -12,6 +12,12 @@ enum GraphType {
     GraphCpu
 };
 
+enum SelectType {
+    SelectNone,
+    SelectTime,
+    SelectProgram
+};
+
 class GraphWidget : public QWidget
 {
     Q_OBJECT
@@ -22,18 +28,28 @@ public:
     void setProfile(trace::Profile* profile, GraphType type);
     const trace::Profile::Call* callAtPosition(const QPoint& pos);
 
+    void selectNone(bool notify = false);
+    void selectTime(int64_t start, int64_t end, bool notify = false);
+    void selectProgram(unsigned program, bool notify = false);
+
 protected:
-    virtual void mousePressEvent(QMouseEvent *e);
     virtual void paintEvent(QPaintEvent *e);
-    virtual void mouseMoveEvent(QMouseEvent *e);
+    virtual void resizeEvent(QResizeEvent *e);
+
     virtual void wheelEvent(QWheelEvent *e);
+    virtual void mouseMoveEvent(QMouseEvent *e);
+    virtual void mousePressEvent(QMouseEvent *e);
+    virtual void mouseReleaseEvent(QMouseEvent *e);
     virtual void mouseDoubleClickEvent(QMouseEvent *e);
-    virtual void resizeEvent(QResizeEvent *e);
 
 signals:
     void jumpToCall(int no);
     void viewChanged(int call, int width);
 
+    void selectedNone();
+    void selectedProgram(unsigned program);
+    void selectedTime(int64_t start, int64_t end);
+
 public slots:
     void changeView(int call, int width);
 
@@ -44,34 +60,46 @@ private:
     void paintHorizontalAxis(QPainter& painter);
 
 private:
+    /* Data */
     trace::Profile* m_profile;
     GraphType m_type;
 
-    int64_t m_maxTime;
-
-    int m_minCall;
-    int m_maxCall;
-
-    int m_minCallWidth;
-    int m_maxCallWidth;
+    /* Vertical Axis */
+    int64_t m_timeMax;
 
+    /* Horizontal axis */
     int m_call;
+    int m_callMin;
+    int m_callMax;
     int m_callWidth;
+    int m_callWidthMin;
+    int m_callWidthMax;
 
-    QPoint m_mousePressPosition;
+    /* Viewport */
+    int m_graphWidth;
+    int m_graphHeight;
+
+    /* Mouse track data */
     int m_mousePressCall;
+    QPoint m_mousePressPosition;
 
+    /* Style */
     int m_axisWidth;
     int m_axisHeight;
-
-    int m_graphWidth;
-    int m_graphHeight;
-
     QPen m_axisForeground;
     QBrush m_axisBackground;
-
     QLinearGradient m_graphGradientGpu;
     QLinearGradient m_graphGradientCpu;
+    QLinearGradient m_graphGradientDeselected;
+
+    struct {
+        SelectType type;
+
+        unsigned program;
+
+        int64_t timeStart;
+        int64_t timeEnd;
+    } m_selection;
 };
 
 #endif // GRAPHWIDGET_H
index 146e26a25d42bea544395089c3504a7fe56b9642..f33020386796def64e5d8f3c71e4eacd347c9568 100644 (file)
@@ -9,13 +9,8 @@ ProfileDialog::ProfileDialog(QWidget *parent)
     setupUi(this);
 
     connect(m_timeline, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
-    connect(m_timeline, SIGNAL(selectionChanged(int64_t,int64_t)), SLOT(selectionChanged(int64_t,int64_t)));
-
     connect(m_gpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
     connect(m_cpuGraph, SIGNAL(jumpToCall(int)), SIGNAL(jumpToCall(int)));
-
-    connect(m_gpuGraph, SIGNAL(viewChanged(int,int)), m_cpuGraph, SLOT(changeView(int,int)));
-    connect(m_cpuGraph, SIGNAL(viewChanged(int,int)), m_gpuGraph, SLOT(changeView(int,int)));
 }
 
 
@@ -32,36 +27,113 @@ void ProfileDialog::tableDoubleClicked(const QModelIndex& index)
 
     if (call) {
         emit jumpToCall(call->no);
+    } else {
+        unsigned program = model->getProgram(index);
+        m_timeline->selectProgram(program);
+        m_cpuGraph->selectProgram(program);
+        m_gpuGraph->selectProgram(program);
     }
 }
 
 
 void ProfileDialog::setProfile(trace::Profile* profile)
 {
-    if (m_profile) {
-        delete m_profile;
+    delete m_profile;
+
+    if (profile->frames.size() == 0) {
+        m_profile = NULL;
+    } else {
+        m_profile = profile;
+        m_timeline->setProfile(m_profile);
+        m_gpuGraph->setProfile(m_profile, GraphGpu);
+        m_cpuGraph->setProfile(m_profile, GraphCpu);
+
+        ProfileTableModel* model = new ProfileTableModel(m_table);
+        model->setProfile(m_profile);
+
+        delete m_table->model();
+        m_table->setModel(model);
+        m_table->update(QModelIndex());
+        m_table->sortByColumn(1, Qt::DescendingOrder);
+        m_table->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
+        m_table->resizeColumnsToContents();
+    }
+}
+
+
+void ProfileDialog::selectNone()
+{
+    QObject* src = QObject::sender();
+
+    /* Update table model */
+    ProfileTableModel* model = (ProfileTableModel*)m_table->model();
+    model->selectNone();
+    m_table->reset();
+
+    /* Update graphs */
+    if (src != m_gpuGraph) {
+        m_gpuGraph->selectNone();
+    }
+
+    if (src != m_cpuGraph) {
+        m_cpuGraph->selectNone();
+    }
+
+    /* Update timeline */
+    if (src != m_timeline) {
+        m_timeline->selectNone();
     }
+}
+
+
+void ProfileDialog::selectProgram(unsigned program)
+{
+    QObject* src = QObject::sender();
 
-    m_profile = profile;
-    m_timeline->setProfile(m_profile);
-    m_gpuGraph->setProfile(m_profile, GraphGpu);
-    m_cpuGraph->setProfile(m_profile, GraphCpu);
+    /* Update table model */
+    ProfileTableModel* model = (ProfileTableModel*)m_table->model();
+    model->selectNone();
+    m_table->reset();
+    m_table->selectRow(model->getRowIndex(program));
+
+    /* Update graphs */
+    if (src != m_gpuGraph) {
+        m_gpuGraph->selectProgram(program);
+    }
 
-    ProfileTableModel* model = new ProfileTableModel(m_table);
-    model->setProfile(m_profile);
+    if (src != m_cpuGraph) {
+        m_cpuGraph->selectProgram(program);
+    }
 
-    delete m_table->model();
-    m_table->setModel(model);
-    m_table->resizeColumnsToContents();
-    m_table->sortByColumn(2, Qt::DescendingOrder);
+    /* Update timeline */
+    if (src != m_timeline) {
+        m_timeline->selectProgram(program);
+    }
 }
 
 
-void ProfileDialog::selectionChanged(int64_t start, int64_t end)
+void ProfileDialog::selectTime(int64_t start, int64_t end)
 {
+    QObject* src = QObject::sender();
+
+    /* Update table model */
     ProfileTableModel* model = (ProfileTableModel*)m_table->model();
-    model->setTimeSelection(start, end);
+    model->selectTime(start, end);
     m_table->reset();
+
+    /* Update graphs */
+    if (src != m_gpuGraph) {
+        m_gpuGraph->selectTime(start, end);
+    }
+
+    if (src != m_cpuGraph) {
+        m_cpuGraph->selectTime(start, end);
+    }
+
+    /* Update timeline */
+    if (src != m_timeline) {
+        m_timeline->selectTime(start, end);
+    }
 }
 
 
index 8a2f39ea175c45b3e7649293fd223ad94d603e69..22ef066f05a25377a0c6fd9e2b2e650048f5f174 100644 (file)
@@ -22,7 +22,9 @@ public slots:
 
     void tableDoubleClicked(const QModelIndex& index);
 
-    void selectionChanged(int64_t start, int64_t end);
+    void selectNone();
+    void selectProgram(unsigned program);
+    void selectTime(int64_t start, int64_t end);
 
 signals:
     void jumpToCall(int call);
index 5b9d48473319a46fdac867184c68bfc51a3fbc64..cbe2eea94189eaeb6125843c9b8d47838a101435 100644 (file)
@@ -48,10 +48,35 @@ void ProfileTableModel::setProfile(trace::Profile* profile)
 }
 
 
-void ProfileTableModel::setTimeSelection(int64_t start, int64_t end)
+/**
+ * Set selection to nothing
+ */
+void ProfileTableModel::selectNone()
+{
+    m_timeMin = m_timeMax = 0;
+    updateModel();
+    sort(m_sortColumn, m_sortOrder);
+}
+
+
+/**
+ * Set selection to program
+ */
+void ProfileTableModel::selectProgram(unsigned /*program*/)
+{
+    /* We have no program based selection for table */
+    selectNone();
+}
+
+
+/**
+ * Set selection to a period of time
+ */
+void ProfileTableModel::selectTime(int64_t start, int64_t end)
 {
     m_timeMin = start;
     m_timeMax = end;
+
     updateModel();
     sort(m_sortColumn, m_sortOrder);
 }
@@ -141,6 +166,33 @@ const trace::Profile::Call *ProfileTableModel::getJumpCall(const QModelIndex & i
 }
 
 
+/**
+ * Get the shader program associated with an item in the table
+ */
+unsigned ProfileTableModel::getProgram(const QModelIndex & index) const
+{
+    const ProfileTableRow& row = m_rowData[index.row()];
+    return row.program;
+}
+
+
+/**
+ * Get the row index for a shader program
+ */
+int ProfileTableModel::getRowIndex(unsigned program) const
+{
+    for (int i = 0; i < m_rowData.size(); ++i) {
+        if (m_rowData[i].program == program)
+            return i;
+    }
+
+    return -1;
+}
+
+
+/**
+ * Get the row data for a shader program
+ */
 ProfileTableRow* ProfileTableModel::getRow(unsigned program) {
     for (QList<ProfileTableRow>::iterator itr = m_rowData.begin(); itr != m_rowData.end(); ++itr) {
         if (itr->program == program)
index dc08ba0ec2b390850b302c207582f9596e6d55a5..da17550393ba83395b7a68539e25e1460b1c2c97 100644 (file)
@@ -37,8 +37,13 @@ public:
     ProfileTableModel(QObject *parent = NULL);
 
     void setProfile(trace::Profile* profile);
-    void setTimeSelection(int64_t start, int64_t end);
 
+    void selectNone();
+    void selectProgram(unsigned program);
+    void selectTime(int64_t start, int64_t end);
+
+    int getRowIndex(unsigned program) const;
+    unsigned getProgram(const QModelIndex & index) const;
     const trace::Profile::Call* getJumpCall(const QModelIndex & index) const;
 
     virtual int rowCount(const QModelIndex & parent) const;
index 0375831871570930eb9d5256aaaecf2e844a4671..0f30225d268c9bf72515ea91fea5fa2562d8354f 100644 (file)
@@ -2,7 +2,7 @@
 #include "profiledialog.h"
 #include "trace_profiler.hpp"
 
-#include <math.h>
+#include <qmath.h>
 #include <QColor>
 #include <QLocale>
 #include <QPainter>
@@ -18,8 +18,6 @@ typedef trace::Profile::Program Program;
 TimelineWidget::TimelineWidget(QWidget *parent)
     : QWidget(parent),
       m_profile(NULL),
-      m_timeSelectionStart(0),
-      m_timeSelectionEnd(0),
       m_rowHeight(20),
       m_axisWidth(50),
       m_axisHeight(30),
@@ -28,8 +26,12 @@ TimelineWidget::TimelineWidget(QWidget *parent)
       m_axisForeground(Qt::black),
       m_axisBackground(QColor(210, 210, 210)),
       m_itemBorder(Qt::red),
-      m_itemForeground(Qt::cyan),
-      m_itemBackground(Qt::red),
+      m_itemGpuForeground(Qt::cyan),
+      m_itemGpuBackground(Qt::red),
+      m_itemCpuForeground(QColor(255, 255, 0)),
+      m_itemCpuBackground(QColor(0, 0, 255)),
+      m_itemDeselectedForeground(Qt::white),
+      m_itemDeselectedBackground(QColor(155, 155, 155)),
       m_selectionBorder(Qt::green),
       m_selectionBackground(QColor(100, 255, 100, 8)),
       m_zoomBorder(QColor(255, 0, 255)),
@@ -38,6 +40,8 @@ TimelineWidget::TimelineWidget(QWidget *parent)
     setBackgroundRole(QPalette::Base);
     setAutoFillBackground(true);
     setMouseTracking(true);
+
+    m_selection.type = SelectNone;
 }
 
 
@@ -74,15 +78,47 @@ void TimelineWidget::setVerticalScrollValue(int value)
 
 
 /**
- * Update the time selection
+ * Set selection to nothing
+ */
+void TimelineWidget::selectNone(bool notify)
+{
+    m_selection.type = SelectNone;
+
+    if (notify) {
+        emit selectedNone();
+    }
+
+    update();
+}
+
+
+/**
+ * Set selection to a program
  */
-void TimelineWidget::setSelection(int64_t start, int64_t end, bool notify)
+void TimelineWidget::selectProgram(unsigned program, bool notify)
 {
-    m_timeSelectionStart = start;
-    m_timeSelectionEnd = end;
+    m_selection.program = program;
+    m_selection.type = SelectProgram;
 
     if (notify) {
-        emit selectionChanged(m_timeSelectionStart, m_timeSelectionEnd);
+        emit selectedProgram(program);
+    }
+
+    update();
+}
+
+
+/**
+ * Set selection to a period of time
+ */
+void TimelineWidget::selectTime(int64_t start, int64_t end, bool notify)
+{
+    m_selection.timeStart = start;
+    m_selection.timeEnd = end;
+    m_selection.type = SelectTime;
+
+    if (notify) {
+        emit selectedTime(start, end);
     }
 
     update();
@@ -158,7 +194,7 @@ std::vector<unsigned>::const_iterator binarySearchTimespanIndexed(
         int64_t time)
 {
     int lower = 0;
-    int upper = end - begin;
+    int upper = end - begin - 1;
     int pos = (lower + upper) / 2;
     std::vector<unsigned>::const_iterator itr = begin + pos;
 
@@ -236,6 +272,27 @@ const Call* TimelineWidget::cpuCallAtTime(int64_t time)
 /**
  * Find the draw call at time
  */
+const Call* TimelineWidget::drawCallAtTime(int64_t time)
+{
+    if (!m_profile) {
+        return NULL;
+    }
+
+    for (int i = 0; i < m_rowPrograms.size(); ++i) {
+        const Call* call = drawCallAtTime(time, m_rowPrograms[i]);
+
+        if (call) {
+            return call;
+        }
+    }
+
+    return NULL;
+}
+
+
+/**
+ * Find the draw call at time for a selected program
+ */
 const Call* TimelineWidget::drawCallAtTime(int64_t time, int program)
 {
     if (!m_profile) {
@@ -314,7 +371,7 @@ void TimelineWidget::setProfile(trace::Profile* profile)
 
     setTimeScroll(m_time);
     setRowScroll(0);
-
+    selectNone();
     update();
 }
 
@@ -371,7 +428,7 @@ void TimelineWidget::resizeEvent(QResizeEvent *e)
 {
     /* Update viewport size */
     m_viewWidth = qMax(0, width() - m_axisWidth);
-    m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight);
+    m_viewHeight = qMax(0, height() - m_axisHeight - m_rowHeight * 2);
 
     /* Update vertical scroll bar */
     if (m_profile) {
@@ -411,25 +468,48 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *e)
                     tooltip = true;
                 }
             } else {
-                int row = (y - m_rowHeight + m_scrollY) / m_rowHeight;
+                const Call* call = NULL;
 
-                if (row < m_rowPrograms.size()) {
-                    const Call* call = drawCallAtTime(time, m_rowPrograms[row]);
-
-                    if (call) {
-                        QString text;
-                        text  = QString::fromStdString(call->name);
-                        text += QString("\nCall: %1").arg(call->no);
-                        text += QString("\nCPU Start: %1").arg(getTimeString(call->cpuStart));
-                        text += QString("\nGPU Start: %1").arg(getTimeString(call->gpuStart));
-                        text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
-                        text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
-                        text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
-
-                        QToolTip::showText(e->globalPos(), text);
-                        tooltip = true;
+                if (y < m_rowHeight * 2) {
+                    call = drawCallAtTime(time);
+                } else {
+                    int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
+
+                    if (row < m_rowPrograms.size()) {
+                        call = drawCallAtTime(time, m_rowPrograms[row]);
                     }
                 }
+
+                if (call) {
+                    QString text;
+                    text  = QString::fromStdString(call->name);
+                    text += QString("\nCall: %1").arg(call->no);
+                    text += QString("\nCPU Start: %1").arg(getTimeString(call->cpuStart));
+                    text += QString("\nGPU Start: %1").arg(getTimeString(call->gpuStart));
+                    text += QString("\nCPU Duration: %1").arg(getTimeString(call->cpuDuration));
+                    text += QString("\nGPU Duration: %1").arg(getTimeString(call->gpuDuration));
+                    text += QString("\nPixels Drawn: %1").arg(QLocale::system().toString((qlonglong)call->pixels));
+
+                    QToolTip::showText(e->globalPos(), text);
+                    tooltip = true;
+                }
+            }
+        } else if (m_mousePosition.x() < m_axisWidth && m_mousePosition.y() > m_axisHeight) {
+            int y = m_mousePosition.y() - m_axisHeight;
+
+            if (y < m_rowHeight) {
+                QToolTip::showText(e->globalPos(), "All CPU calls");
+                tooltip = true;
+            } else if (y < m_rowHeight * 2) {
+                QToolTip::showText(e->globalPos(), "All GPU calls");
+                tooltip = true;
+            } else {
+                int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
+
+                if (row < m_rowPrograms.size()) {
+                    QToolTip::showText(e->globalPos(), QString("All calls in Shader Program %1").arg(m_rowPrograms[row]));
+                    tooltip = true;
+                }
             }
         }
     } else if (e->buttons().testFlag(Qt::LeftButton)) {
@@ -448,7 +528,7 @@ void TimelineWidget::mouseMoveEvent(QMouseEvent *e)
             int64_t down  = positionToTime(m_mousePressPosition.x() - m_axisWidth);
             int64_t up    = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
 
-            setSelection(qMin(down, up), qMax(down, up));
+            selectTime(qMin(down, up), qMax(down, up), true);
         }
 
         update();
@@ -468,18 +548,16 @@ void TimelineWidget::mousePressEvent(QMouseEvent *e)
                 m_mousePressMode = RulerZoom;
             } else {
                 m_mousePressMode = RulerSelect;
-
-                int64_t time = positionToTime(e->pos().x() - m_axisWidth);
-                m_timeSelectionStart = time;
-                m_timeSelectionEnd = time;
             }
-        } else {
+        } else if (e->pos().x() >= m_axisWidth) {
             m_mousePressMode = DragView;
+        } else {
+            m_mousePressMode = NoMousePress;
         }
 
         m_mousePressPosition  = e->pos();
         m_mousePressTime = m_time;
-        m_mousePressRow   = m_scrollY;
+        m_mousePressRow  = m_scrollY;
 
         update();
     }
@@ -493,6 +571,8 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *e)
     }
 
     /* Calculate new time view based on selected area */
+    int dxy = qAbs(m_mousePressPosition.x() - e->pos().x()) + qAbs(m_mousePressPosition.y() - e->pos().y());
+
     int64_t down  = positionToTime(m_mousePressPosition.x() - m_axisWidth);
     int64_t up    = positionToTime(qMax(e->pos().x() - m_axisWidth, 0));
 
@@ -505,8 +585,23 @@ void TimelineWidget::mouseReleaseEvent(QMouseEvent *e)
 
         m_mousePressMode = NoMousePress;
         setTimeScroll(left);
-    } else if (m_mousePressMode == RulerSelect) {
-        setSelection(m_timeSelectionStart, m_timeSelectionEnd, true);
+    } else {
+        if (dxy <= 2) {
+            if (m_selection.type == SelectTime) {
+                if (left < m_selection.timeStart || right > m_selection.timeEnd || e->pos().x() < m_axisWidth) {
+                    selectNone(true);
+                }
+            } else if (m_selection.type == SelectProgram) {
+                int y = e->pos().y() - m_axisHeight;
+                int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
+
+                if (row < 0 || m_rowPrograms[row] != m_selection.program) {
+                    selectNone(true);
+                }
+            }
+        } else if (m_mousePressMode == RulerSelect) {
+            selectTime(left, right, true);
+        }
     }
 }
 
@@ -523,7 +618,7 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e)
             const Frame* frame = frameAtTime(time);
 
             if (frame) {
-                setSelection(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true);
+                selectTime(frame->cpuStart, frame->cpuStart + frame->cpuDuration, true);
                 return;
             }
         } else if (row == 0) {
@@ -543,10 +638,13 @@ void TimelineWidget::mouseDoubleClickEvent(QMouseEvent *e)
                 return;
             }
         }
-    }
+    } else {
+        int y = e->pos().y() - m_axisHeight;
+        int row = (y - m_rowHeight * 2 + m_scrollY) / m_rowHeight;
 
-    if (time < m_timeSelectionStart || time > m_timeSelectionEnd) {
-        setSelection(0, 0, true);
+        if (row >= 0 && row < m_rowPrograms.size()) {
+            selectProgram(m_rowPrograms[row], true);
+        }
     }
 }
 
@@ -557,6 +655,10 @@ void TimelineWidget::wheelEvent(QWheelEvent *e)
         return;
     }
 
+    if (e->pos().x() < m_axisWidth) {
+        return;
+    }
+
     int zoomPercent = 10;
 
     /* If holding Ctrl key then zoom 2x faster */
@@ -588,22 +690,31 @@ void TimelineWidget::wheelEvent(QWheelEvent *e)
 /**
  * Paints a single pixel column of the heat map
  */
-void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu)
+void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected)
 {
     if (heat == 0) {
         return;
     }
 
-    double timePerPixel = m_timeWidth;
-    timePerPixel /= m_viewWidth;
+    if (m_selection.type == SelectTime) {
+        selected = x >= m_selectionLeft && x <= m_selectionRight;
+    }
 
+    double timePerPixel = m_timeWidth / (double)m_viewWidth;
     double colour = heat / timePerPixel;
-    colour = qBound(0.0, colour * 255.0, 255.0);
 
-    if (isCpu) {
-        painter.setPen(QColor(255 - colour, 255 - colour, 255));
-    } else {
+    /* Gamma correction */
+    colour = qPow(colour, 1.0 / 2.0);
+
+    if (!selected) {
+        colour = qBound(0.0, colour * 100.0, 100.0);
+        painter.setPen(QColor(255 - colour, 255 - colour, 255 - colour));
+    } else if (gpu) {
+        colour = qBound(0.0, colour * 255.0, 255.0);
         painter.setPen(QColor(255, 255 - colour, 255 - colour));
+    } else {
+        colour = qBound(0.0, colour * 255.0, 255.0);
+        painter.setPen(QColor(255 - colour, 255 - colour, 255));
     }
 
     painter.drawLine(x, 0, x, m_rowHeight - 1);
@@ -611,156 +722,176 @@ void TimelineWidget::drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu
 
 
 /**
- * Render the whole widget
+ * Draws a call on the heatmap
  */
-void TimelineWidget::paintEvent(QPaintEvent *e)
+bool TimelineWidget::drawCall(QPainter& painter, const trace::Profile::Call& call, int& lastX, int64_t& heat, bool gpu)
 {
-    if (!m_profile)
-        return;
+    int64_t start, duration, end;
 
-    QPainter painter(this);
+    if (gpu) {
+        start = call.gpuStart;
+        duration = call.gpuDuration;
+    } else {
+        start = call.cpuStart;
+        duration = call.cpuDuration;
+    }
 
-    int rowEnd = qMin(m_row + (m_viewHeight / m_rowHeight) + 1, m_rowCount);
-    int64_t timeEnd = m_time + m_timeWidth;
-    int64_t heat = 0;
-    int lastX = 0;
-    int widgetHeight = height();
-    int widgetWidth = width();
+    end = start + duration;
 
-    /* Draw GPU rows */
-    painter.translate(m_axisWidth, m_axisHeight + m_rowHeight - (m_scrollY % m_rowHeight));
+    if (start > m_timeEnd) {
+        return false;
+    }
 
-    for (int row = m_row; row < rowEnd; ++row) {
-        Program& program = m_profile->programs[m_rowPrograms[row]];
-        lastX = 0;
-        heat = 0;
+    if (end < m_time) {
+        return true;
+    }
 
-        for (std::vector<unsigned>::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) {
-            const Call& call = m_profile->calls[*itr];
-            int64_t gpuEnd = call.gpuStart + call.gpuDuration;
+    double left  = timeToPosition(start);
+    double right = timeToPosition(end);
 
-            if (call.gpuStart > timeEnd) {
-                break;
-            }
+    int leftX  = left;
+    int rightX = right;
 
-            if (gpuEnd < m_time) {
-                continue;
-            }
+    bool selected = true;
 
-            double left  = timeToPosition(call.gpuStart);
-            double right = timeToPosition(gpuEnd);
+    if (m_selection.type == SelectProgram) {
+        selected = call.program == m_selection.program;
+    }
 
-            int leftX  = left;
-            int rightX = right;
+    /* Draw last heat if needed */
+    if (leftX != lastX) {
+        drawHeat(painter, lastX, heat, gpu, selected);
+        lastX = leftX;
+        heat = 0;
+    }
 
-            /* Draw last heat if needed */
-            if (leftX != lastX) {
-                drawHeat(painter, lastX, heat, false);
-                lastX = leftX;
-                heat = 0;
-            }
+    if (rightX <= leftX + 1) {
+        if (rightX == lastX) {
+            /* Fully contained in this X */
+            heat += duration;
+        } else {
+            /* Split call time between the two pixels it occupies */
+            int64_t time = positionToTime(rightX);
+            heat += time - start;
 
-            if (rightX <= leftX + 1) {
-                if (rightX == lastX) {
-                    /* Fully contained in this X */
-                    heat += call.gpuDuration;
-                } else {
-                    /* Split call time between the two pixels it occupies */
-                    int64_t time = positionToTime(rightX);
+            drawHeat(painter, lastX, heat, gpu, selected);
 
-                    heat += time - call.gpuStart;
-                    drawHeat(painter, lastX, heat, false);
+            heat = end - time;
+            lastX = rightX;
+        }
+    } else {
+        QRect rect;
+        rect.setLeft(left + 0.5);
+        rect.setWidth(right - left);
+        rect.setTop(0);
+        rect.setHeight(m_rowHeight);
+
+        if (m_selection.type == SelectTime) {
+            selected = (start >= m_selection.timeStart && start <= m_selection.timeEnd)
+                    || (end >= m_selection.timeStart && end <= m_selection.timeEnd);
+        }
 
-                    heat = gpuEnd - time;
-                    lastX = rightX;
-                }
+        /* Draw background rect */
+        if (selected) {
+            if (gpu) {
+                painter.fillRect(rect, m_itemGpuBackground);
             } else {
-                QRect rect;
-                rect.setLeft(left + 0.5);
-                rect.setWidth(right - left);
-                rect.setTop(0);
-                rect.setHeight(m_rowHeight);
-
-                painter.fillRect(rect, m_itemBackground);
+                painter.fillRect(rect, m_itemCpuBackground);
+            }
+        } else {
+            painter.fillRect(rect, m_itemDeselectedBackground);
+        }
 
-                if (rect.width() > 6) {
-                    rect.adjust(1, 0, -1, -2);
-                    painter.setPen(m_itemForeground);
+        /* If wide enough, draw text */
+        if (rect.width() > 6) {
+            rect.adjust(1, 0, -1, -2);
 
-                    painter.drawText(rect,
-                                     Qt::AlignLeft | Qt::AlignVCenter,
-                                     painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
+            if (selected) {
+                if (gpu) {
+                    painter.setPen(m_itemGpuForeground);
+                } else {
+                    painter.setPen(m_itemCpuForeground);
                 }
+            } else {
+                painter.setPen(m_itemDeselectedForeground);
             }
-        }
 
-        painter.translate(0, m_rowHeight);
+            painter.drawText(rect,
+                             Qt::AlignLeft | Qt::AlignVCenter,
+                             painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
+        }
     }
 
-    /* Draw CPU row */
-    painter.resetTransform();
-    painter.translate(m_axisWidth, m_axisHeight);
-    painter.fillRect(0, 0, m_viewWidth, m_rowHeight, Qt::white);
+    return true;
+}
 
-    for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
-        const Call& call = *itr;
-        int64_t cpuEnd = call.cpuStart + call.cpuDuration;
 
-        if (call.cpuStart > timeEnd) {
-            continue;
-        }
+/**
+ * Render the whole widget
+ */
+void TimelineWidget::paintEvent(QPaintEvent *e)
+{
+    if (!m_profile)
+        return;
 
-        if (cpuEnd < m_time) {
-            continue;
-        }
+    QPainter painter(this);
 
-        double left = timeToPosition(call.cpuStart);
-        double right = timeToPosition(cpuEnd);
+    int rowEnd = qMin(m_row + qCeil(m_viewHeight / (double)m_rowHeight) + 1, m_rowCount);
+    int64_t heatGPU = 0, heatCPU = 0;
+    int lastCpuX = 0, lastGpuX = 0;
+    int widgetHeight = height();
+    int widgetWidth = width();
 
-        int leftX = left;
-        int rightX = right;
+    m_timeEnd = m_time + m_timeWidth;
+    m_selectionLeft  = timeToPosition(m_selection.timeStart);
+    m_selectionRight = (timeToPosition(m_selection.timeEnd) + 0.5);
 
-        /* Draw last heat if needed */
-        if (leftX != lastX) {
-            drawHeat(painter, lastX, heat, true);
-            lastX = leftX;
-            heat = 0;
-        }
 
-        if (rightX <= leftX + 1) {
-            if (rightX == lastX) {
-                /* Fully contained in this X */
-                heat += call.cpuDuration;
-            } else {
-                /* Split call time between the two pixels it occupies */
-                int64_t time = positionToTime(rightX);
+    /* Draw program rows */
+    painter.translate(m_axisWidth, m_axisHeight + m_rowHeight * 2 - (m_scrollY % m_rowHeight));
+
+    for (int row = m_row; row < rowEnd; ++row) {
+        Program& program = m_profile->programs[m_rowPrograms[row]];
+        lastGpuX = 0;
+        heatGPU = 0;
 
-                heat += time - call.cpuStart;
-                drawHeat(painter, lastX, heat, true);
+        for (std::vector<unsigned>::const_iterator itr = program.calls.begin(); itr != program.calls.end(); ++itr) {
+            const Call& call = m_profile->calls[*itr];
 
-                heat = cpuEnd - time;
-                lastX = rightX;
+            if (!drawCall(painter, call, lastGpuX, heatGPU, true)) {
+                break;
             }
-        } else {
-            QRect rect;
-            rect.setLeft(left + 0.5);
-            rect.setWidth(right - left);
-            rect.setTop(0);
-            rect.setHeight(m_rowHeight);
+        }
 
-            painter.fillRect(rect, QColor(0, 0, 255));
+        painter.translate(0, m_rowHeight);
+    }
 
-            if (rect.width() > 6) {
-                rect.adjust(1, 0, -1, -2);
-                painter.setPen(QColor(255, 255, 0));
 
-                painter.drawText(rect,
-                                 Qt::AlignLeft | Qt::AlignVCenter,
-                                 painter.fontMetrics().elidedText(QString::fromStdString(call.name), Qt::ElideRight, rect.width()));
-            }
+    /* Draw CPU/GPU rows */
+    painter.resetTransform();
+    painter.translate(m_axisWidth, m_axisHeight);
+    painter.fillRect(0, 0, m_viewWidth, m_rowHeight * 2, Qt::white);
+
+    lastCpuX = lastGpuX = 0;
+    heatCPU = heatGPU = 0;
+
+    for (std::vector<Call>::const_iterator itr = m_profile->calls.begin(); itr != m_profile->calls.end(); ++itr) {
+        const Call& call = *itr;
+
+        /* Draw gpu row */
+        if (call.pixels >= 0) {
+            painter.translate(0, m_rowHeight);
+            drawCall(painter, call, lastGpuX, heatGPU, true);
+            painter.translate(0, -m_rowHeight);
+        }
+
+        /* Draw cpu row */
+        if (!drawCall(painter, call, lastCpuX, heatCPU, false)) {
+            break;
         }
     }
 
+
     /* Draw axis */
     painter.resetTransform();
     painter.setPen(m_axisBorder);
@@ -773,8 +904,9 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
     painter.fillRect(0, m_axisHeight - 1, m_axisWidth - 1, widgetHeight, m_axisBackground);
     painter.drawLine(m_axisWidth - 1, 0, m_axisWidth - 1, widgetHeight);
 
+
     /* Draw the program numbers */
-    painter.translate(0, m_axisHeight + m_rowHeight);
+    painter.translate(0, m_axisHeight + m_rowHeight * 2);
 
     for (int row = m_row; row < rowEnd; ++row) {
         int y = (row - m_row) * m_rowHeight - (m_scrollY % m_rowHeight);
@@ -782,13 +914,21 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
         painter.setPen(m_axisForeground);
         painter.drawText(0, y, m_axisWidth, m_rowHeight, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1").arg(m_rowPrograms[row]));
 
-        painter.setPen(m_axisBorder);
-        painter.drawLine(0, y + m_rowHeight - 1, m_axisWidth - 1, y + m_rowHeight - 1);
+        if (m_selection.type == SelectProgram && m_selection.program == m_rowPrograms[row]) {
+            painter.setPen(m_selectionBorder);
+            painter.drawLine(0, qMax(0, y - 1), widgetWidth, qMax(0, y - 1));
+            painter.drawLine(0, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
+            painter.drawLine(m_axisWidth - 1, y - 1, m_axisWidth - 1, y + m_rowHeight - 1);
+        } else {
+            painter.setPen(m_axisBorder);
+            painter.drawLine(0, y + m_rowHeight - 1, m_axisWidth - 1, y + m_rowHeight - 1);
 
-        painter.setPen(m_axisLine);
-        painter.drawLine(m_axisWidth, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
+            painter.setPen(m_axisLine);
+            painter.drawLine(m_axisWidth, y + m_rowHeight - 1, widgetWidth, y + m_rowHeight - 1);
+        }
     }
 
+
     /* Draw the "CPU" axis label */
     painter.resetTransform();
     painter.translate(0, m_axisHeight);
@@ -804,13 +944,27 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
     painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1);
 
 
+    /* Draw the "GPU" axis label */
+    painter.translate(0, m_rowHeight);
+
+    painter.setPen(m_axisBorder);
+    painter.setBrush(m_axisBackground);
+    painter.drawRect(-1, -1, m_axisWidth, m_rowHeight);
+
+    painter.setPen(m_axisForeground);
+    painter.drawText(0, 0, m_axisWidth - 1, m_rowHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "GPU");
+
+    painter.setPen(m_axisBorder);
+    painter.drawLine(m_axisWidth, m_rowHeight - 1, widgetWidth, m_rowHeight - 1);
+
+
     /* Draw the frame numbers */
     painter.resetTransform();
 
     painter.setPen(m_axisForeground);
     painter.translate(m_axisWidth, 0);
 
-    int lastLabel = -9999;
+    int lastLabel = -999; /* Ensure first label gets drawn */
 
     double scroll = m_time;
     scroll /= m_timeWidth;
@@ -822,7 +976,7 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
         bool draw = true;
         int width;
 
-        if (frame.cpuStart > timeEnd) {
+        if (frame.cpuStart > m_timeEnd) {
             break;
         }
 
@@ -873,6 +1027,7 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
         }
     }
 
+
     /* Draw "Frame" axis label */
     painter.resetTransform();
 
@@ -883,28 +1038,30 @@ void TimelineWidget::paintEvent(QPaintEvent *e)
     painter.setPen(m_axisForeground);
     painter.drawText(0, 0, m_axisWidth - 1, m_axisHeight - 1, Qt::AlignHCenter | Qt::AlignVCenter, "Frame");
 
-    /* Draw the active selection border */
-    if (m_timeSelectionStart != m_timeSelectionEnd) {
-        int selectionLeft  = timeToPosition(m_timeSelectionStart) + m_axisWidth;
-        int selectionRight = (timeToPosition(m_timeSelectionEnd) + 0.5) + m_axisWidth;
 
+    /* Draw the active selection border */
+    if (m_selection.type == SelectTime) {
         painter.setPen(m_selectionBorder);
 
-        if (selectionLeft >= m_axisWidth && selectionLeft < widgetWidth) {
-            painter.drawLine(selectionLeft, 0, selectionLeft, widgetHeight);
+        m_selectionLeft += m_axisWidth;
+        m_selectionRight += m_axisWidth;
+
+        if (m_selectionLeft >= m_axisWidth && m_selectionLeft < widgetWidth) {
+            painter.drawLine(m_selectionLeft, 0, m_selectionLeft, widgetHeight);
         }
 
-        if (selectionRight >= m_axisWidth && selectionRight < widgetWidth) {
-            painter.drawLine(selectionRight, 0, selectionRight, widgetHeight);
+        if (m_selectionRight >= m_axisWidth && m_selectionRight < widgetWidth) {
+            painter.drawLine(m_selectionRight, 0, m_selectionRight, widgetHeight);
         }
 
-        selectionLeft = qBound(m_axisWidth, selectionLeft, widgetWidth);
-        selectionRight = qBound(m_axisWidth, selectionRight, widgetWidth);
+        m_selectionLeft = qBound(m_axisWidth, m_selectionLeft, widgetWidth);
+        m_selectionRight = qBound(m_axisWidth, m_selectionRight, widgetWidth);
 
-        painter.drawLine(selectionLeft, m_axisHeight - 1, selectionRight, m_axisHeight - 1);
-        painter.fillRect(selectionLeft, 0, selectionRight - selectionLeft, widgetHeight, m_selectionBackground);
+        painter.drawLine(m_selectionLeft, m_axisHeight - 1, m_selectionRight, m_axisHeight - 1);
+        painter.fillRect(m_selectionLeft, 0, m_selectionRight - m_selectionLeft, widgetHeight, m_selectionBackground);
     }
 
+
     /* Draw the ruler zoom */
     if (m_mousePressMode == RulerZoom) {
         int x1 = m_mousePressPosition.x();
index 9d5d8a51082d2d5b88970ab31275cdef3c99cd53..ba42a101eb018a34fac2d5716f21f840dfd98bca 100644 (file)
@@ -17,11 +17,21 @@ class TimelineWidget : public QWidget
         RulerSelect
     };
 
+    enum SelectType {
+        SelectNone,
+        SelectTime,
+        SelectProgram
+    };
+
 public:
     TimelineWidget(QWidget *parent = 0);
 
     void setProfile(trace::Profile* profile);
 
+    void selectNone(bool notify = false);
+    void selectProgram(unsigned program, bool notify = false);
+    void selectTime(int64_t start, int64_t end, bool notify = false);
+
 protected:
     virtual void wheelEvent(QWheelEvent *e);
     virtual void mousePressEvent(QMouseEvent *e);
@@ -45,14 +55,16 @@ signals:
 
     void jumpToCall(int call);
 
-    void selectionChanged(int64_t start, int64_t end);
+    void selectedNone();
+    void selectedProgram(unsigned program);
+    void selectedTime(int64_t start, int64_t end);
 
 private:
-    void setSelection(int64_t start, int64_t end, bool notify = false);
     void setRowScroll(int position, bool notify = true);
     void setTimeScroll(int64_t time, bool notify = true);
 
-    void drawHeat(QPainter& painter, int x, int64_t heat, bool isCpu);
+    bool drawCall(QPainter& painter, const trace::Profile::Call& call, int &lastX, int64_t &heat, bool gpu);
+    void drawHeat(QPainter& painter, int x, int64_t heat, bool gpu, bool selected);
 
     double timeToPosition(int64_t time);
     int64_t positionToTime(int pos);
@@ -61,6 +73,7 @@ private:
 
     const trace::Profile::Frame* frameAtTime(int64_t time);
     const trace::Profile::Call* cpuCallAtTime(int64_t time);
+    const trace::Profile::Call* drawCallAtTime(int64_t time);
     const trace::Profile::Call* drawCallAtTime(int64_t time, int program);
 
 private:
@@ -80,13 +93,15 @@ private:
 
     /* Visible Times */
     int64_t m_time;
+    int64_t m_timeEnd;
     int64_t m_timeMin;
     int64_t m_timeMax;
     int64_t m_timeWidth;
     int64_t m_timeWidthMin;
     int64_t m_timeWidthMax;
-    int64_t m_timeSelectionStart;
-    int64_t m_timeSelectionEnd;
+
+    int m_selectionLeft;
+    int m_selectionRight;
 
     /* Visible Rows */
     int m_row;
@@ -108,12 +123,26 @@ private:
     QPen m_axisForeground;
     QBrush m_axisBackground;
     QPen m_itemBorder;
-    QPen m_itemForeground;
-    QBrush m_itemBackground;
+    QPen m_itemGpuForeground;
+    QBrush m_itemGpuBackground;
+    QPen m_itemCpuForeground;
+    QBrush m_itemCpuBackground;
+    QPen m_itemDeselectedForeground;
+    QBrush m_itemDeselectedBackground;
     QPen m_selectionBorder;
     QBrush m_selectionBackground;
     QPen m_zoomBorder;
     QBrush m_zoomBackground;
+
+    /* Selection */
+    struct {
+        SelectType type;
+
+        unsigned program;
+
+        int64_t timeStart;
+        int64_t timeEnd;
+    } m_selection;
 };
 
 #endif // TIMELINEWIDGET_H
index 49b77bd1505dbb3e0ce01df019682e778a19a115..7a54ef72595e8c68b964c98b8e5de9636422c6c7 100644 (file)
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1079</width>
-    <height>768</height>
+    <width>1105</width>
+    <height>804</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Profile Results</string>
   </property>
-  <layout class="QVBoxLayout" name="verticalLayout_3">
-   <property name="spacing">
-    <number>0</number>
-   </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
    <property name="margin">
     <number>0</number>
    </property>
    <item>
-    <widget class="QTabWidget" name="tabWidget">
-     <property name="currentIndex">
-      <number>1</number>
+    <widget class="QSplitter" name="splitter">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
      </property>
-     <widget class="QWidget" name="tabTimeline">
-      <attribute name="title">
-       <string>Timeline</string>
-      </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_2">
+     <property name="opaqueResize">
+      <bool>false</bool>
+     </property>
+     <widget class="QWidget" name="timelineContainer" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>1</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="focusPolicy">
+       <enum>Qt::WheelFocus</enum>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <property name="spacing">
+        <number>0</number>
+       </property>
        <property name="margin">
         <number>0</number>
        </property>
        <item>
-        <widget class="QSplitter" name="splitter">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
+        <layout class="QGridLayout" name="gridLayout">
+         <property name="spacing">
+          <number>2</number>
          </property>
-         <widget class="QWidget" name="timelineContainer" native="true">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-            <horstretch>0</horstretch>
-            <verstretch>1</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="focusPolicy">
-           <enum>Qt::WheelFocus</enum>
-          </property>
-          <layout class="QVBoxLayout" name="verticalLayout">
-           <property name="spacing">
-            <number>0</number>
+         <item row="0" column="0">
+          <widget class="TimelineWidget" name="m_timeline" native="true">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
            </property>
-           <property name="margin">
-            <number>0</number>
+           <property name="focusPolicy">
+            <enum>Qt::WheelFocus</enum>
            </property>
-           <item>
-            <layout class="QGridLayout" name="gridLayout">
-             <property name="spacing">
-              <number>2</number>
-             </property>
-             <item row="0" column="0">
-              <widget class="TimelineWidget" name="m_timeline" native="true">
-               <property name="sizePolicy">
-                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-                 <horstretch>0</horstretch>
-                 <verstretch>0</verstretch>
-                </sizepolicy>
-               </property>
-               <property name="focusPolicy">
-                <enum>Qt::WheelFocus</enum>
-               </property>
-              </widget>
-             </item>
-             <item row="0" column="1">
-              <widget class="QScrollBar" name="m_verticalScrollBar">
-               <property name="sizePolicy">
-                <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
-                 <horstretch>0</horstretch>
-                 <verstretch>0</verstretch>
-                </sizepolicy>
-               </property>
-               <property name="orientation">
-                <enum>Qt::Vertical</enum>
-               </property>
-              </widget>
-             </item>
-             <item row="1" column="0">
-              <widget class="QScrollBar" name="m_horizontalScrollBar">
-               <property name="sizePolicy">
-                <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-                 <horstretch>0</horstretch>
-                 <verstretch>0</verstretch>
-                </sizepolicy>
-               </property>
-               <property name="maximum">
-                <number>10000</number>
-               </property>
-               <property name="orientation">
-                <enum>Qt::Horizontal</enum>
-               </property>
-              </widget>
-             </item>
-            </layout>
-           </item>
-          </layout>
-         </widget>
-         <widget class="QTableView" name="m_table">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="editTriggers">
-           <set>QAbstractItemView::NoEditTriggers</set>
-          </property>
-          <property name="selectionMode">
-           <enum>QAbstractItemView::SingleSelection</enum>
-          </property>
-          <property name="verticalScrollMode">
-           <enum>QAbstractItemView::ScrollPerPixel</enum>
-          </property>
-          <property name="horizontalScrollMode">
-           <enum>QAbstractItemView::ScrollPerPixel</enum>
-          </property>
-          <property name="sortingEnabled">
-           <bool>true</bool>
-          </property>
-          <property name="wordWrap">
-           <bool>false</bool>
-          </property>
-          <attribute name="verticalHeaderVisible">
-           <bool>false</bool>
-          </attribute>
-         </widget>
-        </widget>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QScrollBar" name="m_verticalScrollBar">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="orientation">
+            <enum>Qt::Vertical</enum>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QScrollBar" name="m_horizontalScrollBar">
+           <property name="sizePolicy">
+            <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+             <horstretch>0</horstretch>
+             <verstretch>0</verstretch>
+            </sizepolicy>
+           </property>
+           <property name="maximum">
+            <number>10000</number>
+           </property>
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+          </widget>
+         </item>
+        </layout>
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="tabCalls">
-      <attribute name="title">
-       <string>Histogram</string>
-      </attribute>
-      <layout class="QVBoxLayout" name="verticalLayout_4">
+     <widget class="QWidget" name="graphContainer" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>200</height>
+       </size>
+      </property>
+      <layout class="QVBoxLayout" name="verticalLayout_3">
+       <property name="spacing">
+        <number>0</number>
+       </property>
        <property name="margin">
         <number>0</number>
        </property>
        <item>
-        <widget class="QSplitter" name="splitter_2">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
+        <widget class="GraphWidget" name="m_gpuGraph" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="focusPolicy">
+          <enum>Qt::WheelFocus</enum>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <widget class="GraphWidget" name="m_cpuGraph" native="true">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
          </property>
-         <widget class="GraphWidget" name="m_gpuGraph" native="true">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-          <property name="focusPolicy">
-           <enum>Qt::WheelFocus</enum>
-          </property>
-         </widget>
-         <widget class="GraphWidget" name="m_cpuGraph" native="true">
-          <property name="sizePolicy">
-           <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-            <horstretch>0</horstretch>
-            <verstretch>0</verstretch>
-           </sizepolicy>
-          </property>
-         </widget>
         </widget>
        </item>
       </layout>
      </widget>
+     <widget class="QTableView" name="m_table">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="editTriggers">
+       <set>QAbstractItemView::NoEditTriggers</set>
+      </property>
+      <property name="selectionMode">
+       <enum>QAbstractItemView::SingleSelection</enum>
+      </property>
+      <property name="selectionBehavior">
+       <enum>QAbstractItemView::SelectRows</enum>
+      </property>
+      <property name="verticalScrollMode">
+       <enum>QAbstractItemView::ScrollPerPixel</enum>
+      </property>
+      <property name="horizontalScrollMode">
+       <enum>QAbstractItemView::ScrollPerPixel</enum>
+      </property>
+      <property name="sortingEnabled">
+       <bool>true</bool>
+      </property>
+      <property name="wordWrap">
+       <bool>false</bool>
+      </property>
+      <attribute name="verticalHeaderVisible">
+       <bool>false</bool>
+      </attribute>
+     </widget>
     </widget>
    </item>
   </layout>
     <signal>horizontalScrollMaxChanged(int)</signal>
     <signal>verticalScrollMaxChanged(int)</signal>
     <signal>jumpToCall(int)</signal>
+    <signal>selectedTime(int64_t,int64_t)</signal>
+    <signal>selectedProgram(unsigned)</signal>
+    <signal>selectedNone()</signal>
     <slot>setHorizontalScrollValue(int)</slot>
     <slot>setVerticalScrollValue(int)</slot>
    </slots>
    <extends>QWidget</extends>
    <header>graphwidget.h</header>
    <container>1</container>
+   <slots>
+    <signal>viewChanged(int,int)</signal>
+    <signal>selectedTime(int64_t,int64_t)</signal>
+    <signal>selectedProgram(unsigned)</signal>
+    <signal>selectedNone()</signal>
+    <slot>changeView(int,int)</slot>
+   </slots>
   </customwidget>
  </customwidgets>
  <resources/>
    <slot>setHorizontalScrollValue(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>402</x>
-     <y>195</y>
+     <x>373</x>
+     <y>434</y>
     </hint>
     <hint type="destinationlabel">
-     <x>402</x>
-     <y>93</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
    </hints>
   </connection>
    <slot>setVerticalScrollValue(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>813</x>
-     <y>93</y>
+     <x>754</x>
+     <y>213</y>
     </hint>
     <hint type="destinationlabel">
-     <x>402</x>
-     <y>93</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
    </hints>
   </connection>
    <slot>setValue(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>402</x>
-     <y>93</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
     <hint type="destinationlabel">
-     <x>402</x>
-     <y>195</y>
+     <x>373</x>
+     <y>434</y>
     </hint>
    </hints>
   </connection>
    <slot>setValue(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>402</x>
-     <y>93</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
     <hint type="destinationlabel">
-     <x>813</x>
-     <y>93</y>
+     <x>754</x>
+     <y>213</y>
     </hint>
    </hints>
   </connection>
    <slot>setHorizontalScrollMax(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>504</x>
-     <y>277</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
     <hint type="destinationlabel">
      <x>511</x>
    <slot>setVerticalScrollMax(int)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>504</x>
-     <y>277</y>
+     <x>373</x>
+     <y>213</y>
     </hint>
     <hint type="destinationlabel">
      <x>511</x>
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>m_cpuGraph</sender>
+   <signal>viewChanged(int,int)</signal>
+   <receiver>m_gpuGraph</receiver>
+   <slot>changeView(int,int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>511</x>
+     <y>687</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>511</x>
+     <y>527</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_gpuGraph</sender>
+   <signal>viewChanged(int,int)</signal>
+   <receiver>m_cpuGraph</receiver>
+   <slot>changeView(int,int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>511</x>
+     <y>527</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>511</x>
+     <y>687</y>
+    </hint>
+   </hints>
+  </connection>
   <connection>
    <sender>m_table</sender>
    <signal>doubleClicked(QModelIndex)</signal>
    <slot>tableDoubleClicked(QModelIndex)</slot>
    <hints>
     <hint type="sourcelabel">
-     <x>511</x>
-     <y>671</y>
+     <x>895</x>
+     <y>220</y>
     </hint>
     <hint type="destinationlabel">
      <x>511</x>
     </hint>
    </hints>
   </connection>
+  <connection>
+   <sender>m_cpuGraph</sender>
+   <signal>selectedTime(int64_t,int64_t)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectTime(int64_t,int64_t)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>555</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_gpuGraph</sender>
+   <signal>selectedTime(int64_t,int64_t)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectTime(int64_t,int64_t)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>455</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_timeline</sender>
+   <signal>selectedTime(int64_t,int64_t)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectTime(int64_t,int64_t)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>544</x>
+     <y>192</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_cpuGraph</sender>
+   <signal>selectedProgram(unsigned)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectProgram(unsigned)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>555</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_gpuGraph</sender>
+   <signal>selectedProgram(unsigned)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectProgram(unsigned)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>455</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_timeline</sender>
+   <signal>selectedProgram(unsigned)</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectProgram(unsigned)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>544</x>
+     <y>192</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_cpuGraph</sender>
+   <signal>selectedNone()</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectNone()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>555</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_gpuGraph</sender>
+   <signal>selectedNone()</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectNone()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>552</x>
+     <y>455</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>m_timeline</sender>
+   <signal>selectedNone()</signal>
+   <receiver>ProfileDialog</receiver>
+   <slot>selectNone()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>544</x>
+     <y>192</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>552</x>
+     <y>401</y>
+    </hint>
+   </hints>
+  </connection>
  </connections>
  <slots>
   <signal>jumpToCall(int)</signal>
   <slot>setVerticalScrollMax(int)</slot>
   <slot>setHorizontalScrollMax(int)</slot>
   <slot>tableDoubleClicked(QModelIndex)</slot>
+  <slot>selectTime(int64_t,int64_t)</slot>
+  <slot>selectProgram(unsigned)</slot>
+  <slot>selectNone()</slot>
  </slots>
 </ui>