This repository was archived by the owner on Feb 25, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6k
Streamline frame timings recording #25892
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
// 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/frame_timings.h" | ||
|
||
#include <memory> | ||
|
||
#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_DCHECK(state_ >= State::kVsync); | ||
return vsync_start_; | ||
} | ||
|
||
fml::TimePoint FrameTimingsRecorder::GetVsyncTargetTime() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(state_ >= State::kVsync); | ||
return vsync_target_; | ||
} | ||
|
||
fml::TimePoint FrameTimingsRecorder::GetBuildStartTime() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(state_ >= State::kBuildStart); | ||
return build_start_; | ||
} | ||
|
||
fml::TimePoint FrameTimingsRecorder::GetBuildEndTime() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(state_ >= State::kBuildEnd); | ||
return build_end_; | ||
} | ||
|
||
fml::TimePoint FrameTimingsRecorder::GetRasterStartTime() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(state_ >= State::kRasterStart); | ||
return raster_start_; | ||
} | ||
|
||
fml::TimePoint FrameTimingsRecorder::GetRasterEndTime() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(state_ >= State::kRasterEnd); | ||
return raster_end_; | ||
} | ||
|
||
fml::TimeDelta FrameTimingsRecorder::GetBuildDuration() const { | ||
std::scoped_lock state_lock(state_mutex_); | ||
FML_DCHECK(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_DCHECK(state_ == State::kUninitialized); | ||
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_DCHECK(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_DCHECK(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_DCHECK(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_DCHECK(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; | ||
} | ||
|
||
std::unique_ptr<FrameTimingsRecorder> FrameTimingsRecorder::CloneUntil( | ||
State state) { | ||
std::scoped_lock state_lock(state_mutex_); | ||
std::unique_ptr<FrameTimingsRecorder> recorder = | ||
std::make_unique<FrameTimingsRecorder>(); | ||
recorder->state_ = state; | ||
|
||
if (state >= State::kVsync) { | ||
recorder->vsync_start_ = vsync_start_; | ||
recorder->vsync_target_ = vsync_target_; | ||
} | ||
|
||
if (state >= State::kBuildStart) { | ||
recorder->build_start_ = build_start_; | ||
} | ||
|
||
if (state >= State::kRasterEnd) { | ||
recorder->build_end_ = build_end_; | ||
} | ||
|
||
if (state >= State::kRasterStart) { | ||
recorder->raster_start_ = raster_start_; | ||
} | ||
|
||
if (state >= State::kRasterEnd) { | ||
recorder->raster_end_ = raster_end_; | ||
} | ||
|
||
return recorder; | ||
} | ||
|
||
} // namespace flutter |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// 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 { | ||
|
||
/// Records timestamps for various phases of a frame rendering process. | ||
/// | ||
/// Recorder is created on vsync and destroyed after the rasterization of the | ||
/// frame. This class is thread safe and doesn't require additional | ||
/// synchronization. | ||
class FrameTimingsRecorder { | ||
public: | ||
/// Various states that the recorder can be in. When created the recorder is | ||
/// in an unitialized state and transtions in sequential order of the states. | ||
enum class State : uint32_t { | ||
kUninitialized, | ||
kVsync, | ||
kBuildStart, | ||
kBuildEnd, | ||
kRasterStart, | ||
kRasterEnd, | ||
}; | ||
|
||
/// Default constructor, initializes the recorder with State::kUninitialized. | ||
FrameTimingsRecorder(); | ||
|
||
~FrameTimingsRecorder(); | ||
|
||
/// Timestamp of the vsync signal. | ||
fml::TimePoint GetVsyncStartTime() const; | ||
|
||
/// Timestamp of when the frame was targeted to be presented. | ||
/// | ||
/// This is typically the next vsync signal timestamp. | ||
fml::TimePoint GetVsyncTargetTime() const; | ||
|
||
/// Timestamp of when the frame building started. | ||
fml::TimePoint GetBuildStartTime() const; | ||
|
||
/// Timestamp of when the frame was finished building. | ||
fml::TimePoint GetBuildEndTime() const; | ||
|
||
/// Timestamp of when the frame rasterization started. | ||
fml::TimePoint GetRasterStartTime() const; | ||
|
||
/// Timestamp of when the frame rasterization finished. | ||
fml::TimePoint GetRasterEndTime() const; | ||
|
||
/// Duration of the frame build time. | ||
fml::TimeDelta GetBuildDuration() const; | ||
|
||
/// Records a vsync event. | ||
void RecordVsync(fml::TimePoint vsync_start, fml::TimePoint vsync_target); | ||
|
||
/// Records a build start event. | ||
void RecordBuildStart(fml::TimePoint build_start); | ||
|
||
/// Records a build end event. | ||
void RecordBuildEnd(fml::TimePoint build_end); | ||
|
||
/// Records a raster start event. | ||
void RecordRasterStart(fml::TimePoint raster_start); | ||
|
||
/// Clones the recorder until (and including) the specified state. | ||
std::unique_ptr<FrameTimingsRecorder> CloneUntil(State state); | ||
|
||
/// Records a raster end event, and builds a `FrameTiming` that summarizes all | ||
/// the events. This summary is sent to the framework. | ||
FrameTiming RecordRasterEnd(fml::TimePoint raster_end); | ||
|
||
private: | ||
mutable std::mutex state_mutex_; | ||
State state_ = State::kUninitialized; | ||
|
||
fml::TimePoint vsync_start_; | ||
fml::TimePoint vsync_target_; | ||
fml::TimePoint build_start_; | ||
fml::TimePoint build_end_; | ||
fml::TimePoint raster_start_; | ||
fml::TimePoint raster_end_; | ||
|
||
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(FrameTimingsRecorder); | ||
}; | ||
|
||
} // namespace flutter | ||
|
||
#endif // FLUTTER_FLOW_FRAME_TIMINGS_H_ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// 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/frame_timings.h" | ||
|
||
#include "flutter/fml/time/time_delta.h" | ||
#include "flutter/fml/time/time_point.h" | ||
|
||
#include "gtest/gtest.h" | ||
|
||
namespace flutter { | ||
namespace testing { | ||
|
||
TEST(FrameTimingsRecorderTest, RecordVsync) { | ||
auto recorder = std::make_unique<FrameTimingsRecorder>(); | ||
const auto st = fml::TimePoint::Now(); | ||
const auto en = st + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordVsync(st, en); | ||
|
||
ASSERT_EQ(st, recorder->GetVsyncStartTime()); | ||
ASSERT_EQ(en, recorder->GetVsyncTargetTime()); | ||
} | ||
|
||
TEST(FrameTimingsRecorderTest, RecordBuildTimes) { | ||
auto recorder = std::make_unique<FrameTimingsRecorder>(); | ||
|
||
const auto st = fml::TimePoint::Now(); | ||
const auto en = st + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordVsync(st, en); | ||
|
||
const auto build_start = fml::TimePoint::Now(); | ||
const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordBuildStart(build_start); | ||
recorder->RecordBuildEnd(build_end); | ||
|
||
ASSERT_EQ(build_start, recorder->GetBuildStartTime()); | ||
ASSERT_EQ(build_end, recorder->GetBuildEndTime()); | ||
} | ||
|
||
TEST(FrameTimingsRecorderTest, RecordRasterTimes) { | ||
auto recorder = std::make_unique<FrameTimingsRecorder>(); | ||
|
||
const auto st = fml::TimePoint::Now(); | ||
const auto en = st + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordVsync(st, en); | ||
|
||
const auto build_start = fml::TimePoint::Now(); | ||
const auto build_end = build_start + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordBuildStart(build_start); | ||
recorder->RecordBuildEnd(build_end); | ||
|
||
const auto raster_start = fml::TimePoint::Now(); | ||
const auto raster_end = raster_start + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordRasterStart(raster_start); | ||
recorder->RecordRasterEnd(raster_end); | ||
|
||
ASSERT_EQ(raster_start, recorder->GetRasterStartTime()); | ||
ASSERT_EQ(raster_end, recorder->GetRasterEndTime()); | ||
} | ||
|
||
// Windows and Fuchsia don't allow testing with killed by signal. | ||
#if !defined(OS_FUCHSIA) && !defined(OS_WIN) && \ | ||
(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) | ||
|
||
TEST(FrameTimingsRecorderTest, ThrowWhenRecordBuildBeforeVsync) { | ||
auto recorder = std::make_unique<FrameTimingsRecorder>(); | ||
|
||
const auto build_start = fml::TimePoint::Now(); | ||
EXPECT_EXIT(recorder->RecordBuildStart(build_start), | ||
::testing::KilledBySignal(SIGABRT), | ||
"Check failed: state_ == State::kVsync."); | ||
} | ||
|
||
TEST(FrameTimingsRecorderTest, ThrowWhenRecordRasterBeforeBuildEnd) { | ||
auto recorder = std::make_unique<FrameTimingsRecorder>(); | ||
|
||
const auto st = fml::TimePoint::Now(); | ||
const auto en = st + fml::TimeDelta::FromMillisecondsF(16); | ||
recorder->RecordVsync(st, en); | ||
|
||
const auto raster_start = fml::TimePoint::Now(); | ||
EXPECT_EXIT(recorder->RecordRasterStart(raster_start), | ||
::testing::KilledBySignal(SIGABRT), | ||
"Check failed: state_ == State::kBuildEnd."); | ||
} | ||
|
||
#endif | ||
|
||
} // namespace testing | ||
} // namespace flutter |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.