Streamline frame timings recording `FlutterFrameTimingsRecorder` is passed through the lifecycle of the frame and records the events that happen through it. This also serves as a builder for the `FrameTiming` object that is later passed to the framework. This change also aims to lay the foundation to capture a unique frame identifier which will later be added to `FlutterFrameTimingsRecorder` to identify the trace events corresponding to each frame. x-ref: https://github.com/flutter/engine/pull/25662 x-ref: https://github.com/flutter/flutter/issues/80735
diff --git a/flow/BUILD.gn b/flow/BUILD.gn index e112594..a2f1d8a 100644 --- a/flow/BUILD.gn +++ b/flow/BUILD.gn
@@ -14,6 +14,8 @@ "diff_context.h", "embedded_views.cc", "embedded_views.h", + "frame_timings.cc", + "frame_timings.h", "instrumentation.cc", "instrumentation.h", "layers/backdrop_filter_layer.cc",
diff --git a/flow/frame_timings.cc b/flow/frame_timings.cc new file mode 100644 index 0000000..2f0ccf8 --- /dev/null +++ b/flow/frame_timings.cc
@@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is FrameTimingsRecorder::Governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "flutter/flow/frame_timings.h" +#include <mutex> +#include "flutter/common/settings.h" +#include "flutter/fml/logging.h" + +namespace flutter { + +FrameTimingsRecorder::FrameTimingsRecorder() = default; + +FrameTimingsRecorder::~FrameTimingsRecorder() = default; + +fml::TimePoint FrameTimingsRecorder::GetVsyncStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kVsync); + return vsync_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetVsyncTargetTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kVsync); + return vsync_target_; +} + +fml::TimePoint FrameTimingsRecorder::GetBuildStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kBuildStart); + return build_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetBuildEndTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kBuildEnd); + return build_end_; +} + +fml::TimePoint FrameTimingsRecorder::GetRasterStartTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kRasterStart); + return raster_start_; +} + +fml::TimePoint FrameTimingsRecorder::GetRasterEndTime() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kRasterEnd); + return raster_end_; +} + +fml::TimeDelta FrameTimingsRecorder::GetBuildDuration() const { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ >= State::kBuildEnd); + return build_end_ - build_start_; +} + +void FrameTimingsRecorder::RecordVsync(fml::TimePoint vsync_start, + fml::TimePoint vsync_target) { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ == State::kUnitialized); + state_ = State::kVsync; + vsync_start_ = vsync_start; + vsync_target_ = vsync_target; +} + +void FrameTimingsRecorder::RecordBuildStart(fml::TimePoint build_start) { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ == State::kVsync); + state_ = State::kBuildStart; + build_start_ = build_start; +} + +void FrameTimingsRecorder::RecordBuildEnd(fml::TimePoint build_end) { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ == State::kBuildStart); + state_ = State::kBuildEnd; + build_end_ = build_end; +} + +void FrameTimingsRecorder::RecordRasterStart(fml::TimePoint raster_start) { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ == State::kBuildEnd); + state_ = State::kRasterStart; + raster_start_ = raster_start; +} + +FrameTiming FrameTimingsRecorder::RecordRasterEnd(fml::TimePoint raster_end) { + std::scoped_lock state_lock(state_mutex_); + FML_CHECK(state_ == State::kRasterStart); + state_ = State::kRasterEnd; + raster_end_ = raster_end; + FrameTiming timing; + timing.Set(FrameTiming::kVsyncStart, vsync_start_); + timing.Set(FrameTiming::kBuildStart, build_start_); + timing.Set(FrameTiming::kBuildFinish, build_end_); + timing.Set(FrameTiming::kRasterStart, raster_start_); + timing.Set(FrameTiming::kRasterFinish, raster_end_); + return timing; +} + +} // namespace flutter
diff --git a/flow/frame_timings.h b/flow/frame_timings.h new file mode 100644 index 0000000..4d7d9d4 --- /dev/null +++ b/flow/frame_timings.h
@@ -0,0 +1,72 @@ +// 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_FRAME_TIMINGS_H_ +#define FLUTTER_FLOW_FRAME_TIMINGS_H_ + +#include <mutex> + +#include "flutter/common/settings.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/time/time_delta.h" +#include "flutter/fml/time/time_point.h" + +namespace flutter { + +class FrameTimingsRecorder { + public: + FrameTimingsRecorder(); + + ~FrameTimingsRecorder(); + + fml::TimePoint GetVsyncStartTime() const; + + fml::TimePoint GetVsyncTargetTime() const; + + fml::TimePoint GetBuildStartTime() const; + + fml::TimePoint GetBuildEndTime() const; + + fml::TimePoint GetRasterStartTime() const; + + fml::TimePoint GetRasterEndTime() const; + + fml::TimeDelta GetBuildDuration() const; + + void RecordVsync(fml::TimePoint vsync_start, fml::TimePoint vsync_target); + + void RecordBuildStart(fml::TimePoint build_start); + + void RecordBuildEnd(fml::TimePoint build_end); + + void RecordRasterStart(fml::TimePoint raster_start); + + FrameTiming RecordRasterEnd(fml::TimePoint raster_end); + + private: + enum class State : uint32_t { + kUnitialized = 1, + kVsync = 2, + kBuildStart = 3, + kBuildEnd = 4, + kRasterStart = 5, + kRasterEnd = 6, + }; + + mutable std::mutex state_mutex_; + State state_ = State::kUnitialized; + + fml::TimePoint vsync_start_ = fml::TimePoint::Min(); + fml::TimePoint vsync_target_ = fml::TimePoint::Min(); + fml::TimePoint build_start_ = fml::TimePoint::Min(); + fml::TimePoint build_end_ = fml::TimePoint::Min(); + fml::TimePoint raster_start_ = fml::TimePoint::Min(); + fml::TimePoint raster_end_ = fml::TimePoint::Min(); + + FML_DISALLOW_COPY_ASSIGN_AND_MOVE(FrameTimingsRecorder); +}; + +} // namespace flutter + +#endif // FLUTTER_FLOW_FRAME_TIMINGS_H_
diff --git a/flow/layers/layer_tree.cc b/flow/layers/layer_tree.cc index eb58bc1..2fa755f 100644 --- a/flow/layers/layer_tree.cc +++ b/flow/layers/layer_tree.cc
@@ -4,7 +4,9 @@ #include "flutter/flow/layers/layer_tree.h" +#include "flutter/flow/frame_timings.h" #include "flutter/flow/layers/layer.h" +#include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/utils/SkNWayCanvas.h" @@ -20,15 +22,6 @@ FML_CHECK(device_pixel_ratio_ != 0.0f); } -void LayerTree::RecordBuildTime(fml::TimePoint vsync_start, - fml::TimePoint build_start, - fml::TimePoint target_time) { - vsync_start_ = vsync_start; - build_start_ = build_start; - target_time_ = target_time; - build_finish_ = fml::TimePoint::Now(); -} - bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame, bool ignore_raster_cache) { TRACE_EVENT0("flutter", "LayerTree::Preroll");
diff --git a/flow/layers/layer_tree.h b/flow/layers/layer_tree.h index db8a33b..67a32ca 100644 --- a/flow/layers/layer_tree.h +++ b/flow/layers/layer_tree.h
@@ -56,16 +56,6 @@ #endif // FLUTTER_ENABLE_DIFF_CONTEXT - void RecordBuildTime(fml::TimePoint vsync_start, - fml::TimePoint build_start, - fml::TimePoint target_time); - fml::TimePoint vsync_start() const { return vsync_start_; } - fml::TimeDelta vsync_overhead() const { return build_start_ - vsync_start_; } - fml::TimePoint build_start() const { return build_start_; } - fml::TimePoint build_finish() const { return build_finish_; } - fml::TimeDelta build_time() const { return build_finish_ - build_start_; } - fml::TimePoint target_time() const { return target_time_; } - // The number of frame intervals missed after which the compositor must // trace the rasterized picture to a trace file. Specify 0 to disable all // tracing @@ -87,10 +77,6 @@ private: std::shared_ptr<Layer> root_layer_; - fml::TimePoint vsync_start_; - fml::TimePoint build_start_; - fml::TimePoint build_finish_; - fml::TimePoint target_time_; SkISize frame_size_ = SkISize::MakeEmpty(); // Physical pixels. const float device_pixel_ratio_; // Logical / Physical pixels ratio. uint32_t rasterizer_tracing_threshold_;
diff --git a/lib/ui/painting/image_decoder_unittests.cc b/lib/ui/painting/image_decoder_unittests.cc index 6e74df5..7b787b0 100644 --- a/lib/ui/painting/image_decoder_unittests.cc +++ b/lib/ui/painting/image_decoder_unittests.cc
@@ -427,7 +427,8 @@ latch.Wait(); } -// TODO(https://github.com/flutter/flutter/issues/81232) - disabled due to flakiness +// TODO(https://github.com/flutter/flutter/issues/81232) - disabled due to +// flakiness TEST_F(ImageDecoderFixtureTest, DISABLED_CanResizeWithoutDecode) { SkImageInfo info = {}; size_t row_bytes;
diff --git a/shell/common/animator.cc b/shell/common/animator.cc index c2fc83e..4e5fff1 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc
@@ -4,6 +4,8 @@ #include "flutter/shell/common/animator.h" +#include "flutter/flow/frame_timings.h" +#include "flutter/fml/time/time_point.h" #include "flutter/fml/trace_event.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -25,9 +27,6 @@ : delegate_(delegate), task_runners_(std::move(task_runners)), waiter_(std::move(waiter)), - last_frame_begin_time_(), - last_vsync_start_time_(), - last_frame_target_time_(), dart_frame_deadline_(0), #if SHELL_ENABLE_METAL layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)), @@ -96,8 +95,8 @@ return (time - fxl_now).ToMicroseconds() + dart_now; } -void Animator::BeginFrame(fml::TimePoint vsync_start_time, - fml::TimePoint frame_target_time) { +void Animator::BeginFrame( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++); TRACE_EVENT0("flutter", "Animator::BeginFrame"); @@ -131,12 +130,14 @@ // to service potential frame. FML_DCHECK(producer_continuation_); - last_frame_begin_time_ = fml::TimePoint::Now(); - last_vsync_start_time_ = vsync_start_time; - fml::tracing::TraceEventAsyncComplete("flutter", "VsyncSchedulingOverhead", - last_vsync_start_time_, - last_frame_begin_time_); - last_frame_target_time_ = frame_target_time; + frame_timings_recorder_ = std::move(frame_timings_recorder); + frame_timings_recorder_->RecordBuildStart(fml::TimePoint::Now()); + fml::tracing::TraceEventAsyncComplete( + "flutter", "VsyncSchedulingOverhead", + frame_timings_recorder_->GetVsyncStartTime(), + frame_timings_recorder_->GetBuildStartTime()); + const fml::TimePoint frame_target_time = + frame_timings_recorder_->GetVsyncTargetTime(); dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time); { TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", @@ -180,9 +181,7 @@ } last_layer_tree_size_ = layer_tree->frame_size(); - // Note the frame time for instrumentation. - layer_tree->RecordBuildTime(last_vsync_start_time_, last_frame_begin_time_, - last_frame_target_time_); + frame_timings_recorder_->RecordBuildEnd(fml::TimePoint::Now()); // Commit the pending continuation. bool result = producer_continuation_.Complete(std::move(layer_tree)); @@ -190,16 +189,25 @@ FML_DLOG(INFO) << "No pending continuation to commit"; } - delegate_.OnAnimatorDraw(layer_tree_pipeline_, last_frame_target_time_); + delegate_.OnAnimatorDraw(layer_tree_pipeline_, + std::move(frame_timings_recorder_)); } bool Animator::CanReuseLastLayerTree() { return !regenerate_layer_tree_; } -void Animator::DrawLastLayerTree() { +void Animator::DrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { pending_frame_semaphore_.Signal(); - delegate_.OnAnimatorDrawLastLayerTree(); + // In this case BeginFrame doesn't get called, we need to + // adjust frame timings to update build start and end times, + // given that the frame doesn't get built in this case, we + // will use Now() for both start and end times as an indication. + const auto now = fml::TimePoint::Now(); + frame_timings_recorder->RecordBuildStart(now); + frame_timings_recorder->RecordBuildEnd(now); + delegate_.OnAnimatorDrawLastLayerTree(std::move(frame_timings_recorder)); } void Animator::RequestFrame(bool regenerate_layer_tree) { @@ -236,13 +244,13 @@ void Animator::AwaitVSync() { waiter_->AsyncWaitForVsync( - [self = weak_factory_.GetWeakPtr()](fml::TimePoint vsync_start_time, - fml::TimePoint frame_target_time) { + [self = weak_factory_.GetWeakPtr()]( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { if (self) { if (self->CanReuseLastLayerTree()) { - self->DrawLastLayerTree(); + self->DrawLastLayerTree(std::move(frame_timings_recorder)); } else { - self->BeginFrame(vsync_start_time, frame_target_time); + self->BeginFrame(std::move(frame_timings_recorder)); } } });
diff --git a/shell/common/animator.h b/shell/common/animator.h index 6927e85..c8efaff 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h
@@ -8,6 +8,7 @@ #include <deque> #include "flutter/common/task_runners.h" +#include "flutter/flow/frame_timings.h" #include "flutter/fml/memory/ref_ptr.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/synchronization/semaphore.h" @@ -36,9 +37,10 @@ virtual void OnAnimatorDraw( fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, - fml::TimePoint frame_target_time) = 0; + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) = 0; - virtual void OnAnimatorDrawLastLayerTree() = 0; + virtual void OnAnimatorDrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) = 0; }; Animator(Delegate& delegate, @@ -83,11 +85,12 @@ private: using LayerTreePipeline = Pipeline<flutter::LayerTree>; - void BeginFrame(fml::TimePoint frame_start_time, - fml::TimePoint frame_target_time); + void BeginFrame(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder); bool CanReuseLastLayerTree(); - void DrawLastLayerTree(); + + void DrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder); void AwaitVSync(); @@ -100,9 +103,7 @@ TaskRunners task_runners_; std::shared_ptr<VsyncWaiter> waiter_; - fml::TimePoint last_frame_begin_time_; - fml::TimePoint last_vsync_start_time_; - fml::TimePoint last_frame_target_time_; + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder_; int64_t dart_frame_deadline_; fml::RefPtr<LayerTreePipeline> layer_tree_pipeline_; fml::Semaphore pending_frame_semaphore_;
diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index acd3397..40ec095 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc
@@ -7,10 +7,12 @@ #include <algorithm> #include <utility> +#include "flow/frame_timings.h" #include "flutter/common/graphics/persistent_cache.h" #include "flutter/fml/time/time_delta.h" #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" @@ -145,15 +147,18 @@ return last_layer_tree_.get(); } -void Rasterizer::DrawLastLayerTree() { +void Rasterizer::DrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { if (!last_layer_tree_ || !surface_) { return; } - DrawToSurface(*last_layer_tree_); + DrawToSurface(frame_timings_recorder->GetBuildDuration(), *last_layer_tree_); } -void Rasterizer::Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, - LayerTreeDiscardCallback discardCallback) { +void Rasterizer::Draw( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder, + fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, + LayerTreeDiscardCallback discardCallback) { TRACE_EVENT0("flutter", "GPURasterizer::Draw"); if (raster_thread_merger_ && !raster_thread_merger_->IsOnRasterizingThread()) { @@ -170,7 +175,8 @@ if (discardCallback(*layer_tree.get())) { raster_status = RasterStatus::kDiscarded; } else { - raster_status = DoDraw(std::move(layer_tree)); + raster_status = + DoDraw(std::move(frame_timings_recorder), std::move(layer_tree)); } }; @@ -196,22 +202,6 @@ external_view_embedder_->EndFrame(should_resubmit_frame, raster_thread_merger_); } - - // Consume as many pipeline items as possible. But yield the event loop - // between successive tries. - switch (consume_result) { - case PipelineConsumeResult::MoreAvailable: { - delegate_.GetTaskRunners().GetRasterTaskRunner()->PostTask( - [weak_this = weak_factory_.GetWeakPtr(), pipeline]() { - if (weak_this) { - weak_this->Draw(pipeline); - } - }); - break; - } - default: - break; - } } namespace { @@ -332,6 +322,7 @@ } RasterStatus Rasterizer::DoDraw( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder, std::unique_ptr<flutter::LayerTree> layer_tree) { FML_DCHECK(delegate_.GetTaskRunners() .GetRasterTaskRunner() @@ -341,19 +332,13 @@ return RasterStatus::kFailed; } - FrameTiming timing; -#if !defined(OS_FUCHSIA) - const fml::TimePoint frame_target_time = layer_tree->target_time(); -#endif - timing.Set(FrameTiming::kVsyncStart, layer_tree->vsync_start()); - timing.Set(FrameTiming::kBuildStart, layer_tree->build_start()); - timing.Set(FrameTiming::kBuildFinish, layer_tree->build_finish()); - timing.Set(FrameTiming::kRasterStart, fml::TimePoint::Now()); + frame_timings_recorder->RecordRasterStart(fml::TimePoint::Now()); PersistentCache* persistent_cache = PersistentCache::GetCacheForProcess(); persistent_cache->ResetStoredNewShaders(); - RasterStatus raster_status = DrawToSurface(*layer_tree); + RasterStatus raster_status = + DrawToSurface(frame_timings_recorder->GetBuildDuration(), *layer_tree); if (raster_status == RasterStatus::kSuccess) { last_layer_tree_ = std::move(layer_tree); } else if (raster_status == RasterStatus::kResubmit || @@ -373,12 +358,14 @@ // Rasterizer::DoDraw finishes. Future work is needed to adapt the timestamp // for Fuchsia to capture SceneUpdateContext::ExecutePaintTasks. const auto raster_finish_time = fml::TimePoint::Now(); - timing.Set(FrameTiming::kRasterFinish, raster_finish_time); - delegate_.OnFrameRasterized(timing); + delegate_.OnFrameRasterized( + frame_timings_recorder->RecordRasterEnd(raster_finish_time)); // SceneDisplayLag events are disabled on Fuchsia. // see: https://github.com/flutter/flutter/issues/56598 #if !defined(OS_FUCHSIA) + fml::TimePoint frame_target_time = + frame_timings_recorder->GetVsyncTargetTime(); if (raster_finish_time > frame_target_time) { fml::TimePoint latest_frame_target_time = delegate_.GetLatestFrameTargetTime(); @@ -432,14 +419,13 @@ return raster_status; } -RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) { +RasterStatus Rasterizer::DrawToSurface( + const fml::TimeDelta frame_build_duration, + flutter::LayerTree& layer_tree) { TRACE_EVENT0("flutter", "Rasterizer::DrawToSurface"); FML_DCHECK(surface_); - // There is no way for the compositor to know how long the layer tree - // construction took. Fortunately, the layer tree does. Grab that time - // for instrumentation. - compositor_context_->ui_time().SetLapTime(layer_tree.build_time()); + compositor_context_->ui_time().SetLapTime(frame_build_duration); SkCanvas* embedder_root_canvas = nullptr; if (external_view_embedder_) {
diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index 9258224..1f1559c 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h
@@ -8,10 +8,11 @@ #include <memory> #include <optional> -#include "flow/embedded_views.h" #include "flutter/common/settings.h" #include "flutter/common/task_runners.h" #include "flutter/flow/compositor_context.h" +#include "flutter/flow/embedded_views.h" +#include "flutter/flow/frame_timings.h" #include "flutter/flow/layers/layer_tree.h" #include "flutter/flow/surface.h" #include "flutter/fml/closure.h" @@ -195,7 +196,8 @@ /// textures instead of waiting for the framework to do the work /// to generate the layer tree describing the same contents. /// - void DrawLastLayerTree(); + void DrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder); //---------------------------------------------------------------------------- /// @brief Gets the registry of external textures currently in use by the @@ -241,7 +243,8 @@ /// @param[in] discardCallback if specified and returns true, the layer tree /// is discarded instead of being rendered /// - void Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, + void Draw(std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder, + fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, LayerTreeDiscardCallback discardCallback = NoDiscard); //---------------------------------------------------------------------------- @@ -478,9 +481,12 @@ SkISize size, std::function<void(SkCanvas*)> draw_callback); - RasterStatus DoDraw(std::unique_ptr<flutter::LayerTree> layer_tree); + RasterStatus DoDraw( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder, + std::unique_ptr<flutter::LayerTree> layer_tree); - RasterStatus DrawToSurface(flutter::LayerTree& layer_tree); + RasterStatus DrawToSurface(const fml::TimeDelta frame_build_duration, + flutter::LayerTree& layer_tree); void FireNextFrameCallbackIfPresent();
diff --git a/shell/common/rasterizer_unittests.cc b/shell/common/rasterizer_unittests.cc index 96321df..b95212f 100644 --- a/shell/common/rasterizer_unittests.cc +++ b/shell/common/rasterizer_unittests.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> +#include "flow/frame_timings.h" #define FML_USED_ON_EMBEDDER #include "flutter/shell/common/rasterizer.h" @@ -91,7 +93,8 @@ fml::AutoResetWaitableEvent latch; thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = fml::AdoptRef(new Pipeline<LayerTree>(/*depth=*/10)); - rasterizer->Draw(pipeline, nullptr); + rasterizer->Draw(std::make_unique<FrameTimingsRecorder>(), pipeline, + nullptr); latch.Signal(); }); latch.Wait(); @@ -148,7 +151,8 @@ bool result = pipeline->Produce().Complete(std::move(layer_tree)); EXPECT_TRUE(result); auto no_discard = [](LayerTree&) { return false; }; - rasterizer->Draw(pipeline, no_discard); + rasterizer->Draw(std::make_unique<FrameTimingsRecorder>(), pipeline, + no_discard); latch.Signal(); }); latch.Wait(); @@ -202,7 +206,8 @@ bool result = pipeline->Produce().Complete(std::move(layer_tree)); EXPECT_TRUE(result); auto no_discard = [](LayerTree&) { return false; }; - rasterizer->Draw(pipeline, no_discard); + rasterizer->Draw(std::make_unique<FrameTimingsRecorder>(), pipeline, + no_discard); latch.Signal(); }); latch.Wait(); @@ -261,7 +266,8 @@ bool result = pipeline->Produce().Complete(std::move(layer_tree)); EXPECT_TRUE(result); auto no_discard = [](LayerTree&) { return false; }; - rasterizer->Draw(pipeline, no_discard); + rasterizer->Draw(std::make_unique<FrameTimingsRecorder>(), pipeline, + no_discard); } TEST(RasterizerTest, externalViewEmbedderDoesntEndFrameWhenNoSurfaceIsSet) { @@ -294,7 +300,8 @@ thread_host.raster_thread->GetTaskRunner()->PostTask([&] { auto pipeline = fml::AdoptRef(new Pipeline<LayerTree>(/*depth=*/10)); auto no_discard = [](LayerTree&) { return false; }; - rasterizer->Draw(pipeline, no_discard); + rasterizer->Draw(std::make_unique<FrameTimingsRecorder>(), pipeline, + no_discard); latch.Signal(); }); latch.Wait();
diff --git a/shell/common/shell.cc b/shell/common/shell.cc index a2f71f6..c9541ba 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc
@@ -1138,13 +1138,16 @@ } // |Animator::Delegate| -void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, - fml::TimePoint frame_target_time) { +void Shell::OnAnimatorDraw( + fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { FML_DCHECK(is_setup_); // record the target time for use by rasterizer. { std::scoped_lock time_recorder_lock(time_recorder_mutex_); + const fml::TimePoint frame_target_time = + frame_timings_recorder->GetVsyncTargetTime(); if (!latest_frame_target_time_) { latest_frame_target_time_ = frame_target_time; } else if (latest_frame_target_time_ < frame_target_time) { @@ -1158,32 +1161,38 @@ tree.frame_size() != expected_frame_size_; }; - task_runners_.GetRasterTaskRunner()->PostTask( + task_runners_.GetRasterTaskRunner()->PostTask(fml::MakeCopyable( [&waiting_for_first_frame = waiting_for_first_frame_, &waiting_for_first_frame_condition = waiting_for_first_frame_condition_, rasterizer = rasterizer_->GetWeakPtr(), pipeline = std::move(pipeline), - discard_callback = std::move(discard_callback)]() { + discard_callback = std::move(discard_callback), + frame_timings_recorder = std::move(frame_timings_recorder)]() mutable { if (rasterizer) { - rasterizer->Draw(pipeline, std::move(discard_callback)); + rasterizer->Draw(std::move(frame_timings_recorder), pipeline, + std::move(discard_callback)); if (waiting_for_first_frame.load()) { waiting_for_first_frame.store(false); waiting_for_first_frame_condition.notify_all(); } } - }); + })); } // |Animator::Delegate| -void Shell::OnAnimatorDrawLastLayerTree() { +void Shell::OnAnimatorDrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) { FML_DCHECK(is_setup_); - task_runners_.GetRasterTaskRunner()->PostTask( - [rasterizer = rasterizer_->GetWeakPtr()]() { + auto task = fml::MakeCopyable( + [rasterizer = rasterizer_->GetWeakPtr(), + frame_timings_recorder = std::move(frame_timings_recorder)]() mutable { if (rasterizer) { - rasterizer->DrawLastLayerTree(); + rasterizer->DrawLastLayerTree(std::move(frame_timings_recorder)); } }); + + task_runners_.GetRasterTaskRunner()->PostTask(task); } // |Engine::Delegate|
diff --git a/shell/common/shell.h b/shell/common/shell.h index de24a19..53e1b83 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h
@@ -539,11 +539,13 @@ void OnAnimatorNotifyIdle(int64_t deadline) override; // |Animator::Delegate| - void OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, - fml::TimePoint frame_target_time) override; + void OnAnimatorDraw( + fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline, + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override; // |Animator::Delegate| - void OnAnimatorDrawLastLayerTree() override; + void OnAnimatorDrawLastLayerTree( + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder) override; // |Engine::Delegate| void OnEngineUpdateSemantics(
diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 00c12f0..5a4803e 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <memory> +#include "flow/frame_timings.h" #define FML_USED_ON_EMBEDDER #include "flutter/shell/common/shell_test.h" @@ -136,7 +138,10 @@ const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); - engine->animator_->BeginFrame(frame_begin_time, frame_end_time); + std::unique_ptr<FrameTimingsRecorder> recorder = + std::make_unique<FrameTimingsRecorder>(); + recorder->RecordVsync(frame_begin_time, frame_end_time); + engine->animator_->BeginFrame(std::move(recorder)); } latch.Signal(); }); @@ -175,7 +180,10 @@ const auto frame_begin_time = fml::TimePoint::Now(); const auto frame_end_time = frame_begin_time + fml::TimeDelta::FromSecondsF(1.0 / 60.0); - engine->animator_->BeginFrame(frame_begin_time, frame_end_time); + std::unique_ptr<FrameTimingsRecorder> recorder = + std::make_unique<FrameTimingsRecorder>(); + recorder->RecordVsync(frame_begin_time, frame_end_time); + engine->animator_->BeginFrame(std::move(recorder)); latch.Signal(); }); latch.Wait();
diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 40ced72..ede589f 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc
@@ -4,6 +4,7 @@ #include "flutter/shell/common/vsync_waiter.h" +#include "flow/frame_timings.h" #include "flutter/fml/task_runner.h" #include "flutter/fml/trace_event.h" #include "fml/message_loop_task_queues.h" @@ -132,7 +133,11 @@ pause_secondary_tasks]() { FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime", frame_start_time, "TargetTime", frame_target_time); - callback(frame_start_time, frame_target_time); + std::unique_ptr<FrameTimingsRecorder> frame_timings_recorder = + std::make_unique<FrameTimingsRecorder>(); + frame_timings_recorder->RecordVsync(frame_start_time, + frame_target_time); + callback(std::move(frame_timings_recorder)); TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier); if (pause_secondary_tasks) { ResumeDartMicroTasks();
diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index d593c70..6af0b8d 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h
@@ -11,6 +11,7 @@ #include <unordered_map> #include "flutter/common/task_runners.h" +#include "flutter/flow/frame_timings.h" #include "flutter/fml/time/time_point.h" namespace flutter { @@ -19,8 +20,7 @@ /// getting callbacks when a vsync event happens. class VsyncWaiter : public std::enable_shared_from_this<VsyncWaiter> { public: - using Callback = std::function<void(fml::TimePoint frame_start_time, - fml::TimePoint frame_target_time)>; + using Callback = std::function<void(std::unique_ptr<FrameTimingsRecorder>)>; virtual ~VsyncWaiter(); @@ -40,7 +40,7 @@ const TaskRunners task_runners_; - VsyncWaiter(TaskRunners task_runners); + explicit VsyncWaiter(TaskRunners task_runners); // Implementations are meant to override this method and arm their vsync // latches when in response to this invocation. On vsync, they are meant to