blob: 72545050fe0d55832347c19417f5af83088fa878 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flow/instrumentation.h"
#include <algorithm>
#include "third_party/skia/include/core/SkPath.h"
namespace flow {
namespace instrumentation {
static const size_t kMaxSamples = 120;
static const size_t kMaxFrameMarkers = 8;
Stopwatch::Stopwatch() : _start(base::TimeTicks::Now()), _current_sample(0) {
const base::TimeDelta delta;
_laps.resize(kMaxSamples, delta);
}
void Stopwatch::start() {
_start = base::TimeTicks::Now();
_current_sample = (_current_sample + 1) % kMaxSamples;
}
void Stopwatch::stop() {
_laps[_current_sample] = base::TimeTicks::Now() - _start;
}
void Stopwatch::setLapTime(const base::TimeDelta& delta) {
_current_sample = (_current_sample + 1) % kMaxSamples;
_laps[_current_sample] = delta;
}
const base::TimeDelta& Stopwatch::lastLap() const {
return _laps[(_current_sample - 1) % kMaxSamples];
}
static inline constexpr double UnitFrameInterval(double frameTimeMS) {
return frameTimeMS * 60.0 * 1e-3;
}
static inline double UnitHeight(double frameTimeMS, double maxUnitInterval) {
double unitHeight = UnitFrameInterval(frameTimeMS) / maxUnitInterval;
if (unitHeight > 1.0)
unitHeight = 1.0;
return unitHeight;
}
base::TimeDelta Stopwatch::maxDelta() const {
base::TimeDelta maxDelta;
for (size_t i = 0; i < kMaxSamples; i++) {
if (_laps[i] > maxDelta) {
maxDelta = _laps[i];
}
}
return maxDelta;
}
void Stopwatch::visualize(SkCanvas& canvas, const SkRect& rect) const {
SkPaint paint;
// Paint the background.
paint.setColor(0x99FFFFFF);
canvas.drawRect(rect, paint);
// Establish the graph position.
const SkScalar x = rect.x();
const SkScalar y = rect.y();
const SkScalar width = rect.width();
const SkScalar height = rect.height();
const SkScalar bottom = y + height;
const SkScalar right = x + width;
// Scale the graph to show frame times up to those that are 4 times the frame time.
const double maxInterval = kOneFrameMS * 3.0;
const double maxUnitInterval = UnitFrameInterval(maxInterval);
// Prepare a path for the data.
// we start at the height of the last point, so it looks like we wrap around
SkPath path;
const double sampleUnitWidth = (1.0 / kMaxSamples);
const double sampleMarginUnitWidth = sampleUnitWidth / 6.0;
const double sampleMarginWidth = width * sampleMarginUnitWidth;
path.moveTo(x, bottom);
path.lineTo(x, y + height * (1.0 - UnitHeight(_laps[0].InMillisecondsF(),
maxUnitInterval)));
double unitX;
double unitNextX = 0.0;
for (size_t i = 0; i < kMaxSamples; i += 1) {
unitX = unitNextX;
unitNextX = (static_cast<double>(i + 1) / kMaxSamples);
const double sampleY = y + height * (1.0 - UnitHeight(_laps[i].InMillisecondsF(),
maxUnitInterval));
path.lineTo(x + width * unitX + sampleMarginWidth, sampleY);
path.lineTo(x + width * unitNextX - sampleMarginWidth, sampleY);
}
path.lineTo(right, y + height * (1.0 - UnitHeight(_laps[kMaxSamples - 1].InMillisecondsF(),
maxUnitInterval)));
path.lineTo(right, bottom);
path.close();
// Draw the graph.
paint.setColor(0xAA0000FF);
canvas.drawPath(path, paint);
// Draw horizontal markers.
paint.setStrokeWidth(0); // hairline
paint.setStyle(SkPaint::Style::kStroke_Style);
paint.setColor(0xCC000000);
if (maxInterval > kOneFrameMS) {
// Paint the horizontal markers
size_t frameMarkerCount = static_cast<size_t>(maxInterval / kOneFrameMS);
// Limit the number of markers displayed. After a certain point, the graph
// becomes crowded
if (frameMarkerCount > kMaxFrameMarkers) {
frameMarkerCount = 1;
}
for (size_t frameIndex = 0; frameIndex < frameMarkerCount; frameIndex++) {
const double frameHeight =
height * (1.0 - (UnitFrameInterval((frameIndex + 1) * kOneFrameMS) /
maxUnitInterval));
canvas.drawLine(x, y + frameHeight, right, y + frameHeight, paint);
}
}
// Paint the vertical marker for the current frame.
// We paint it over the current frame, not after it, because when we
// paint this we don't yet have all the times for the current frame.
paint.setStyle(SkPaint::Style::kFill_Style);
if (UnitFrameInterval(lastLap().InMillisecondsF()) > 1.0) {
// budget exceeded
paint.setColor(SK_ColorRED);
} else {
// within budget
paint.setColor(SK_ColorGREEN);
}
double sampleX = x + width * (static_cast<double>(_current_sample) / kMaxSamples)
- sampleMarginWidth;
canvas.drawRectCoords(sampleX, y, sampleX + width * sampleUnitWidth + sampleMarginWidth * 2, bottom, paint);
}
Stopwatch::~Stopwatch() = default;
} // namespace instrumentation
} // namespace flow