1/*
2* Copyright 2017 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#include "StatsLayer.h"
9
10#include "SkCanvas.h"
11#include "SkString.h"
12#include "SkTime.h"
13
14StatsLayer::StatsLayer()
15    : fCurrentMeasurement(0)
16    , fCumulativeMeasurementTime(0)
17    , fCumulativeMeasurementCount(0) {}
18
19void StatsLayer::resetMeasurements() {
20    for (int i = 0; i < fTimers.count(); ++i) {
21        memset(fTimers[i].fTimes, 0, sizeof(fTimers[i].fTimes));
22    }
23    fCurrentMeasurement = 0;
24    fCumulativeMeasurementTime = 0;
25    fCumulativeMeasurementCount = 0;
26}
27
28StatsLayer::Timer StatsLayer::addTimer(const char* label, SkColor color, SkColor labelColor) {
29    Timer newTimer = fTimers.count();
30    TimerData& newData = fTimers.push_back();
31    memset(newData.fTimes, 0, sizeof(newData.fTimes));
32    newData.fLabel = label;
33    newData.fColor = color;
34    newData.fLabelColor = labelColor ? labelColor : color;
35    return newTimer;
36}
37
38void StatsLayer::beginTiming(Timer timer) {
39    fTimers[timer].fTimes[fCurrentMeasurement] -= SkTime::GetMSecs();
40}
41
42void StatsLayer::endTiming(Timer timer) {
43    fTimers[timer].fTimes[fCurrentMeasurement] += SkTime::GetMSecs();
44}
45
46double StatsLayer::getLastTime(Timer timer) {
47    int idx = (fCurrentMeasurement + (kMeasurementCount - 1)) & (kMeasurementCount - 1);
48    return fTimers[timer].fTimes[idx];
49}
50
51void StatsLayer::onPaint(SkCanvas* canvas) {
52    // Advance our timing bookkeeping
53    for (int i = 0; i < fTimers.count(); ++i) {
54        fCumulativeMeasurementTime += fTimers[i].fTimes[fCurrentMeasurement];
55    }
56    fCumulativeMeasurementCount++;
57    fCurrentMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1);
58    SkASSERT(fCurrentMeasurement < kMeasurementCount);
59    for (int i = 0; i < fTimers.count(); ++i) {
60        fTimers[i].fTimes[fCurrentMeasurement] = 0;
61    }
62
63    // Now draw everything
64    static const float kPixelPerMS = 2.0f;
65    static const int kDisplayWidth = 192;
66    static const int kGraphHeight = 100;
67    static const int kTextHeight = 60;
68    static const int kDisplayHeight = kGraphHeight + kTextHeight;
69    static const int kDisplayPadding = 10;
70    static const int kGraphPadding = 3;
71    static const SkScalar kBaseMS = 1000.f / 60.f;  // ms/frame to hit 60 fps
72
73    SkISize canvasSize = canvas->getBaseLayerSize();
74    SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
75                                   SkIntToScalar(kDisplayPadding),
76                                   SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
77    SkPaint paint;
78    canvas->save();
79
80    paint.setColor(SK_ColorBLACK);
81    canvas->drawRect(rect, paint);
82    // draw the 16ms line
83    paint.setColor(SK_ColorLTGRAY);
84    canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
85                     rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
86    paint.setColor(SK_ColorRED);
87    paint.setStyle(SkPaint::kStroke_Style);
88    canvas->drawRect(rect, paint);
89    paint.setStyle(SkPaint::kFill_Style);
90
91    int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
92    const int xStep = 3;
93    int i = fCurrentMeasurement;
94    double ms = 0;
95    SkTDArray<double> sumTimes;
96    sumTimes.setCount(fTimers.count());
97    memset(sumTimes.begin(), 0, sumTimes.count() * sizeof(double));
98    int count = 0;
99    do {
100        int startY = SkScalarTruncToInt(rect.fBottom);
101        double inc = 0;
102        for (int timer = 0; timer < fTimers.count(); ++timer) {
103            int height = (int)(fTimers[timer].fTimes[i] * kPixelPerMS + 0.5);
104            int endY = SkTMax(startY - height, kDisplayPadding + kTextHeight);
105            paint.setColor(fTimers[timer].fColor);
106            canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
107                             SkIntToScalar(x), SkIntToScalar(endY), paint);
108            startY = endY;
109            inc += fTimers[timer].fTimes[i];
110            sumTimes[timer] += fTimers[timer].fTimes[i];
111        }
112
113        if (inc > 0) {
114            ms += inc;
115            ++count;
116        }
117
118        i++;
119        i &= (kMeasurementCount - 1);  // fast mod
120        x += xStep;
121    } while (i != fCurrentMeasurement);
122
123    paint.setTextSize(16);
124    SkString mainString;
125    mainString.appendf("%4.3f ms -> %4.3f ms", ms / SkTMax(1, count),
126                  fCumulativeMeasurementTime / SkTMax(1, fCumulativeMeasurementCount));
127    paint.setColor(SK_ColorWHITE);
128    canvas->drawString(mainString.c_str(), rect.fLeft + 3, rect.fTop + 14, paint);
129
130    for (int timer = 0; timer < fTimers.count(); ++timer) {
131        SkString str;
132        str.appendf("%s: %4.3f ms", fTimers[timer].fLabel.c_str(),
133                    sumTimes[timer] / SkTMax(1, count));
134        paint.setColor(fTimers[timer].fLabelColor);
135        canvas->drawString(str, rect.fLeft + 3, rect.fTop + 28 + (14 * timer), paint);
136    }
137
138    canvas->restore();
139}
140