diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 536c859585..27424939bc 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -78,6 +78,7 @@ FILE: ../../../flutter/flow/frame_timings_recorder_unittests.cc FILE: ../../../flutter/flow/gl_context_switch_unittests.cc FILE: ../../../flutter/flow/instrumentation.cc FILE: ../../../flutter/flow/instrumentation.h +FILE: ../../../flutter/flow/instrumentation_unittests.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.cc FILE: ../../../flutter/flow/layers/backdrop_filter_layer.h FILE: ../../../flutter/flow/layers/backdrop_filter_layer_unittests.cc diff --git a/engine/src/flutter/flow/BUILD.gn b/engine/src/flutter/flow/BUILD.gn index 9312329a31..212d396077 100644 --- a/engine/src/flutter/flow/BUILD.gn +++ b/engine/src/flutter/flow/BUILD.gn @@ -135,6 +135,7 @@ if (enable_unittests) { "flow_test_utils.h", "frame_timings_recorder_unittests.cc", "gl_context_switch_unittests.cc", + "instrumentation_unittests.cc", "layers/backdrop_filter_layer_unittests.cc", "layers/checkerboard_layertree_unittests.cc", "layers/clip_path_layer_unittests.cc", diff --git a/engine/src/flutter/flow/compositor_context.cc b/engine/src/flutter/flow/compositor_context.cc index 5f38d9e2f0..3cf3e61fd6 100644 --- a/engine/src/flutter/flow/compositor_context.cc +++ b/engine/src/flutter/flow/compositor_context.cc @@ -43,8 +43,12 @@ std::optional FrameDamage::ComputeClipRect( } } -CompositorContext::CompositorContext(fml::Milliseconds frame_budget) - : raster_time_(frame_budget), ui_time_(frame_budget) {} +CompositorContext::CompositorContext() + : raster_time_(fixed_refresh_rate_updater_), + ui_time_(fixed_refresh_rate_updater_) {} + +CompositorContext::CompositorContext(Stopwatch::RefreshRateUpdater& updater) + : raster_time_(updater), ui_time_(updater) {} CompositorContext::~CompositorContext() = default; diff --git a/engine/src/flutter/flow/compositor_context.h b/engine/src/flutter/flow/compositor_context.h index 6a973d861b..80aaf22155 100644 --- a/engine/src/flutter/flow/compositor_context.h +++ b/engine/src/flutter/flow/compositor_context.h @@ -138,8 +138,9 @@ class CompositorContext { FML_DISALLOW_COPY_AND_ASSIGN(ScopedFrame); }; - explicit CompositorContext( - fml::Milliseconds frame_budget = fml::kDefaultFrameBudget); + CompositorContext(); + + explicit CompositorContext(Stopwatch::RefreshRateUpdater& updater); virtual ~CompositorContext(); @@ -173,6 +174,9 @@ class CompositorContext { Stopwatch raster_time_; Stopwatch ui_time_; + /// Only used by default constructor of `CompositorContext`. + FixedRefreshRateUpdater fixed_refresh_rate_updater_; + void BeginFrame(ScopedFrame& frame, bool enable_instrumentation); void EndFrame(ScopedFrame& frame, bool enable_instrumentation); diff --git a/engine/src/flutter/flow/instrumentation.cc b/engine/src/flutter/flow/instrumentation.cc index 49a05a9aa4..4365aa8014 100644 --- a/engine/src/flutter/flow/instrumentation.cc +++ b/engine/src/flutter/flow/instrumentation.cc @@ -15,17 +15,26 @@ namespace flutter { static const size_t kMaxSamples = 120; static const size_t kMaxFrameMarkers = 8; -Stopwatch::Stopwatch(fml::Milliseconds frame_budget) - : start_(fml::TimePoint::Now()), current_sample_(0) { +Stopwatch::Stopwatch(const RefreshRateUpdater& updater) + : refresh_rate_updater_(updater), + start_(fml::TimePoint::Now()), + current_sample_(0) { const fml::TimeDelta delta = fml::TimeDelta::Zero(); laps_.resize(kMaxSamples, delta); cache_dirty_ = true; prev_drawn_sample_index_ = 0; - frame_budget_ = frame_budget; } Stopwatch::~Stopwatch() = default; +FixedRefreshRateStopwatch::FixedRefreshRateStopwatch( + fml::Milliseconds frame_budget) + : Stopwatch(fixed_delegate_), fixed_delegate_(frame_budget) {} + +FixedRefreshRateUpdater::FixedRefreshRateUpdater( + fml::Milliseconds fixed_frame_budget) + : fixed_frame_budget_(fixed_frame_budget) {} + void Stopwatch::Start() { start_ = fml::TimePoint::Now(); current_sample_ = (current_sample_ + 1) % kMaxSamples; @@ -45,7 +54,7 @@ const fml::TimeDelta& Stopwatch::LastLap() const { } double Stopwatch::UnitFrameInterval(double raster_time_ms) const { - return raster_time_ms / frame_budget_.count(); + return raster_time_ms / GetFrameBudget().count(); } double Stopwatch::UnitHeight(double raster_time_ms, @@ -101,7 +110,7 @@ void Stopwatch::InitVisualizeSurface(const SkRect& rect) const { // Scale the graph to show frame times up to those that are 3 times the frame // time. - const double one_frame_ms = frame_budget_.count(); + const double one_frame_ms = GetFrameBudget().count(); const double max_interval = one_frame_ms * 3.0; const double max_unit_interval = UnitFrameInterval(max_interval); @@ -151,7 +160,7 @@ void Stopwatch::Visualize(SkCanvas* canvas, const SkRect& rect) const { // Scale the graph to show frame times up to those that are 3 times the frame // time. - const double one_frame_ms = frame_budget_.count(); + const double one_frame_ms = GetFrameBudget().count(); const double max_interval = one_frame_ms * 3.0; const double max_unit_interval = UnitFrameInterval(max_interval); @@ -228,6 +237,10 @@ void Stopwatch::Visualize(SkCanvas* canvas, const SkRect& rect) const { visualize_cache_surface_->draw(canvas, rect.x(), rect.y()); } +fml::Milliseconds Stopwatch::GetFrameBudget() const { + return refresh_rate_updater_.GetFrameBudget(); +} + CounterValues::CounterValues() : current_sample_(kMaxSamples - 1) { values_.resize(kMaxSamples, 0); } @@ -300,10 +313,6 @@ void CounterValues::Visualize(SkCanvas* canvas, const SkRect& rect) const { canvas->drawRect(marker_rect, paint); } -int64_t CounterValues::GetCurrentValue() const { - return values_[current_sample_]; -} - int64_t CounterValues::GetMaxValue() const { auto max = std::numeric_limits::min(); for (size_t i = 0; i < kMaxSamples; ++i) { @@ -320,4 +329,8 @@ int64_t CounterValues::GetMinValue() const { return min; } +fml::Milliseconds FixedRefreshRateUpdater::GetFrameBudget() const { + return fixed_frame_budget_; +} + } // namespace flutter diff --git a/engine/src/flutter/flow/instrumentation.h b/engine/src/flutter/flow/instrumentation.h index e38e7118f5..a61e9a5e48 100644 --- a/engine/src/flutter/flow/instrumentation.h +++ b/engine/src/flutter/flow/instrumentation.h @@ -16,14 +16,22 @@ namespace flutter { class Stopwatch { public: - explicit Stopwatch(fml::Milliseconds frame_budget = fml::kDefaultFrameBudget); + /// The refresh rate interface for `Stopwatch`. + class RefreshRateUpdater { + public: + /// Time limit for a smooth frame. + /// See: `DisplayManager::GetMainDisplayRefreshRate`. + virtual fml::Milliseconds GetFrameBudget() const = 0; + }; + + /// The constructor with a updater parameter, it will update frame_budget + /// everytime when `GetFrameBudget()` is called. + explicit Stopwatch(const RefreshRateUpdater& updater); ~Stopwatch(); const fml::TimeDelta& LastLap() const; - fml::TimeDelta CurrentLap() const { return fml::TimePoint::Now() - start_; } - fml::TimeDelta MaxDelta() const; fml::TimeDelta AverageDelta() const; @@ -38,16 +46,18 @@ class Stopwatch { void SetLapTime(const fml::TimeDelta& delta); + /// All places which want to get frame_budget should call this function. + 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 laps_; size_t current_sample_; - fml::Milliseconds frame_budget_; - // Mutable data cache for performance optimization of the graphs. Prevents // expensive redrawing of old data. mutable bool cache_dirty_; @@ -57,6 +67,28 @@ class Stopwatch { FML_DISALLOW_COPY_AND_ASSIGN(Stopwatch); }; +/// Used for fixed refresh rate query cases. +class FixedRefreshRateUpdater : public Stopwatch::RefreshRateUpdater { + fml::Milliseconds GetFrameBudget() const override; + + public: + explicit FixedRefreshRateUpdater( + fml::Milliseconds fixed_frame_budget = fml::kDefaultFrameBudget); + + private: + fml::Milliseconds fixed_frame_budget_; +}; + +/// Used for fixed refresh rate cases. +class FixedRefreshRateStopwatch : public Stopwatch { + public: + explicit FixedRefreshRateStopwatch( + fml::Milliseconds fixed_frame_budget = fml::kDefaultFrameBudget); + + private: + FixedRefreshRateUpdater fixed_delegate_; +}; + class Counter { public: Counter() : count_(0) {} @@ -83,8 +115,6 @@ class CounterValues { void Visualize(SkCanvas* canvas, const SkRect& rect) const; - int64_t GetCurrentValue() const; - int64_t GetMaxValue() const; int64_t GetMinValue() const; diff --git a/engine/src/flutter/flow/instrumentation_unittests.cc b/engine/src/flutter/flow/instrumentation_unittests.cc new file mode 100644 index 0000000000..77f684752e --- /dev/null +++ b/engine/src/flutter/flow/instrumentation_unittests.cc @@ -0,0 +1,46 @@ +// 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/instrumentation.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using testing::Return; + +namespace flutter { +namespace testing { + +class MockRefreshRateUpdater : public Stopwatch::RefreshRateUpdater { + public: + MOCK_CONST_METHOD0(GetFrameBudget, fml::Milliseconds()); +}; + +TEST(Instrumentation, GetDefaultFrameBudgetTest) { + fml::Milliseconds frame_budget_60fps = fml::RefreshRateToFrameBudget(60); + // The default constructor sets the frame_budget to 16.6667 (60 fps). + FixedRefreshRateStopwatch stopwatch; + fml::Milliseconds actual_frame_budget = stopwatch.GetFrameBudget(); + EXPECT_EQ(frame_budget_60fps, actual_frame_budget); +} + +TEST(Instrumentation, GetOneShotFrameBudgetTest) { + fml::Milliseconds frame_budget_90fps = fml::RefreshRateToFrameBudget(90); + FixedRefreshRateStopwatch stopwatch(frame_budget_90fps); + fml::Milliseconds actual_frame_budget = stopwatch.GetFrameBudget(); + EXPECT_EQ(frame_budget_90fps, actual_frame_budget); +} + +TEST(Instrumentation, GetFrameBudgetFromUpdaterTest) { + MockRefreshRateUpdater updater; + fml::Milliseconds frame_budget_90fps = fml::RefreshRateToFrameBudget(90); + EXPECT_CALL(updater, GetFrameBudget()) + .Times(1) + .WillOnce(Return(frame_budget_90fps)); + Stopwatch stopwatch(updater); + fml::Milliseconds actual_frame_budget = stopwatch.GetFrameBudget(); + EXPECT_EQ(frame_budget_90fps, actual_frame_budget); +} + +} // namespace testing +} // namespace flutter diff --git a/engine/src/flutter/flow/layers/layer_tree.cc b/engine/src/flutter/flow/layers/layer_tree.cc index 447998ec5a..981e70b162 100644 --- a/engine/src/flutter/flow/layers/layer_tree.cc +++ b/engine/src/flutter/flow/layers/layer_tree.cc @@ -102,7 +102,7 @@ sk_sp LayerTree::Flatten(const SkRect& bounds) { } MutatorsStack unused_stack; - const Stopwatch unused_stopwatch; + const FixedRefreshRateStopwatch unused_stopwatch; TextureRegistry unused_texture_registry; SkMatrix root_surface_transformation; // No root surface transformation. So assume identity. diff --git a/engine/src/flutter/flow/layers/layer_tree_unittests.cc b/engine/src/flutter/flow/layers/layer_tree_unittests.cc index 2c70149a2c..839112707e 100644 --- a/engine/src/flutter/flow/layers/layer_tree_unittests.cc +++ b/engine/src/flutter/flow/layers/layer_tree_unittests.cc @@ -19,7 +19,6 @@ class LayerTreeTest : public CanvasTest { public: LayerTreeTest() : layer_tree_(SkISize::Make(64, 64), 1.0f), - compositor_context_(fml::kDefaultFrameBudget), root_transform_(SkMatrix::Translate(1.0f, 1.0f)), scoped_frame_(compositor_context_.AcquireFrame(nullptr, &mock_canvas(), diff --git a/engine/src/flutter/flow/layers/performance_overlay_layer_unittests.cc b/engine/src/flutter/flow/layers/performance_overlay_layer_unittests.cc index 0fe52300cb..ecc400c26a 100644 --- a/engine/src/flutter/flow/layers/performance_overlay_layer_unittests.cc +++ b/engine/src/flutter/flow/layers/performance_overlay_layer_unittests.cc @@ -10,11 +10,7 @@ #include "flutter/flow/flow_test_utils.h" #include "flutter/flow/raster_cache.h" #include "flutter/flow/testing/layer_test.h" -#include "flutter/flow/testing/mock_layer.h" -#include "flutter/fml/build_config.h" -#include "flutter/fml/macros.h" #include "flutter/testing/mock_canvas.h" -#include "gtest/gtest.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkSurface.h" @@ -49,7 +45,7 @@ static void TestPerformanceOverlayLayerGold(int refresh_rate) { std::string golden_file_path = GetGoldenFilePath(refresh_rate, false); std::string new_golden_file_path = GetGoldenFilePath(refresh_rate, true); - flutter::Stopwatch mock_stopwatch( + FixedRefreshRateStopwatch mock_stopwatch( fml::RefreshRateToFrameBudget(refresh_rate)); for (int i = 0; i < size(kMockedTimes); ++i) { mock_stopwatch.SetLapTime( diff --git a/engine/src/flutter/flow/testing/layer_test.h b/engine/src/flutter/flow/testing/layer_test.h index d3facde953..1e810ec80d 100644 --- a/engine/src/flutter/flow/testing/layer_test.h +++ b/engine/src/flutter/flow/testing/layer_test.h @@ -136,8 +136,8 @@ class LayerTestBase : public CanvasTestBase { paint_context_.raster_cache = raster_cache_.get(); } - Stopwatch raster_time_; - Stopwatch ui_time_; + FixedRefreshRateStopwatch raster_time_; + FixedRefreshRateStopwatch ui_time_; MutatorsStack mutators_stack_; TextureRegistry texture_registry_; diff --git a/engine/src/flutter/flow/testing/mock_raster_cache.cc b/engine/src/flutter/flow/testing/mock_raster_cache.cc index f9c1f5e6c2..60e01f15cc 100644 --- a/engine/src/flutter/flow/testing/mock_raster_cache.cc +++ b/engine/src/flutter/flow/testing/mock_raster_cache.cc @@ -66,8 +66,8 @@ void MockRasterCache::AddMockPicture(int width, int height) { } PrerollContextHolder GetSamplePrerollContextHolder() { - Stopwatch raster_time; - Stopwatch ui_time; + FixedRefreshRateStopwatch raster_time; + FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; TextureRegistry texture_registry; sk_sp srgb = SkColorSpace::MakeSRGB(); diff --git a/engine/src/flutter/flow/testing/mock_raster_cache.h b/engine/src/flutter/flow/testing/mock_raster_cache.h index f51f700b3d..e10e1876d6 100644 --- a/engine/src/flutter/flow/testing/mock_raster_cache.h +++ b/engine/src/flutter/flow/testing/mock_raster_cache.h @@ -74,8 +74,8 @@ class MockRasterCache : public RasterCache { MockCanvas mock_canvas_; SkColorSpace* color_space_ = mock_canvas_.imageInfo().colorSpace(); MutatorsStack mutators_stack_; - Stopwatch raster_time_; - Stopwatch ui_time_; + FixedRefreshRateStopwatch raster_time_; + FixedRefreshRateStopwatch ui_time_; TextureRegistry texture_registry_; PrerollContext preroll_context_ = { nullptr, /* raster_cache */ diff --git a/engine/src/flutter/shell/common/rasterizer.cc b/engine/src/flutter/shell/common/rasterizer.cc index 2fec73ab23..ef12ceaf7b 100644 --- a/engine/src/flutter/shell/common/rasterizer.cc +++ b/engine/src/flutter/shell/common/rasterizer.cc @@ -14,7 +14,6 @@ #include "flutter/fml/time/time_point.h" #include "flutter/shell/common/serialization_callbacks.h" #include "fml/make_copyable.h" -#include "third_party/skia/include/core/SkEncodedImageFormat.h" #include "third_party/skia/include/core/SkImageEncoder.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkSerialProcs.h" @@ -30,8 +29,7 @@ static constexpr std::chrono::milliseconds kSkiaCleanupExpiration(15000); Rasterizer::Rasterizer(Delegate& delegate) : delegate_(delegate), - compositor_context_(std::make_unique( - delegate.GetFrameBudget())), + compositor_context_(std::make_unique(*this)), user_override_resource_cache_bytes_(false), weak_factory_(this) { FML_DCHECK(compositor_context_); @@ -381,6 +379,10 @@ sk_sp Rasterizer::ConvertToRasterImage(sk_sp image) { }); } +fml::Milliseconds Rasterizer::GetFrameBudget() const { + return delegate_.GetFrameBudget(); +}; + RasterStatus Rasterizer::DoDraw( std::unique_ptr frame_timings_recorder, std::unique_ptr layer_tree) { diff --git a/engine/src/flutter/shell/common/rasterizer.h b/engine/src/flutter/shell/common/rasterizer.h index fb381cc0f9..6d53055ddf 100644 --- a/engine/src/flutter/shell/common/rasterizer.h +++ b/engine/src/flutter/shell/common/rasterizer.h @@ -42,7 +42,8 @@ namespace flutter { /// and the on-screen render surface. The compositor context has all the GPU /// state necessary to render frames to the render surface. /// -class Rasterizer final : public SnapshotDelegate { +class Rasterizer final : public SnapshotDelegate, + public Stopwatch::RefreshRateUpdater { public: //---------------------------------------------------------------------------- /// @brief Used to forward events from the rasterizer to interested @@ -460,6 +461,12 @@ class Rasterizer final : public SnapshotDelegate { // |SnapshotDelegate| sk_sp ConvertToRasterImage(sk_sp image) override; + // |Stopwatch::Delegate| + /// Time limit for a smooth frame. + /// + /// See: `DisplayManager::GetMainDisplayRefreshRate`. + fml::Milliseconds GetFrameBudget() const override; + sk_sp ScreenshotLayerTreeAsImage( flutter::LayerTree* tree, flutter::CompositorContext& compositor_context, diff --git a/engine/src/flutter/shell/common/shell_unittests.cc b/engine/src/flutter/shell/common/shell_unittests.cc index 46cd8863d0..87fd38ed28 100644 --- a/engine/src/flutter/shell/common/shell_unittests.cc +++ b/engine/src/flutter/shell/common/shell_unittests.cc @@ -2248,8 +2248,8 @@ TEST_F(ShellTest, OnServiceProtocolEstimateRasterCacheMemoryWorks) { auto* compositor_context = shell->GetRasterizer()->compositor_context(); auto& raster_cache = compositor_context->raster_cache(); - Stopwatch raster_time; - Stopwatch ui_time; + FixedRefreshRateStopwatch raster_time; + FixedRefreshRateStopwatch ui_time; MutatorsStack mutators_stack; TextureRegistry texture_registry; PrerollContext preroll_context = {