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

FrameTiming build start timestamp fix and add vsync start timestamp #20229

Merged
merged 8 commits into from
Aug 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,17 @@ namespace flutter {

class FrameTiming {
public:
enum Phase { kBuildStart, kBuildFinish, kRasterStart, kRasterFinish, kCount };

static constexpr Phase kPhases[kCount] = {kBuildStart, kBuildFinish,
kRasterStart, kRasterFinish};
enum Phase {
kVsyncStart,
kBuildStart,
kBuildFinish,
kRasterStart,
kRasterFinish,
kCount
};

static constexpr Phase kPhases[kCount] = {
kVsyncStart, kBuildStart, kBuildFinish, kRasterStart, kRasterFinish};

fml::TimePoint Get(Phase phase) const { return data_[phase]; }
fml::TimePoint Set(Phase phase, fml::TimePoint value) {
Expand Down
4 changes: 3 additions & 1 deletion flow/layers/layer_tree.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ LayerTree::LayerTree(const SkISize& frame_size,
checkerboard_raster_cache_images_(false),
checkerboard_offscreen_layers_(false) {}

void LayerTree::RecordBuildTime(fml::TimePoint build_start,
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();
Expand Down
7 changes: 6 additions & 1 deletion flow/layers/layer_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ class LayerTree {
float frame_physical_depth() const { return frame_physical_depth_; }
float frame_device_pixel_ratio() const { return frame_device_pixel_ratio_; }

void RecordBuildTime(fml::TimePoint build_start, fml::TimePoint target_time);
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_; }
Expand Down Expand Up @@ -83,6 +87,7 @@ class LayerTree {

private:
std::shared_ptr<Layer> root_layer_;
fml::TimePoint vsync_start_;
fml::TimePoint build_start_;
fml::TimePoint build_finish_;
fml::TimePoint target_time_;
Expand Down
2 changes: 1 addition & 1 deletion lib/ui/hooks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ void _reportTimings(List<int> timings) {
assert(timings.length % FramePhase.values.length == 0);
final List<FrameTiming> frameTimings = <FrameTiming>[];
for (int i = 0; i < timings.length; i += FramePhase.values.length) {
frameTimings.add(FrameTiming(timings.sublist(i, i + FramePhase.values.length)));
frameTimings.add(FrameTiming._(timings.sublist(i, i + FramePhase.values.length)));
}
_invoke1(window.onReportTimings, window._onReportTimingsZone, frameTimings);
}
Expand Down
43 changes: 37 additions & 6 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ typedef _SetNeedsReportTimingsFunc = void Function(bool value);
///
/// [FrameTiming] records a timestamp of each phase for performance analysis.
enum FramePhase {
/// The timestamp of the vsync signal given by the operating system.
///
/// See also [FrameTiming.vsyncOverhead].
vsyncStart,

/// When the UI thread starts building a frame.
///
/// See also [FrameTiming.buildDuration].
Expand Down Expand Up @@ -82,14 +87,34 @@ enum FramePhase {
/// Therefore it's recommended to only monitor and analyze performance metrics
/// in profile and release modes.
class FrameTiming {
/// Construct [FrameTiming] with raw timestamps in microseconds.
///
/// This constructor is used for unit test only. Real [FrameTiming]s should
/// be retrieved from [Window.onReportTimings].
factory FrameTiming({
required int vsyncStart,
required int buildStart,
required int buildFinish,
required int rasterStart,
required int rasterFinish,
}) {
return FrameTiming._(<int>[
vsyncStart,
buildStart,
buildFinish,
rasterStart,
rasterFinish
]);
}

/// Construct [FrameTiming] with raw timestamps in microseconds.
///
/// List [timestamps] must have the same number of elements as
/// [FramePhase.values].
///
/// This constructor is usually only called by the Flutter engine, or a test.
/// To get the [FrameTiming] of your app, see [Window.onReportTimings].
FrameTiming(List<int> timestamps)
FrameTiming._(List<int> timestamps)
: assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps;

/// Construct [FrameTiming] with given timestamp in micrseconds.
Expand All @@ -106,7 +131,9 @@ class FrameTiming {
required int rasterStart,
required int rasterFinish
}) {
return FrameTiming(<int>[
return FrameTiming._(<int>[
// This is for temporarily backward compatiblilty.
vsyncStart ?? buildStart,
buildStart,
buildFinish,
rasterStart,
Expand Down Expand Up @@ -143,22 +170,26 @@ class FrameTiming {
/// {@macro dart.ui.FrameTiming.fps_milliseconds}
Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart);

/// The timespan between build start and raster finish.
/// The duration between receiving the vsync signal and starting building the
/// frame.
Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart);

/// The timespan between vsync start and raster finish.
///
/// To achieve the lowest latency on an X fps display, this should not exceed
/// 1000/X milliseconds.
/// {@macro dart.ui.FrameTiming.fps_milliseconds}
///
/// See also [buildDuration] and [rasterDuration].
Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.buildStart);
/// See also [vsyncOverhead], [buildDuration] and [rasterDuration].
Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart);

final List<int> _timestamps; // in microseconds

String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms';

@override
String toString() {
return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, totalSpan: ${_formatMS(totalSpan)})';
return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)})';
}
}

Expand Down
55 changes: 46 additions & 9 deletions lib/web_ui/lib/src/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,11 @@ class IsolateNameServer {
///
/// [FrameTiming] records a timestamp of each phase for performance analysis.
enum FramePhase {
/// The timestamp of the vsync signal given by the operating system.
///
/// See also [FrameTiming.vsyncOverhead].
vsyncStart,

/// When the UI thread starts building a frame.
///
/// See also [FrameTiming.buildDuration].
Expand Down Expand Up @@ -1035,16 +1040,35 @@ enum FramePhase {
/// Therefore it's recommended to only monitor and analyze performance metrics
/// in profile and release modes.
class FrameTiming {
/// Construct [FrameTiming] with raw timestamps in microseconds.
///
/// This constructor is used for unit test only. Real [FrameTiming]s should
/// be retrieved from [Window.onReportTimings].
factory FrameTiming({
required int vsyncStart,
required int buildStart,
required int buildFinish,
required int rasterStart,
required int rasterFinish,
}) {
return FrameTiming._(<int>[
vsyncStart,
buildStart,
buildFinish,
rasterStart,
rasterFinish
]);
}

/// Construct [FrameTiming] with raw timestamps in microseconds.
///
/// List [timestamps] must have the same number of elements as
/// [FramePhase.values].
///
/// This constructor is usually only called by the Flutter engine, or a test.
/// To get the [FrameTiming] of your app, see [Window.onReportTimings].
FrameTiming(List<int> timestamps)
: assert(timestamps.length == FramePhase.values.length),
_timestamps = timestamps;
FrameTiming._(List<int> timestamps)
: assert(timestamps.length == FramePhase.values.length), _timestamps = timestamps;

/// Construct [FrameTiming] with given timestamp in micrseconds.
///
Expand All @@ -1053,13 +1077,22 @@ class FrameTiming {
///
/// TODO(CareF): This is part of #20229. Remove back to default constructor
/// after #20229 lands and corresponding framwork PRs land.
FrameTiming.fromTimeStamps({
factory FrameTiming.fromTimeStamps({
int? vsyncStart,
required int buildStart,
required int buildFinish,
required int rasterStart,
required int rasterFinish
}) : _timestamps = <int>[buildStart, buildFinish, rasterStart, rasterFinish];
}) {
return FrameTiming._(<int>[
// This is for temporarily backward compatiblilty.
vsyncStart ?? buildStart,
buildStart,
buildFinish,
rasterStart,
rasterFinish
]);
}

/// This is a raw timestamp in microseconds from some epoch. The epoch in all
/// [FrameTiming] is the same, but it may not match [DateTime]'s epoch.
Expand Down Expand Up @@ -1095,24 +1128,28 @@ class FrameTiming {
_rawDuration(FramePhase.rasterFinish) -
_rawDuration(FramePhase.rasterStart);

/// The timespan between build start and raster finish.
/// The duration between receiving the vsync signal and starting building the
/// frame.
Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart);

/// The timespan between vsync start and raster finish.
///
/// To achieve the lowest latency on an X fps display, this should not exceed
/// 1000/X milliseconds.
/// {@macro dart.ui.FrameTiming.fps_milliseconds}
///
/// See also [buildDuration] and [rasterDuration].
/// See also [vsyncOverhead], [buildDuration] and [rasterDuration].
Duration get totalSpan =>
_rawDuration(FramePhase.rasterFinish) -
_rawDuration(FramePhase.buildStart);
_rawDuration(FramePhase.vsyncStart);

final List<int> _timestamps; // in microseconds

String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms';

@override
String toString() {
return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, totalSpan: ${_formatMS(totalSpan)})';
return '$runtimeType(buildDuration: ${_formatMS(buildDuration)}, rasterDuration: ${_formatMS(rasterDuration)}, vsyncOverhead: ${_formatMS(vsyncOverhead)}, totalSpan: ${_formatMS(totalSpan)})';
}
}

Expand Down
16 changes: 11 additions & 5 deletions shell/common/animator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Animator::Animator(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 FLUTTER_SHELL_ENABLE_METAL
Expand Down Expand Up @@ -98,7 +99,7 @@ static int64_t FxlToDartOrEarlier(fml::TimePoint time) {
return (time - fxl_now).ToMicroseconds() + dart_now;
}

void Animator::BeginFrame(fml::TimePoint frame_start_time,
void Animator::BeginFrame(fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time) {
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);

Expand Down Expand Up @@ -133,7 +134,11 @@ void Animator::BeginFrame(fml::TimePoint frame_start_time,
// to service potential frame.
FML_DCHECK(producer_continuation_);

last_frame_begin_time_ = frame_start_time;
last_frame_begin_time_ = fml::TimePoint::Now();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when we post a trace event for VsyncSchedulingOverhead we also use fml::TimePoint::Now(). Can we unify both these and maybe post a trace event here instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure I understand but I made a commit moving the TaceEventAsyncComplete line. PTAL.

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;
dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
{
Expand Down Expand Up @@ -179,7 +184,8 @@ void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
last_layer_tree_size_ = layer_tree->frame_size();

// Note the frame time for instrumentation.
layer_tree->RecordBuildTime(last_frame_begin_time_, last_frame_target_time_);
layer_tree->RecordBuildTime(last_vsync_start_time_, last_frame_begin_time_,
last_frame_target_time_);

// Commit the pending continuation.
bool result = producer_continuation_.Complete(std::move(layer_tree));
Expand Down Expand Up @@ -233,13 +239,13 @@ void Animator::RequestFrame(bool regenerate_layer_tree) {

void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
[self = weak_factory_.GetWeakPtr()](fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time) {
if (self) {
if (self->CanReuseLastLayerTree()) {
self->DrawLastLayerTree();
} else {
self->BeginFrame(frame_start_time, frame_target_time);
self->BeginFrame(vsync_start_time, frame_target_time);
}
}
});
Expand Down
1 change: 1 addition & 0 deletions shell/common/animator.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class Animator final {
std::shared_ptr<VsyncWaiter> waiter_;

fml::TimePoint last_frame_begin_time_;
fml::TimePoint last_vsync_start_time_;
fml::TimePoint last_frame_target_time_;
int64_t dart_frame_deadline_;
fml::RefPtr<LayerTreePipeline> layer_tree_pipeline_;
Expand Down
1 change: 1 addition & 0 deletions shell/common/rasterizer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ RasterStatus Rasterizer::DoDraw(
#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());
Expand Down
3 changes: 0 additions & 3 deletions shell/common/vsync_waiter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,6 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time,
[callback, flow_identifier, frame_start_time, frame_target_time]() {
FML_TRACE_EVENT("flutter", kVsyncTraceName, "StartTime",
frame_start_time, "TargetTime", frame_target_time);
fml::tracing::TraceEventAsyncComplete(
"flutter", "VsyncSchedulingOverhead", fml::TimePoint::Now(),
frame_start_time);
callback(frame_start_time, frame_target_time);
TRACE_FLOW_END("flutter", kVsyncFlowName, flow_identifier);
},
Expand Down
10 changes: 8 additions & 2 deletions testing/dart/window_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,14 @@ void main() {
});

test('FrameTiming.toString has the correct format', () {
final FrameTiming timing = FrameTiming(<int>[1000, 8000, 9000, 19500]);
expect(timing.toString(), 'FrameTiming(buildDuration: 7.0ms, rasterDuration: 10.5ms, totalSpan: 18.5ms)');
final FrameTiming timing = FrameTiming(
vsyncStart: 500,
buildStart: 1000,
buildFinish: 8000,
rasterStart: 9000,
rasterFinish: 19500
);
expect(timing.toString(), 'FrameTiming(buildDuration: 7.0ms, rasterDuration: 10.5ms, vsyncOverhead: 0.5ms, totalSpan: 19.0ms)');
});

test('computePlatformResolvedLocale basic', () {
Expand Down