Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
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
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ FILE: ../../../flutter/shell/common/display.cc
FILE: ../../../flutter/shell/common/display.h
FILE: ../../../flutter/shell/common/display_manager.cc
FILE: ../../../flutter/shell/common/display_manager.h
FILE: ../../../flutter/shell/common/display_manager_unittests.cc
FILE: ../../../flutter/shell/common/engine.cc
FILE: ../../../flutter/shell/common/engine.h
FILE: ../../../flutter/shell/common/engine_unittests.cc
Expand Down
1 change: 1 addition & 0 deletions shell/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ if (enable_unittests) {
sources = [
"animator_unittests.cc",
"canvas_spy_unittests.cc",
"display_manager_unittests.cc",
"engine_unittests.cc",
"input_events_unittests.cc",
"persistent_cache_unittests.cc",
Expand Down
8 changes: 5 additions & 3 deletions shell/common/animator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,10 @@ void Animator::BeginFrame(
// We have acquired a valid continuation from the pipeline and are ready
// to service potential frame.
FML_DCHECK(producer_continuation_);
const fml::TimePoint vsync_start_time =
frame_timings_recorder_->GetVsyncStartTime();
fml::tracing::TraceEventAsyncComplete(
"flutter", "VsyncSchedulingOverhead",
frame_timings_recorder_->GetVsyncStartTime(),
"flutter", "VsyncSchedulingOverhead", vsync_start_time,
frame_timings_recorder_->GetBuildStartTime());
const fml::TimePoint frame_target_time =
frame_timings_recorder_->GetVsyncTargetTime();
Expand All @@ -144,7 +145,8 @@ void Animator::BeginFrame(
TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
FrameParity());
uint64_t frame_number = frame_timings_recorder_->GetFrameNumber();
delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number);
delegate_.OnAnimatorBeginFrame(vsync_start_time, frame_target_time,
frame_number);
}

if (!frame_scheduled_ && has_rendered_) {
Expand Down
3 changes: 2 additions & 1 deletion shell/common/animator.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class Animator final {
public:
class Delegate {
public:
virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
virtual void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time,
uint64_t frame_number) = 0;

virtual void OnAnimatorNotifyIdle(int64_t deadline) = 0;
Expand Down
3 changes: 2 additions & 1 deletion shell/common/animator_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ namespace testing {

class FakeAnimatorDelegate : public Animator::Delegate {
public:
void OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time,
uint64_t frame_number) override {}

void OnAnimatorNotifyIdle(int64_t deadline) override {
Expand Down
25 changes: 25 additions & 0 deletions shell/common/display_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ void DisplayManager::HandleDisplayUpdates(
FML_CHECK(displays_.empty());
displays_ = std::move(displays);
return;
case DisplayUpdateType::kUpdateRefreshRate:
displays_.insert(displays_.begin(),
std::make_move_iterator(displays.begin()),
std::make_move_iterator(displays.end()));
break;
default:
FML_CHECK(false) << "Unknown DisplayUpdateType.";
}
Expand All @@ -47,4 +52,24 @@ void DisplayManager::CheckDisplayConfiguration(
}
}

void DisplayManager::UpdateRefreshRateIfNecessary(
DisplayUpdateType update_type,
fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time) {
auto is_frame_rate_same = [](double fr1, double fr2) {
const double kRefreshRateCompareEpsilon = 1;
return fabs(fr1 - fr2) < kRefreshRateCompareEpsilon;
};

double new_frame_rate =
round((1 / (frame_target_time - vsync_start_time).ToSecondsF()));
if (is_frame_rate_same(GetMainDisplayRefreshRate(), new_frame_rate)) {
return;
}

std::vector<std::unique_ptr<flutter::Display>> displays;
displays.push_back(std::make_unique<flutter::Display>(new_frame_rate));
HandleDisplayUpdates(update_type, std::move(displays));
}

} // namespace flutter
16 changes: 15 additions & 1 deletion shell/common/display_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
#ifndef FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_
#define FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_

#include <math.h>
#include <mutex>
#include <vector>

#include "flutter/fml/time/time_point.h"
#include "flutter/shell/common/display.h"

namespace flutter {
Expand All @@ -20,7 +22,11 @@ enum class DisplayUpdateType {
/// 1. The frame buffer hardware is connected.
/// 2. The display is drawable, e.g. it isn't being mirrored from another
/// connected display or sleeping.
kStartup
kStartup,

/// The `flutter::Display` that were active and a new frame indicates that the
/// refresh rate might be updated.
kUpdateRefreshRate,
};

/// Manages lifecycle of the connected displays. This class is thread-safe.
Expand All @@ -42,6 +48,14 @@ class DisplayManager {
void HandleDisplayUpdates(DisplayUpdateType update_type,
std::vector<std::unique_ptr<Display>> displays);

/// Updates the frame rate indicated in the |DisplayManager| if necessary.
///
/// If the |DisplayManager| decides an update is required,
/// |GetMainDisplayRefreshRate| will return the updated value.
void UpdateRefreshRateIfNecessary(DisplayUpdateType update_type,
fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time);

private:
/// Guards `displays_` vector.
mutable std::mutex displays_mutex_;
Expand Down
60 changes: 60 additions & 0 deletions shell/common/display_manager_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "display.h"
#include "display_manager.h"

#include "flutter/fml/time/time_point.h"

#include "gtest/gtest.h"

namespace flutter {
namespace testing {

TEST(DisplayManagerTest, SetsDisplayOnStartup) {
DisplayManager manager;
const double refresh_rate = 60;
std::vector<std::unique_ptr<flutter::Display>> displays;
displays.push_back(std::make_unique<flutter::Display>(refresh_rate));
manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup,
std::move(displays));
ASSERT_EQ(manager.GetMainDisplayRefreshRate(), refresh_rate);
}

TEST(DisplayManagerTest, UpdatesDisplayIfTypeIsUpdateRefreshRate) {
DisplayManager manager;
const double refresh_rate = 60;
std::vector<std::unique_ptr<flutter::Display>> displays1;
displays1.push_back(std::make_unique<flutter::Display>(refresh_rate));
manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup,
std::move(displays1));

const double new_refresh_rate = 120;
std::vector<std::unique_ptr<flutter::Display>> displays2;
displays2.push_back(std::make_unique<flutter::Display>(new_refresh_rate));
manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kUpdateRefreshRate,
std::move(displays2));
ASSERT_EQ(manager.GetMainDisplayRefreshRate(), new_refresh_rate);
}

TEST(DisplayManagerTest, UpdatesDisplayIfNewlyCalculatedRefreshRateChanged) {
DisplayManager manager;
const double refresh_rate = 60;
std::vector<std::unique_ptr<flutter::Display>> displays;
displays.push_back(std::make_unique<flutter::Display>(refresh_rate));
manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup,
std::move(displays));
fml::TimePoint vsync_start =
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMilliseconds(0));
fml::TimePoint vsync_target =
fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMilliseconds(8));

manager.UpdateRefreshRateIfNecessary(
flutter::DisplayUpdateType::kUpdateRefreshRate, vsync_start,
vsync_target);

const double kRefreshRateCompareEpsilon = 0.1;
ASSERT_NEAR(manager.GetMainDisplayRefreshRate(),
round(1 / (vsync_target - vsync_start).ToSecondsF()),
kRefreshRateCompareEpsilon);
}

} // namespace testing
} // namespace flutter
7 changes: 6 additions & 1 deletion shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1068,11 +1068,16 @@ void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) {
}

