Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2e9de09

Browse files
authored
Streamline frame timings recording (#25892)
1 parent 0b4bf7e commit 2e9de09

22 files changed

+509
-121
lines changed

ci/firebase_testlab.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ gcloud --project flutter-infra firebase test android run \
3232
--timeout 2m \
3333
--results-bucket=gs://flutter_firebase_testlab \
3434
--results-dir="engine_scenario_test/$GIT_REVISION/$BUILD_ID" \
35-
--device model=flame,version=29
35+
--device model=flame,version=29

ci/licenses_golden/licenses_flutter

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ FILE: ../../../flutter/flow/embedded_views.h
4242
FILE: ../../../flutter/flow/flow_run_all_unittests.cc
4343
FILE: ../../../flutter/flow/flow_test_utils.cc
4444
FILE: ../../../flutter/flow/flow_test_utils.h
45+
FILE: ../../../flutter/flow/frame_timings.cc
46+
FILE: ../../../flutter/flow/frame_timings.h
47+
FILE: ../../../flutter/flow/frame_timings_recorder_unittests.cc
4548
FILE: ../../../flutter/flow/gl_context_switch_unittests.cc
4649
FILE: ../../../flutter/flow/instrumentation.cc
4750
FILE: ../../../flutter/flow/instrumentation.h

flow/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ source_set("flow") {
1414
"diff_context.h",
1515
"embedded_views.cc",
1616
"embedded_views.h",
17+
"frame_timings.cc",
18+
"frame_timings.h",
1719
"instrumentation.cc",
1820
"instrumentation.h",
1921
"layers/backdrop_filter_layer.cc",
@@ -143,6 +145,7 @@ if (enable_unittests) {
143145
"flow_run_all_unittests.cc",
144146
"flow_test_utils.cc",
145147
"flow_test_utils.h",
148+
"frame_timings_recorder_unittests.cc",
146149
"gl_context_switch_unittests.cc",
147150
"layers/backdrop_filter_layer_unittests.cc",
148151
"layers/checkerboard_layertree_unittests.cc",

flow/frame_timings.cc

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/flow/frame_timings.h"
6+
7+
#include <memory>
8+
9+
#include "flutter/common/settings.h"
10+
#include "flutter/fml/logging.h"
11+
12+
namespace flutter {
13+
14+
FrameTimingsRecorder::FrameTimingsRecorder() = default;
15+
16+
FrameTimingsRecorder::~FrameTimingsRecorder() = default;
17+
18+
fml::TimePoint FrameTimingsRecorder::GetVsyncStartTime() const {
19+
std::scoped_lock state_lock(state_mutex_);
20+
FML_DCHECK(state_ >= State::kVsync);
21+
return vsync_start_;
22+
}
23+
24+
fml::TimePoint FrameTimingsRecorder::GetVsyncTargetTime() const {
25+
std::scoped_lock state_lock(state_mutex_);
26+
FML_DCHECK(state_ >= State::kVsync);
27+
return vsync_target_;
28+
}
29+
30+
fml::TimePoint FrameTimingsRecorder::GetBuildStartTime() const {
31+
std::scoped_lock state_lock(state_mutex_);
32+
FML_DCHECK(state_ >= State::kBuildStart);
33+
return build_start_;
34+
}
35+
36+
fml::TimePoint FrameTimingsRecorder::GetBuildEndTime() const {
37+
std::scoped_lock state_lock(state_mutex_);
38+
FML_DCHECK(state_ >= State::kBuildEnd);
39+
return build_end_;
40+
}
41+
42+
fml::TimePoint FrameTimingsRecorder::GetRasterStartTime() const {
43+
std::scoped_lock state_lock(state_mutex_);
44+
FML_DCHECK(state_ >= State::kRasterStart);
45+
return raster_start_;
46+
}
47+
48+
fml::TimePoint FrameTimingsRecorder::GetRasterEndTime() const {
49+
std::scoped_lock state_lock(state_mutex_);
50+
FML_DCHECK(state_ >= State::kRasterEnd);
51+
return raster_end_;
52+
}
53+
54+
fml::TimeDelta FrameTimingsRecorder::GetBuildDuration() const {
55+
std::scoped_lock state_lock(state_mutex_);
56+
FML_DCHECK(state_ >= State::kBuildEnd);
57+
return build_end_ - build_start_;
58+
}
59+
60+
void FrameTimingsRecorder::RecordVsync(fml::TimePoint vsync_start,
61+
fml::TimePoint vsync_target) {
62+
std::scoped_lock state_lock(state_mutex_);
63+
FML_DCHECK(state_ == State::kUninitialized);
64+
state_ = State::kVsync;
65+
vsync_start_ = vsync_start;
66+
vsync_target_ = vsync_target;
67+
}
68+
69+
void FrameTimingsRecorder::RecordBuildStart(fml::TimePoint build_start) {
70+
std::scoped_lock state_lock(state_mutex_);
71+
FML_DCHECK(state_ == State::kVsync);
72+
state_ = State::kBuildStart;
73+
build_start_ = build_start;
74+
}
75+
76+
void FrameTimingsRecorder::RecordBuildEnd(fml::TimePoint build_end) {
77+
std::scoped_lock state_lock(state_mutex_);
78+
FML_DCHECK(state_ == State::kBuildStart);
79+
state_ = State::kBuildEnd;
80+
build_end_ = build_end;
81+
}
82+
83+
void FrameTimingsRecorder::RecordRasterStart(fml::TimePoint raster_start) {
84+
std::scoped_lock state_lock(state_mutex_);
85+
FML_DCHECK(state_ == State::kBuildEnd);
86+
state_ = State::kRasterStart;
87+
raster_start_ = raster_start;
88+
}
89+
90+
FrameTiming FrameTimingsRecorder::RecordRasterEnd(fml::TimePoint raster_end) {
91+
std::scoped_lock state_lock(state_mutex_);
92+
FML_DCHECK(state_ == State::kRasterStart);
93+
state_ = State::kRasterEnd;
94+
raster_end_ = raster_end;
95+
FrameTiming timing;
96+
timing.Set(FrameTiming::kVsyncStart, vsync_start_);
97+
timing.Set(FrameTiming::kBuildStart, build_start_);
98+
timing.Set(FrameTiming::kBuildFinish, build_end_);
99+
timing.Set(FrameTiming::kRasterStart, raster_start_);
100+
timing.Set(FrameTiming::kRasterFinish, raster_end_);
101+
return timing;
102+
}
103+
104+
std::unique_ptr<FrameTimingsRecorder> FrameTimingsRecorder::CloneUntil(
105+
State state) {
106+
std::scoped_lock state_lock(state_mutex_);
107+
std::unique_ptr<FrameTimingsRecorder> recorder =
108+
std::make_unique<FrameTimingsRecorder>();
109+
recorder->state_ = state;
110+
111+
if (state >= State::kVsync) {
112+
recorder->vsync_start_ = vsync_start_;
113+
recorder->vsync_target_ = vsync_target_;
114+
}
115+
116+
if (state >= State::kBuildStart) {
117+
recorder->build_start_ = build_start_;
118+
}
119+
120+
if (state >= State::kRasterEnd) {
121+
recorder->build_end_ = build_end_;
122+
}
123+
124+
if (state >= State::kRasterStart) {
125+
recorder->raster_start_ = raster_start_;
126+
}
127+
128+
if (state >= State::kRasterEnd) {
129+
recorder->raster_end_ = raster_end_;
130+
}
131+
132+
return recorder;
133+
}
134+
135+
} // namespace flutter

flow/frame_timings.h

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_FLOW_FRAME_TIMINGS_H_
6+
#define FLUTTER_FLOW_FRAME_TIMINGS_H_
7+
8+
#include <mutex>
9+
10+
#include "flutter/common/settings.h"
11+
#include "flutter/fml/macros.h"
12+
#include "flutter/fml/time/time_delta.h"
13+
#include "flutter/fml/time/time_point.h"
14+
15+
namespace flutter {
16+
17+
/// Records timestamps for various phases of a frame rendering process.
18+
///
19+
/// Recorder is created on vsync and destroyed after the rasterization of the
20+
/// frame. This class is thread safe and doesn't require additional
21+
/// synchronization.
22+
class FrameTimingsRecorder {
23+
public:
24+
/// Various states that the recorder can be in. When created the recorder is
25+
/// in an unitialized state and transtions in sequential order of the states.
26+
enum class State : uint32_t {
27+
kUninitialized,
28+
kVsync,
29+
kBuildStart,
30+
kBuildEnd,
31+
kRasterStart,
32+
kRasterEnd,
33+
};
34+
35+
/// Default constructor, initializes the recorder with State::kUninitialized.
36+
FrameTimingsRecorder();
37+
38+
~FrameTimingsRecorder();
39+
40+
/// Timestamp of the vsync signal.
41+
fml::TimePoint GetVsyncStartTime() const;
42+
43+
/// Timestamp of when the frame was targeted to be presented.
44+
///
45+
/// This is typically the next vsync signal timestamp.
46+
fml::TimePoint GetVsyncTargetTime() const;
47+
48+
/// Timestamp of when the frame building started.
49+
fml::TimePoint GetBuildStartTime() const;
50+
51+
/// Timestamp of when the frame was finished building.
52+
fml::TimePoint GetBuildEndTime() const;
53+
54+
/// Timestamp of when the frame rasterization started.
55+
fml::TimePoint GetRasterStartTime() const;
56+
57+
/// Timestamp of when the frame rasterization finished.
58+
fml::TimePoint GetRasterEndTime() const;
59+
60+
/// Duration of the frame build time.
61+
fml::TimeDelta GetBuildDuration() const;
62+
63+
/// Records a vsync event.
64+
void RecordVsync(fml::TimePoint vsync_start, fml::TimePoint vsync_target);
65+
66+
/// Records a build start event.
67+
void RecordBuildStart(fml::TimePoint build_start);
68+
69+
/// Records a build end event.
70+
void RecordBuildEnd(fml::TimePoint build_end);
71+
72+
/// Records a raster start event.
73+
void RecordRasterStart(fml::TimePoint raster_start);
74+
75+
/// Clones the recorder until (and including) the specified state.
76+
std::unique_ptr<FrameTimingsRecorder> CloneUntil(State state);
77+
78+
/// Records a raster end event, and builds a `FrameTiming` that summarizes all
79+
/// the events. This summary is sent to the framework.
80+
FrameTiming RecordRasterEnd(fml::TimePoint raster_end);
81+
82+
private:
83+
mutable std::mutex state_mutex_;
84+
State state_ = State::kUninitialized;
85+
86+
fml::TimePoint vsync_start_;
87+
fml::TimePoint vsync_target_;
88+
fml::TimePoint build_start_;
89+
fml::TimePoint build_end_;
90+
fml::TimePoint raster_start_;
91+
fml::TimePoint raster_end_;
92+
93+
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(FrameTimingsRecorder);
94+
};
95+
96+
} // namespace flutter
97+
98+
#endif // FLUTTER_FLOW_FRAME_TIMINGS_H_
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/flow/frame_timings.h"
6+
7+
#include "flutter/fml/time/time_delta.h"
8+
#include "flutter/fml/time/time_point.h"
9+
10+
#include "gtest/gtest.h"
11+
12+
namespace flutter {
13+
namespace testing {
14+
15+
TEST(FrameTimingsRecorderTest, RecordVsync) {
16+
auto recorder = std::make_unique<FrameTimingsRecorder>();
17+
const auto st = fml::TimePoint::Now();
18+
const auto en = st + fml::TimeDelta::FromMillisecondsF(16);
19+
recorder->RecordVsync(st, en);
20+
21+
ASSERT_EQ(st, recorder->GetVsyncStartTime());
22+
ASSERT_EQ(en, recorder->GetVsyncTargetTime());
23+
}
24+
25+
TEST(FrameTimingsRecorderTest, RecordBuildTimes) {
26+
auto recorder = std::make_unique<FrameTimingsRecorder>();
27+
28+
const auto st = fml::TimePoint::Now();
29+
const auto en = st + fml::TimeDelta::FromMillisecondsF(16);
30+
recorder->RecordVsync(st, en);
31+
32+
const auto build_start = fml::TimePoint::Now();
33+
const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16);
34+
recorder->RecordBuildStart(build_start);
35+
recorder->RecordBuildEnd(build_end);
36+
37+
ASSERT_EQ(build_start, recorder->GetBuildStartTime());
38+
ASSERT_EQ(build_end, recorder->GetBuildEndTime());
39+
}
40+
41+
TEST(FrameTimingsRecorderTest, RecordRasterTimes) {
42+
auto recorder = std::make_unique<FrameTimingsRecorder>();
43+
44+
const auto st = fml::TimePoint::Now();
45+
const auto en = st + fml::TimeDelta::FromMillisecondsF(16);
46+
recorder->RecordVsync(st, en);
47+
48+
const auto build_start = fml::TimePoint::Now();
49+
const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16);
50+
recorder->RecordBuildStart(build_start);
51+
recorder->RecordBuildEnd(build_end);
52+
53+
const auto raster_start = fml::TimePoint::Now();
54+
const auto raster_end = raster_start + fml::TimeDelta::FromMillisecondsF(16);
55+
recorder->RecordRasterStart(raster_start);
56+
recorder->RecordRasterEnd(raster_end);
57+
58+
ASSERT_EQ(raster_start, recorder->GetRasterStartTime());
59+
ASSERT_EQ(raster_end, recorder->GetRasterEndTime());
60+
}
61+
62+
// Windows and Fuchsia don't allow testing with killed by signal.
63+
#if !defined(OS_FUCHSIA) && !defined(OS_WIN) && \
64+
(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
65+
66+
TEST(FrameTimingsRecorderTest, ThrowWhenRecordBuildBeforeVsync) {
67+
auto recorder = std::make_unique<FrameTimingsRecorder>();
68+
69+
const auto build_start = fml::TimePoint::Now();
70+
EXPECT_EXIT(recorder->RecordBuildStart(build_start),
71+
::testing::KilledBySignal(SIGABRT),
72+
"Check failed: state_ == State::kVsync.");
73+
}
74+
75+
TEST(FrameTimingsRecorderTest, ThrowWhenRecordRasterBeforeBuildEnd) {
76+
auto recorder = std::make_unique<FrameTimingsRecorder>();
77+
78+
const auto st = fml::TimePoint::Now();
79+
const auto en = st + fml::TimeDelta::FromMillisecondsF(16);
80+
recorder->RecordVsync(st, en);
81+
82+
const auto raster_start = fml::TimePoint::Now();
83+
EXPECT_EXIT(recorder->RecordRasterStart(raster_start),
84+
::testing::KilledBySignal(SIGABRT),
85+
"Check failed: state_ == State::kBuildEnd.");
86+
}
87+
88+
#endif
89+
90+
} // namespace testing
91+
} // namespace flutter

flow/layers/layer_tree.cc

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
#include "flutter/flow/layers/layer_tree.h"
66

7+
#include "flutter/flow/frame_timings.h"
78
#include "flutter/flow/layers/layer.h"
9+
#include "flutter/fml/time/time_point.h"
810
#include "flutter/fml/trace_event.h"
911
#include "third_party/skia/include/core/SkPictureRecorder.h"
1012
#include "third_party/skia/include/utils/SkNWayCanvas.h"
@@ -20,15 +22,6 @@ LayerTree::LayerTree(const SkISize& frame_size, float device_pixel_ratio)
2022
FML_CHECK(device_pixel_ratio_ != 0.0f);
2123
}
2224

23-
void LayerTree::RecordBuildTime(fml::TimePoint vsync_start,
24-
fml::TimePoint build_start,
25-
fml::TimePoint target_time) {
26-
vsync_start_ = vsync_start;
27-
build_start_ = build_start;
28-
target_time_ = target_time;
29-
build_finish_ = fml::TimePoint::Now();
30-
}
31-
3225
bool LayerTree::Preroll(CompositorContext::ScopedFrame& frame,
3326
bool ignore_raster_cache) {
3427
TRACE_EVENT0("flutter", "LayerTree::Preroll");

0 commit comments

Comments
 (0)