Refactor `Stopwatch` into `StopwatchVisualizer` and `SkStopwatchVisualizer` (#45200)
Partial work towards https://github.com/flutter/flutter/issues/126009.
Still working as before:
<img width="1210" alt="Screenshot 2023-08-28 at 6 32 45 PM"
src="https://github.com/flutter/engine/assets/168174/38728015-d0d4-4933-bd31-d2326c76aeee">
(There are some `PerformanceOverlayLayerDefault.*` tests that don't run
locally, so I guess I'll let CI run those)
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 11251d2..3ae421e 100644
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -846,6 +846,8 @@
ORIGIN: ../../../flutter/flow/skia_gpu_object.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/flow/stopwatch.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/flow/stopwatch.h + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/flow/stopwatch_sk.cc + ../../../flutter/LICENSE
+ORIGIN: ../../../flutter/flow/stopwatch_sk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/flow/surface.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/flow/surface.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/flow/surface_frame.cc + ../../../flutter/LICENSE
@@ -3592,6 +3594,8 @@
FILE: ../../../flutter/flow/skia_gpu_object.h
FILE: ../../../flutter/flow/stopwatch.cc
FILE: ../../../flutter/flow/stopwatch.h
+FILE: ../../../flutter/flow/stopwatch_sk.cc
+FILE: ../../../flutter/flow/stopwatch_sk.h
FILE: ../../../flutter/flow/surface.cc
FILE: ../../../flutter/flow/surface.h
FILE: ../../../flutter/flow/surface_frame.cc
diff --git a/flow/BUILD.gn b/flow/BUILD.gn
index f52d325..5d69ae2 100644
--- a/flow/BUILD.gn
+++ b/flow/BUILD.gn
@@ -77,6 +77,8 @@
"skia_gpu_object.h",
"stopwatch.cc",
"stopwatch.h",
+ "stopwatch_sk.cc",
+ "stopwatch_sk.h",
"surface.cc",
"surface.h",
"surface_frame.cc",
diff --git a/flow/layers/performance_overlay_layer.cc b/flow/layers/performance_overlay_layer.cc
index 2b693b2..4275a99 100644
--- a/flow/layers/performance_overlay_layer.cc
+++ b/flow/layers/performance_overlay_layer.cc
@@ -8,6 +8,7 @@
#include <iostream>
#include <string>
+#include "flow/stopwatch_sk.h"
#include "third_party/skia/include/core/SkFont.h"
#include "third_party/skia/include/core/SkTextBlob.h"
@@ -29,7 +30,11 @@
if (show_graph) {
SkRect visualization_rect = SkRect::MakeXYWH(x, y, width, height);
- stopwatch.Visualize(canvas, visualization_rect);
+
+ // TODO(matanlurey): Select a visualizer based on the current backend.
+ // https://github.com/flutter/flutter/issues/126009
+ SkStopwatchVisualizer visualizer = SkStopwatchVisualizer(stopwatch);
+ visualizer.Visualize(canvas, visualization_rect);
}
if (show_labels) {
diff --git a/flow/stopwatch.cc b/flow/stopwatch.cc
index 4d28f37..b8e8d30 100644
--- a/flow/stopwatch.cc
+++ b/flow/stopwatch.cc
@@ -4,14 +4,9 @@
#include "flutter/flow/stopwatch.h"
-#include "include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkPath.h"
-#include "third_party/skia/include/core/SkSurface.h"
-
namespace flutter {
static const size_t kMaxSamples = 120;
-static const size_t kMaxFrameMarkers = 8;
Stopwatch::Stopwatch(const RefreshRateUpdater& updater)
: refresh_rate_updater_(updater),
@@ -19,8 +14,6 @@
current_sample_(0) {
const fml::TimeDelta delta = fml::TimeDelta::Zero();
laps_.resize(kMaxSamples, delta);
- cache_dirty_ = true;
- prev_drawn_sample_index_ = 0;
}
Stopwatch::~Stopwatch() = default;
@@ -51,12 +44,20 @@
return laps_[(current_sample_ - 1) % kMaxSamples];
}
-double Stopwatch::UnitFrameInterval(double raster_time_ms) const {
- return raster_time_ms / GetFrameBudget().count();
+const fml::TimeDelta& Stopwatch::GetLap(size_t index) const {
+ return laps_[index];
}
-double Stopwatch::UnitHeight(double raster_time_ms,
- double max_unit_interval) const {
+size_t Stopwatch::GetCurrentSample() const {
+ return current_sample_;
+}
+
+double StopwatchVisualizer::UnitFrameInterval(double raster_time_ms) const {
+ return raster_time_ms / stopwatch_.GetFrameBudget().count();
+}
+
+double StopwatchVisualizer::UnitHeight(double raster_time_ms,
+ double max_unit_interval) const {
double unit_height = UnitFrameInterval(raster_time_ms) / max_unit_interval;
if (unit_height > 1.0) {
unit_height = 1.0;
@@ -82,169 +83,6 @@
return sum / kMaxSamples;
}
-// Initialize the SkSurface for drawing into. Draws the base background and any
-// timing data from before the initial Visualize() call.
-void Stopwatch::InitVisualizeSurface(SkISize size) const {
- // Mark as dirty if the size has changed.
- if (visualize_cache_surface_) {
- if (size.width() != visualize_cache_surface_->width() ||
- size.height() != visualize_cache_surface_->height()) {
- cache_dirty_ = true;
- };
- }
-
- if (!cache_dirty_) {
- return;
- }
- cache_dirty_ = false;
-
- // TODO(garyq): Use a GPU surface instead of a CPU surface.
- visualize_cache_surface_ =
- SkSurfaces::Raster(SkImageInfo::MakeN32Premul(size));
-
- SkCanvas* cache_canvas = visualize_cache_surface_->getCanvas();
-
- // Establish the graph position.
- const SkScalar x = 0;
- const SkScalar y = 0;
- const SkScalar width = size.width();
- const SkScalar height = size.height();
-
- SkPaint paint;
- paint.setColor(0x99FFFFFF);
- cache_canvas->drawRect(SkRect::MakeXYWH(x, y, width, height), paint);
-
- // Scale the graph to show frame times up to those that are 3 times the frame
- // time.
- const double one_frame_ms = GetFrameBudget().count();
- const double max_interval = one_frame_ms * 3.0;
- const double max_unit_interval = UnitFrameInterval(max_interval);
-
- // Draw the old data to initially populate the graph.
- // Prepare a path for the data. We start at the height of the last point, so
- // it looks like we wrap around
- SkPath path;
- path.setIsVolatile(true);
- path.moveTo(x, height);
- path.lineTo(x, y + height * (1.0 - UnitHeight(laps_[0].ToMillisecondsF(),
- max_unit_interval)));
- double unit_x;
- double unit_next_x = 0.0;
- for (size_t i = 0; i < kMaxSamples; i += 1) {
- unit_x = unit_next_x;
- unit_next_x = (static_cast<double>(i + 1) / kMaxSamples);
- const double sample_y =
- y + height * (1.0 - UnitHeight(laps_[i].ToMillisecondsF(),
- max_unit_interval));
- path.lineTo(x + width * unit_x, sample_y);
- path.lineTo(x + width * unit_next_x, sample_y);
- }
- path.lineTo(
- width,
- y + height * (1.0 - UnitHeight(laps_[kMaxSamples - 1].ToMillisecondsF(),
- max_unit_interval)));
- path.lineTo(width, height);
- path.close();
-
- // Draw the graph.
- paint.setColor(0xAA0000FF);
- cache_canvas->drawPath(path, paint);
-}
-
-void Stopwatch::Visualize(DlCanvas* canvas, const SkRect& rect) const {
- // Initialize visualize cache if it has not yet been initialized.
- InitVisualizeSurface(SkISize::Make(rect.width(), rect.height()));
-
- SkCanvas* cache_canvas = visualize_cache_surface_->getCanvas();
- SkPaint paint;
-
- // Establish the graph position.
- const SkScalar x = 0;
- const SkScalar y = 0;
- const SkScalar width = visualize_cache_surface_->width();
- const SkScalar height = visualize_cache_surface_->height();
-
- // Scale the graph to show frame times up to those that are 3 times the frame
- // time.
- const double one_frame_ms = GetFrameBudget().count();
- const double max_interval = one_frame_ms * 3.0;
- const double max_unit_interval = UnitFrameInterval(max_interval);
-
- const double sample_unit_width = (1.0 / kMaxSamples);
-
- // Draw vertical replacement bar to erase old/stale pixels.
- paint.setColor(0x99FFFFFF);
- paint.setStyle(SkPaint::Style::kFill_Style);
- paint.setBlendMode(SkBlendMode::kSrc);
- double sample_x =
- x + width * (static_cast<double>(prev_drawn_sample_index_) / kMaxSamples);
- const auto eraser_rect = SkRect::MakeLTRB(
- sample_x, y, sample_x + width * sample_unit_width, height);
- cache_canvas->drawRect(eraser_rect, paint);
-
- // Draws blue timing bar for new data.
- paint.setColor(0xAA0000FF);
- paint.setBlendMode(SkBlendMode::kSrcOver);
- const auto bar_rect = SkRect::MakeLTRB(
- sample_x,
- y + height * (1.0 -
- UnitHeight(laps_[current_sample_ == 0 ? kMaxSamples - 1
- : current_sample_ - 1]
- .ToMillisecondsF(),
- max_unit_interval)),
- sample_x + width * sample_unit_width, height);
- cache_canvas->drawRect(bar_rect, paint);
-
- // Draw horizontal frame markers.
- paint.setStrokeWidth(0); // hairline
- paint.setStyle(SkPaint::Style::kStroke_Style);
- paint.setColor(0xCC000000);
-
- if (max_interval > one_frame_ms) {
- // Paint the horizontal markers
- size_t frame_marker_count =
- static_cast<size_t>(max_interval / one_frame_ms);
-
- // Limit the number of markers displayed. After a certain point, the graph
- // becomes crowded
- if (frame_marker_count > kMaxFrameMarkers) {
- frame_marker_count = 1;
- }
-
- for (size_t frame_index = 0; frame_index < frame_marker_count;
- frame_index++) {
- const double frame_height =
- height * (1.0 - (UnitFrameInterval((frame_index + 1) * one_frame_ms) /
- max_unit_interval));
- cache_canvas->drawLine(x, y + frame_height, width, y + frame_height,
- 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);
- paint.setBlendMode(SkBlendMode::kSrcOver);
- if (UnitFrameInterval(LastLap().ToMillisecondsF()) > 1.0) {
- // budget exceeded
- paint.setColor(SK_ColorRED);
- } else {
- // within budget
- paint.setColor(SK_ColorGREEN);
- }
- sample_x = x + width * (static_cast<double>(current_sample_) / kMaxSamples);
- const auto marker_rect = SkRect::MakeLTRB(
- sample_x, y, sample_x + width * sample_unit_width, height);
- cache_canvas->drawRect(marker_rect, paint);
- prev_drawn_sample_index_ = current_sample_;
-
- // Draw the cached surface onto the output canvas.
- auto image = DlImage::Make(visualize_cache_surface_->makeImageSnapshot());
- canvas->DrawImage(image, {rect.x(), rect.y()},
- DlImageSampling::kNearestNeighbor);
-}
-
fml::Milliseconds Stopwatch::GetFrameBudget() const {
return refresh_rate_updater_.GetFrameBudget();
}
diff --git a/flow/stopwatch.h b/flow/stopwatch.h
index 005697d..d6e8f0d 100644
--- a/flow/stopwatch.h
+++ b/flow/stopwatch.h
@@ -12,8 +12,6 @@
#include "flutter/fml/time/time_delta.h"
#include "flutter/fml/time/time_point.h"
-#include "third_party/skia/include/core/SkSurface.h"
-
namespace flutter {
class Stopwatch {
@@ -32,16 +30,16 @@
~Stopwatch();
+ const fml::TimeDelta& GetLap(size_t index) const;
+
+ size_t GetCurrentSample() const;
+
const fml::TimeDelta& LastLap() const;
fml::TimeDelta MaxDelta() const;
fml::TimeDelta AverageDelta() const;
- void InitVisualizeSurface(SkISize size) const;
-
- void Visualize(DlCanvas* canvas, const SkRect& rect) const;
-
void Start();
void Stop();
@@ -52,20 +50,11 @@
fml::Milliseconds GetFrameBudget() const;
private:
- inline double UnitFrameInterval(double time_ms) const;
- inline double UnitHeight(double time_ms, double max_height) const;
-
const RefreshRateUpdater& refresh_rate_updater_;
fml::TimePoint start_;
std::vector<fml::TimeDelta> laps_;
size_t current_sample_;
- // Mutable data cache for performance optimization of the graphs. Prevents
- // expensive redrawing of old data.
- mutable bool cache_dirty_;
- mutable sk_sp<SkSurface> visualize_cache_surface_;
- mutable size_t prev_drawn_sample_index_;
-
FML_DISALLOW_COPY_AND_ASSIGN(Stopwatch);
};
@@ -91,6 +80,38 @@
FixedRefreshRateUpdater fixed_delegate_;
};
+//------------------------------------------------------------------------------
+/// @brief Abstract class for visualizing (i.e. drawing) a stopwatch.
+///
+/// @note This was originally folded into the |Stopwatch| class, but
+/// was separated out to make it easier to change the underlying
+/// implementation (which relied directly on Skia, not showing on
+/// Impeller: https://github.com/flutter/flutter/issues/126009).
+class StopwatchVisualizer {
+ public:
+ explicit StopwatchVisualizer(const Stopwatch& stopwatch)
+ : stopwatch_(stopwatch) {}
+
+ virtual ~StopwatchVisualizer() = default;
+
+ /// @brief Renders the stopwatch as a graph.
+ ///
+ /// @param canvas The canvas to draw on.
+ /// @param[in] rect The rectangle to draw in.
+ virtual void Visualize(DlCanvas* canvas, const SkRect& rect) const = 0;
+
+ FML_DISALLOW_COPY_AND_ASSIGN(StopwatchVisualizer);
+
+ protected:
+ /// @brief Converts a raster time to a unit interval.
+ double UnitFrameInterval(double time_ms) const;
+
+ /// @brief Converts a raster time to a unit height.
+ double UnitHeight(double time_ms, double max_height) const;
+
+ const Stopwatch& stopwatch_;
+};
+
} // namespace flutter
#endif // FLUTTER_FLOW_INSTRUMENTATION_H_
diff --git a/flow/stopwatch_sk.cc b/flow/stopwatch_sk.cc
new file mode 100644
index 0000000..16a3411
--- /dev/null
+++ b/flow/stopwatch_sk.cc
@@ -0,0 +1,187 @@
+// Copyright 2013 The Flutter 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 "flutter/flow/stopwatch_sk.h"
+#include "include/core/SkCanvas.h"
+#include "include/core/SkImageInfo.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkSize.h"
+#include "include/core/SkSurface.h"
+
+namespace flutter {
+
+static const size_t kMaxSamples = 120;
+static const size_t kMaxFrameMarkers = 8;
+
+void SkStopwatchVisualizer::InitVisualizeSurface(SkISize size) const {
+ // Mark as dirty if the size has changed.
+ if (visualize_cache_surface_) {
+ if (size.width() != visualize_cache_surface_->width() ||
+ size.height() != visualize_cache_surface_->height()) {
+ cache_dirty_ = true;
+ };
+ }
+
+ if (!cache_dirty_) {
+ return;
+ }
+ cache_dirty_ = false;
+
+ // TODO(garyq): Use a GPU surface instead of a CPU surface.
+ visualize_cache_surface_ =
+ SkSurfaces::Raster(SkImageInfo::MakeN32Premul(size));
+
+ SkCanvas* cache_canvas = visualize_cache_surface_->getCanvas();
+
+ // Establish the graph position.
+ const SkScalar x = 0;
+ const SkScalar y = 0;
+ const SkScalar width = size.width();
+ const SkScalar height = size.height();
+
+ SkPaint paint;
+ paint.setColor(0x99FFFFFF);
+ cache_canvas->drawRect(SkRect::MakeXYWH(x, y, width, height), paint);
+
+ // Scale the graph to show frame times up to those that are 3 times the frame
+ // time.
+ const double one_frame_ms = stopwatch_.GetFrameBudget().count();
+ const double max_interval = one_frame_ms * 3.0;
+ const double max_unit_interval = UnitFrameInterval(max_interval);
+
+ // Draw the old data to initially populate the graph.
+ // Prepare a path for the data. We start at the height of the last point, so
+ // it looks like we wrap around
+ SkPath path;
+ path.setIsVolatile(true);
+ path.moveTo(x, height);
+ path.lineTo(
+ x, y + height * (1.0 - UnitHeight(stopwatch_.GetLap(0).ToMillisecondsF(),
+ max_unit_interval)));
+ double unit_x;
+ double unit_next_x = 0.0;
+ for (size_t i = 0; i < kMaxSamples; i += 1) {
+ unit_x = unit_next_x;
+ unit_next_x = (static_cast<double>(i + 1) / kMaxSamples);
+ const double sample_y =
+ y + height * (1.0 - UnitHeight(stopwatch_.GetLap(i).ToMillisecondsF(),
+ max_unit_interval));
+ path.lineTo(x + width * unit_x, sample_y);
+ path.lineTo(x + width * unit_next_x, sample_y);
+ }
+ path.lineTo(
+ width,
+ y + height *
+ (1.0 -
+ UnitHeight(stopwatch_.GetLap(kMaxSamples - 1).ToMillisecondsF(),
+ max_unit_interval)));
+ path.lineTo(width, height);
+ path.close();
+
+ // Draw the graph.
+ paint.setColor(0xAA0000FF);
+ cache_canvas->drawPath(path, paint);
+}
+
+void SkStopwatchVisualizer::Visualize(DlCanvas* canvas,
+ const SkRect& rect) const {
+ // Initialize visualize cache if it has not yet been initialized.
+ InitVisualizeSurface(SkISize::Make(rect.width(), rect.height()));
+
+ SkCanvas* cache_canvas = visualize_cache_surface_->getCanvas();
+ SkPaint paint;
+
+ // Establish the graph position.
+ const SkScalar x = 0;
+ const SkScalar y = 0;
+ const SkScalar width = visualize_cache_surface_->width();
+ const SkScalar height = visualize_cache_surface_->height();
+
+ // Scale the graph to show frame times up to those that are 3 times the frame
+ // time.
+ const double one_frame_ms = stopwatch_.GetFrameBudget().count();
+ const double max_interval = one_frame_ms * 3.0;
+ const double max_unit_interval = UnitFrameInterval(max_interval);
+
+ const double sample_unit_width = (1.0 / kMaxSamples);
+
+ // Draw vertical replacement bar to erase old/stale pixels.
+ paint.setColor(0x99FFFFFF);
+ paint.setStyle(SkPaint::Style::kFill_Style);
+ paint.setBlendMode(SkBlendMode::kSrc);
+ double sample_x =
+ x + width * (static_cast<double>(prev_drawn_sample_index_) / kMaxSamples);
+ const auto eraser_rect = SkRect::MakeLTRB(
+ sample_x, y, sample_x + width * sample_unit_width, height);
+ cache_canvas->drawRect(eraser_rect, paint);
+
+ // Draws blue timing bar for new data.
+ paint.setColor(0xAA0000FF);
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ const auto bar_rect = SkRect::MakeLTRB(
+ sample_x,
+ y + height *
+ (1.0 -
+ UnitHeight(stopwatch_
+ .GetLap(stopwatch_.GetCurrentSample() == 0
+ ? kMaxSamples - 1
+ : stopwatch_.GetCurrentSample() - 1)
+ .ToMillisecondsF(),
+ max_unit_interval)),
+ sample_x + width * sample_unit_width, height);
+ cache_canvas->drawRect(bar_rect, paint);
+
+ // Draw horizontal frame markers.
+ paint.setStrokeWidth(0); // hairline
+ paint.setStyle(SkPaint::Style::kStroke_Style);
+ paint.setColor(0xCC000000);
+
+ if (max_interval > one_frame_ms) {
+ // Paint the horizontal markers
+ size_t frame_marker_count =
+ static_cast<size_t>(max_interval / one_frame_ms);
+
+ // Limit the number of markers displayed. After a certain point, the graph
+ // becomes crowded
+ if (frame_marker_count > kMaxFrameMarkers) {
+ frame_marker_count = 1;
+ }
+
+ for (size_t frame_index = 0; frame_index < frame_marker_count;
+ frame_index++) {
+ const double frame_height =
+ height * (1.0 - (UnitFrameInterval((frame_index + 1) * one_frame_ms) /
+ max_unit_interval));
+ cache_canvas->drawLine(x, y + frame_height, width, y + frame_height,
+ 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);
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ if (UnitFrameInterval(stopwatch_.LastLap().ToMillisecondsF()) > 1.0) {
+ // budget exceeded
+ paint.setColor(SK_ColorRED);
+ } else {
+ // within budget
+ paint.setColor(SK_ColorGREEN);
+ }
+ sample_x = x + width * (static_cast<double>(stopwatch_.GetCurrentSample()) /
+ kMaxSamples);
+ const auto marker_rect = SkRect::MakeLTRB(
+ sample_x, y, sample_x + width * sample_unit_width, height);
+ cache_canvas->drawRect(marker_rect, paint);
+ prev_drawn_sample_index_ = stopwatch_.GetCurrentSample();
+
+ // Draw the cached surface onto the output canvas.
+ auto image = DlImage::Make(visualize_cache_surface_->makeImageSnapshot());
+ canvas->DrawImage(image, {rect.x(), rect.y()},
+ DlImageSampling::kNearestNeighbor);
+}
+
+} // namespace flutter
diff --git a/flow/stopwatch_sk.h b/flow/stopwatch_sk.h
new file mode 100644
index 0000000..c1dfb24
--- /dev/null
+++ b/flow/stopwatch_sk.h
@@ -0,0 +1,40 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_FLOW_STOPWATCH_SK_H_
+#define FLUTTER_FLOW_STOPWATCH_SK_H_
+
+#include "flow/stopwatch.h"
+#include "include/core/SkSurface.h"
+
+namespace flutter {
+
+//------------------------------------------------------------------------------
+/// A stopwatch visualizer that uses Skia (|SkCanvas|) to draw the stopwatch.
+///
+/// @see DlStopwatchVisualizer for the newer non-backend specific version.
+class SkStopwatchVisualizer : public StopwatchVisualizer {
+ public:
+ explicit SkStopwatchVisualizer(const Stopwatch& stopwatch)
+ : StopwatchVisualizer(stopwatch) {}
+
+ void Visualize(DlCanvas* canvas, const SkRect& rect) const override;
+
+ private:
+ /// Initializes the |SkSurface| used for drawing the stopwatch.
+ ///
+ /// Draws the base background and any timing data from before the initial
+ /// call to |Visualize|.
+ void InitVisualizeSurface(SkISize size) const;
+
+ // Mutable data cache for performance optimization of the graphs.
+ // Prevents expensive redrawing of old data.
+ mutable bool cache_dirty_ = true;
+ mutable sk_sp<SkSurface> visualize_cache_surface_;
+ mutable size_t prev_drawn_sample_index_ = 0;
+};
+
+} // namespace flutter
+
+#endif // FLUTTER_FLOW_STOPWATCH_SK_H_
diff --git a/flow/stopwatch_unittests.cc b/flow/stopwatch_unittests.cc
index 76893c7..3ded514 100644
--- a/flow/stopwatch_unittests.cc
+++ b/flow/stopwatch_unittests.cc
@@ -42,5 +42,20 @@
EXPECT_EQ(frame_budget_90fps, actual_frame_budget);
}
+TEST(Instrumentation, GetLapByIndexTest) {
+ fml::Milliseconds frame_budget_90fps = fml::RefreshRateToFrameBudget(90);
+ FixedRefreshRateStopwatch stopwatch(frame_budget_90fps);
+ stopwatch.SetLapTime(fml::TimeDelta::FromMilliseconds(10));
+ EXPECT_EQ(stopwatch.GetLap(1), fml::TimeDelta::FromMilliseconds(10));
+}
+
+TEST(Instrumentation, GetCurrentSampleTest) {
+ fml::Milliseconds frame_budget_90fps = fml::RefreshRateToFrameBudget(90);
+ FixedRefreshRateStopwatch stopwatch(frame_budget_90fps);
+ stopwatch.Start();
+ stopwatch.Stop();
+ EXPECT_EQ(stopwatch.GetCurrentSample(), size_t(1));
+}
+
} // namespace testing
} // namespace flutter
diff --git a/testing/resources/performance_overlay_gold_120fps.png b/testing/resources/performance_overlay_gold_120fps.png
index c19d1eb..9677724 100644
--- a/testing/resources/performance_overlay_gold_120fps.png
+++ b/testing/resources/performance_overlay_gold_120fps.png
Binary files differ
diff --git a/testing/resources/performance_overlay_gold_60fps.png b/testing/resources/performance_overlay_gold_60fps.png
index 4e18fa1..0d45210 100644
--- a/testing/resources/performance_overlay_gold_60fps.png
+++ b/testing/resources/performance_overlay_gold_60fps.png
Binary files differ
diff --git a/testing/resources/performance_overlay_gold_90fps.png b/testing/resources/performance_overlay_gold_90fps.png
index 962e287..d6fb7e8 100644
--- a/testing/resources/performance_overlay_gold_90fps.png
+++ b/testing/resources/performance_overlay_gold_90fps.png
Binary files differ