1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci* Copyright 2017 Google Inc.
3cb93a386Sopenharmony_ci*
4cb93a386Sopenharmony_ci* Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci* found in the LICENSE file.
6cb93a386Sopenharmony_ci*/
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "tools/viewer/StatsLayer.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h"
11cb93a386Sopenharmony_ci#include "include/core/SkFont.h"
12cb93a386Sopenharmony_ci#include "include/core/SkString.h"
13cb93a386Sopenharmony_ci#include "include/core/SkSurface.h"
14cb93a386Sopenharmony_ci#include "include/core/SkTime.h"
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ciStatsLayer::StatsLayer()
17cb93a386Sopenharmony_ci    : fCurrentMeasurement(-1)
18cb93a386Sopenharmony_ci    , fLastTotalBegin(0)
19cb93a386Sopenharmony_ci    , fCumulativeMeasurementTime(0)
20cb93a386Sopenharmony_ci    , fCumulativeMeasurementCount(0)
21cb93a386Sopenharmony_ci    , fDisplayScale(1.0f) {
22cb93a386Sopenharmony_ci    memset(fTotalTimes, 0, sizeof(fTotalTimes));
23cb93a386Sopenharmony_ci}
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_civoid StatsLayer::resetMeasurements() {
26cb93a386Sopenharmony_ci    for (int i = 0; i < fTimers.count(); ++i) {
27cb93a386Sopenharmony_ci        memset(fTimers[i].fTimes, 0, sizeof(fTimers[i].fTimes));
28cb93a386Sopenharmony_ci    }
29cb93a386Sopenharmony_ci    memset(fTotalTimes, 0, sizeof(fTotalTimes));
30cb93a386Sopenharmony_ci    fCurrentMeasurement = -1;
31cb93a386Sopenharmony_ci    fLastTotalBegin = 0;
32cb93a386Sopenharmony_ci    fCumulativeMeasurementTime = 0;
33cb93a386Sopenharmony_ci    fCumulativeMeasurementCount = 0;
34cb93a386Sopenharmony_ci}
35cb93a386Sopenharmony_ci
36cb93a386Sopenharmony_ciStatsLayer::Timer StatsLayer::addTimer(const char* label, SkColor color, SkColor labelColor) {
37cb93a386Sopenharmony_ci    Timer newTimer = fTimers.count();
38cb93a386Sopenharmony_ci    TimerData& newData = fTimers.push_back();
39cb93a386Sopenharmony_ci    memset(newData.fTimes, 0, sizeof(newData.fTimes));
40cb93a386Sopenharmony_ci    newData.fLabel = label;
41cb93a386Sopenharmony_ci    newData.fColor = color;
42cb93a386Sopenharmony_ci    newData.fLabelColor = labelColor ? labelColor : color;
43cb93a386Sopenharmony_ci    return newTimer;
44cb93a386Sopenharmony_ci}
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_civoid StatsLayer::beginTiming(Timer timer) {
47cb93a386Sopenharmony_ci    if (fCurrentMeasurement >= 0) {
48cb93a386Sopenharmony_ci        fTimers[timer].fTimes[fCurrentMeasurement] -= SkTime::GetMSecs();
49cb93a386Sopenharmony_ci    }
50cb93a386Sopenharmony_ci}
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_civoid StatsLayer::endTiming(Timer timer) {
53cb93a386Sopenharmony_ci    if (fCurrentMeasurement >= 0) {
54cb93a386Sopenharmony_ci        fTimers[timer].fTimes[fCurrentMeasurement] += SkTime::GetMSecs();
55cb93a386Sopenharmony_ci    }
56cb93a386Sopenharmony_ci}
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_civoid StatsLayer::onPrePaint() {
59cb93a386Sopenharmony_ci    if (fCurrentMeasurement >= 0) {
60cb93a386Sopenharmony_ci        fTotalTimes[fCurrentMeasurement] = SkTime::GetMSecs() - fLastTotalBegin;
61cb93a386Sopenharmony_ci        fCumulativeMeasurementTime += fTotalTimes[fCurrentMeasurement];
62cb93a386Sopenharmony_ci        fCumulativeMeasurementCount++;
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci    fCurrentMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1);
65cb93a386Sopenharmony_ci    SkASSERT(fCurrentMeasurement >= 0 && fCurrentMeasurement < kMeasurementCount);
66cb93a386Sopenharmony_ci    fLastTotalBegin = SkTime::GetMSecs();
67cb93a386Sopenharmony_ci}
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_civoid StatsLayer::onPaint(SkSurface* surface) {
70cb93a386Sopenharmony_ci    int nextMeasurement = (fCurrentMeasurement + 1) & (kMeasurementCount - 1);
71cb93a386Sopenharmony_ci    for (int i = 0; i < fTimers.count(); ++i) {
72cb93a386Sopenharmony_ci        fTimers[i].fTimes[nextMeasurement] = 0;
73cb93a386Sopenharmony_ci    }
74cb93a386Sopenharmony_ci
75cb93a386Sopenharmony_ci#ifdef SK_BUILD_FOR_ANDROID
76cb93a386Sopenharmony_ci    // Scale up the stats overlay on Android devices
77cb93a386Sopenharmony_ci    static constexpr SkScalar kScale = 1.5;
78cb93a386Sopenharmony_ci#else
79cb93a386Sopenharmony_ci    SkScalar kScale = fDisplayScale;
80cb93a386Sopenharmony_ci#endif
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci    // Now draw everything
83cb93a386Sopenharmony_ci    static const float kPixelPerMS = 2.0f;
84cb93a386Sopenharmony_ci    static const int kDisplayWidth = 192;
85cb93a386Sopenharmony_ci    static const int kGraphHeight = 100;
86cb93a386Sopenharmony_ci    static const int kTextHeight = 60;
87cb93a386Sopenharmony_ci    static const int kDisplayHeight = kGraphHeight + kTextHeight;
88cb93a386Sopenharmony_ci    static const int kDisplayPadding = 10;
89cb93a386Sopenharmony_ci    static const int kGraphPadding = 3;
90cb93a386Sopenharmony_ci    static const SkScalar kBaseMS = 1000.f / 60.f;  // ms/frame to hit 60 fps
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci    auto canvas = surface->getCanvas();
93cb93a386Sopenharmony_ci    SkISize canvasSize = canvas->getBaseLayerSize();
94cb93a386Sopenharmony_ci    SkRect rect = SkRect::MakeXYWH(SkIntToScalar(canvasSize.fWidth-kDisplayWidth-kDisplayPadding),
95cb93a386Sopenharmony_ci                                   SkIntToScalar(kDisplayPadding),
96cb93a386Sopenharmony_ci                                   SkIntToScalar(kDisplayWidth), SkIntToScalar(kDisplayHeight));
97cb93a386Sopenharmony_ci    SkPaint paint;
98cb93a386Sopenharmony_ci    canvas->save();
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci    // Scale the canvas while keeping the right edge in place.
101cb93a386Sopenharmony_ci    canvas->concat(SkMatrix::RectToRect(SkRect::Make(canvasSize),
102cb93a386Sopenharmony_ci                                        SkRect::MakeXYWH(canvasSize.width()  * (1 - kScale),
103cb93a386Sopenharmony_ci                                                         0,
104cb93a386Sopenharmony_ci                                                         canvasSize.width()  * kScale,
105cb93a386Sopenharmony_ci                                                         canvasSize.height() * kScale)));
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ci    paint.setColor(SK_ColorBLACK);
108cb93a386Sopenharmony_ci    canvas->drawRect(rect, paint);
109cb93a386Sopenharmony_ci    // draw the 16ms line
110cb93a386Sopenharmony_ci    paint.setColor(SK_ColorLTGRAY);
111cb93a386Sopenharmony_ci    canvas->drawLine(rect.fLeft, rect.fBottom - kBaseMS*kPixelPerMS,
112cb93a386Sopenharmony_ci                     rect.fRight, rect.fBottom - kBaseMS*kPixelPerMS, paint);
113cb93a386Sopenharmony_ci    paint.setColor(SK_ColorRED);
114cb93a386Sopenharmony_ci    paint.setStyle(SkPaint::kStroke_Style);
115cb93a386Sopenharmony_ci    canvas->drawRect(rect, paint);
116cb93a386Sopenharmony_ci    paint.setStyle(SkPaint::kFill_Style);
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    int x = SkScalarTruncToInt(rect.fLeft) + kGraphPadding;
119cb93a386Sopenharmony_ci    const int xStep = 3;
120cb93a386Sopenharmony_ci    int i = nextMeasurement;
121cb93a386Sopenharmony_ci    SkTDArray<double> sumTimes;
122cb93a386Sopenharmony_ci    sumTimes.setCount(fTimers.count());
123cb93a386Sopenharmony_ci    memset(sumTimes.begin(), 0, sumTimes.count() * sizeof(double));
124cb93a386Sopenharmony_ci    int count = 0;
125cb93a386Sopenharmony_ci    double totalTime = 0;
126cb93a386Sopenharmony_ci    int totalCount = 0;
127cb93a386Sopenharmony_ci    do {
128cb93a386Sopenharmony_ci        int startY = SkScalarTruncToInt(rect.fBottom);
129cb93a386Sopenharmony_ci        double inc = 0;
130cb93a386Sopenharmony_ci        for (int timer = 0; timer < fTimers.count(); ++timer) {
131cb93a386Sopenharmony_ci            int height = (int)(fTimers[timer].fTimes[i] * kPixelPerMS + 0.5);
132cb93a386Sopenharmony_ci            int endY = std::max(startY - height, kDisplayPadding + kTextHeight);
133cb93a386Sopenharmony_ci            paint.setColor(fTimers[timer].fColor);
134cb93a386Sopenharmony_ci            canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
135cb93a386Sopenharmony_ci                             SkIntToScalar(x), SkIntToScalar(endY), paint);
136cb93a386Sopenharmony_ci            startY = endY;
137cb93a386Sopenharmony_ci            inc += fTimers[timer].fTimes[i];
138cb93a386Sopenharmony_ci            sumTimes[timer] += fTimers[timer].fTimes[i];
139cb93a386Sopenharmony_ci        }
140cb93a386Sopenharmony_ci
141cb93a386Sopenharmony_ci        int height = (int)(fTotalTimes[i] * kPixelPerMS + 0.5);
142cb93a386Sopenharmony_ci        height = std::max(0, height - (SkScalarTruncToInt(rect.fBottom) - startY));
143cb93a386Sopenharmony_ci        int endY = std::max(startY - height, kDisplayPadding + kTextHeight);
144cb93a386Sopenharmony_ci        paint.setColor(SK_ColorWHITE);
145cb93a386Sopenharmony_ci        canvas->drawLine(SkIntToScalar(x), SkIntToScalar(startY),
146cb93a386Sopenharmony_ci                         SkIntToScalar(x), SkIntToScalar(endY), paint);
147cb93a386Sopenharmony_ci        totalTime += fTotalTimes[i];
148cb93a386Sopenharmony_ci        if (fTotalTimes[i] > 0) {
149cb93a386Sopenharmony_ci            ++totalCount;
150cb93a386Sopenharmony_ci        }
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ci        if (inc > 0) {
153cb93a386Sopenharmony_ci            ++count;
154cb93a386Sopenharmony_ci        }
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci        i++;
157cb93a386Sopenharmony_ci        i &= (kMeasurementCount - 1);  // fast mod
158cb93a386Sopenharmony_ci        x += xStep;
159cb93a386Sopenharmony_ci    } while (i != nextMeasurement);
160cb93a386Sopenharmony_ci
161cb93a386Sopenharmony_ci    SkFont font(nullptr, 16);
162cb93a386Sopenharmony_ci    paint.setColor(SK_ColorWHITE);
163cb93a386Sopenharmony_ci    double time = totalTime / std::max(1, totalCount);
164cb93a386Sopenharmony_ci    double measure = fCumulativeMeasurementTime / std::max(1, fCumulativeMeasurementCount);
165cb93a386Sopenharmony_ci    canvas->drawString(SkStringPrintf("%4.3f ms -> %4.3f ms", time, measure),
166cb93a386Sopenharmony_ci                       rect.fLeft + 3, rect.fTop + 14, font, paint);
167cb93a386Sopenharmony_ci
168cb93a386Sopenharmony_ci    for (int timer = 0; timer < fTimers.count(); ++timer) {
169cb93a386Sopenharmony_ci        paint.setColor(fTimers[timer].fLabelColor);
170cb93a386Sopenharmony_ci        canvas->drawString(SkStringPrintf("%s: %4.3f ms", fTimers[timer].fLabel.c_str(),
171cb93a386Sopenharmony_ci                                          sumTimes[timer] / std::max(1, count)),
172cb93a386Sopenharmony_ci                           rect.fLeft + 3, rect.fTop + 28 + (14 * timer), font, paint);
173cb93a386Sopenharmony_ci    }
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci    canvas->restore();
176cb93a386Sopenharmony_ci}
177