// |Animator::Delegate|
void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
void Shell::OnAnimatorBeginFrame(fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time,
uint64_t frame_number) {
FML_DCHECK(is_setup_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());

display_manager_->UpdateRefreshRateIfNecessary(
flutter::DisplayUpdateType::kUpdateRefreshRate, vsync_start_time,
frame_target_time);
Comment on lines +1077 to +1079
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the cost of this on iOS? We know for sure that on some Android platforms it's pretty expensive (e.g. 2-5ms) and this is during frameworkload.

Can we instead listen to refresh rate updates from the OS somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you elaborate how this is expensive on Android. This method is essentially a no-op if framerate doesn't change. See: https://github.com/flutter/engine/pull/30054/files#diff-c9141b5122b7cb9dcc29a05b1660dbb92c1c6ea8e0df831c96b42708fa1e20b5R66
(I should probably need to update the code to return early so it is clearer)


// record the target time for use by rasterizer.
{
std::scoped_lock time_recorder_lock(time_recorder_mutex_);
Expand Down
3 changes: 2 additions & 1 deletion shell/common/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,8 @@ class Shell final : public PlatformView::Delegate,
AssetResolver::AssetResolverType type) override;

// |Animator::Delegate|
void OnAnimatorBeginFrame(fml::TimePoint frame_target_time,
void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time,
fml::TimePoint frame_target_time,
uint64_t frame_number) override;

// |Animator::Delegate|
Expand Down