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