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

[WIP] Support iOS Premotion refresh rate #30900

Closed
wants to merge 8 commits into from
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
6 changes: 6 additions & 0 deletions lib/ui/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,12 @@ class PlatformDispatcher {
/// scheduling of frames.
void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';

/// Update the refresh frame rate of the operating system.
///
/// The Flutter framework will call this function to tell engine the max frame rate
/// that the screen will take
void updateFrameRate(int frequency) native 'PlatformConfiguration_updateFrameRate';

/// Additional accessibility features that may be enabled by the platform.
AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures;

Expand Down
3 changes: 3 additions & 0 deletions lib/ui/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,9 @@ class SingletonFlutterWindow extends FlutterWindow {
/// scheduling of frames.
void scheduleFrame() => platformDispatcher.scheduleFrame();

/// Update the refresh frame rate of the operating system.
void updateFrameRate(int frequency) => platformDispatcher.updateFrameRate(frequency);

/// Whether the user has requested that [updateSemantics] be called when
/// the semantic contents of window changes.
///
Expand Down
14 changes: 14 additions & 0 deletions lib/ui/window/platform_configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ void ScheduleFrame(Dart_NativeArguments args) {
UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
}

void UpdateFrameRate(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
int64_t frequency =
tonic::DartConverter<int64_t>::FromArguments(args, 1, exception);
if (exception) {
Dart_ThrowException(exception);
return;
}
UIDartState::Current()->platform_configuration()->client()->UpdateFrameRate(
frequency);
}

void Render(Dart_NativeArguments args) {
UIDartState::ThrowIfUIOperationsProhibited();
Dart_Handle exception = nullptr;
Expand Down Expand Up @@ -454,6 +467,7 @@ void PlatformConfiguration::RegisterNatives(
natives->Register({
{"PlatformConfiguration_defaultRouteName", DefaultRouteName, 1, true},
{"PlatformConfiguration_scheduleFrame", ScheduleFrame, 1, true},
{"PlatformConfiguration_updateFrameRate", UpdateFrameRate, 2, true},
{"PlatformConfiguration_sendPlatformMessage", _SendPlatformMessage, 4,
true},
{"PlatformConfiguration_respondToPlatformMessage",
Expand Down
8 changes: 8 additions & 0 deletions lib/ui/window/platform_configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ class PlatformConfigurationClient {
///
virtual void UpdateSemantics(SemanticsUpdate* update) = 0;

//--------------------------------------------------------------------------
/// @brief Update the refresh frame rate for operating system
///
/// @param[in] frequency The frequency the operating system will take for
/// refresh rate
///
virtual void UpdateFrameRate(int64_t frequency) = 0;

//--------------------------------------------------------------------------
/// @brief When the Flutter application has a message to send to the
/// underlying platform, the message needs to be forwarded to
Expand Down
5 changes: 5 additions & 0 deletions runtime/runtime_controller.cc
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,11 @@ void RuntimeController::ScheduleFrame() {
client_.ScheduleFrame();
}

// |PlatformConfigurationClient|
void RuntimeController::UpdateFrameRate(int64_t frequency) {
client_.UpdateFrameRate(frequency);
}

// |PlatformConfigurationClient|
void RuntimeController::Render(Scene* scene) {
client_.Render(scene->takeLayerTree());
Expand Down
3 changes: 3 additions & 0 deletions runtime/runtime_controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,9 @@ class RuntimeController : public PlatformConfigurationClient {
// |PlatformConfigurationClient|
void ScheduleFrame() override;

// |PlatformConfigurationClient|
void UpdateFrameRate(int64_t frequency) override;

// |PlatformConfigurationClient|
void Render(Scene* scene) override;

Expand Down
2 changes: 2 additions & 0 deletions runtime/runtime_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class RuntimeDelegate {

virtual void ScheduleFrame(bool regenerate_layer_tree = true) = 0;

virtual void UpdateFrameRate(int64_t frequency) = 0;

virtual void Render(std::unique_ptr<flutter::LayerTree> layer_tree) = 0;

virtual void UpdateSemantics(SemanticsNodeUpdates update,
Expand Down
4 changes: 4 additions & 0 deletions shell/common/animator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ void Animator::RequestFrame(bool regenerate_layer_tree) {
frame_scheduled_ = true;
}

void Animator::UpdateFrameRate(int64_t frequency) {
waiter_->UpdateFrameRate(frequency);
}

void Animator::AwaitVSync() {
waiter_->AsyncWaitForVsync(
[self = weak_factory_.GetWeakPtr()](
Expand Down
2 changes: 2 additions & 0 deletions shell/common/animator.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ class Animator final {

void RequestFrame(bool regenerate_layer_tree = true);

void UpdateFrameRate(int64_t frequency);

void Render(std::unique_ptr<flutter::LayerTree> layer_tree);

const VsyncWaiter& GetVsyncWaiter() const;
Expand Down
4 changes: 4 additions & 0 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@ void Engine::ScheduleFrame(bool regenerate_layer_tree) {
animator_->RequestFrame(regenerate_layer_tree);
}

void Engine::UpdateFrameRate(int64_t frequency) {
animator_->UpdateFrameRate(frequency);
}

void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
if (!layer_tree) {
return;
Expand Down
3 changes: 3 additions & 0 deletions shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,9 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
/// tree.
void ScheduleFrame() { ScheduleFrame(true); }

// |RuntimeDelegate|
void UpdateFrameRate(int64_t frequency) override;

// |RuntimeDelegate|
FontCollection& GetFontCollection() override;

Expand Down
4 changes: 4 additions & 0 deletions shell/common/vsync_waiter.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class VsyncWaiter : public std::enable_shared_from_this<VsyncWaiter> {
/// |Animator::ScheduleMaybeClearTraceFlowIds|.
void ScheduleSecondaryCallback(uintptr_t id, const fml::closure& callback);

/// Update the screen's refresh rate,different platforms may have different
/// ways to implement this function to change refresh rate.
virtual void UpdateFrameRate(int64_t frequency){};

protected:
// On some backends, the |FireCallback| needs to be made from a static C
// method.
Expand Down
5 changes: 5 additions & 0 deletions shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@

- (double)getRefreshRate;

- (void)updateFrameRate:(int64_t)frequency;

@end

namespace flutter {
Expand All @@ -53,6 +55,9 @@ class VsyncWaiterIOS final : public VsyncWaiter, public VariableRefreshRateRepor
private:
fml::scoped_nsobject<VSyncClient> client_;

// |VsyncWaiter|
void UpdateFrameRate(int64_t frequency) override;

// |VsyncWaiter|
void AwaitVSync() override;

Expand Down
48 changes: 48 additions & 0 deletions shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@

namespace flutter {

const int64_t kNormalRate = -1;
const int64_t kMinRate = 0;
const int64_t kMaxRate = 2147483647;

VsyncWaiterIOS::VsyncWaiterIOS(flutter::TaskRunners task_runners)
: VsyncWaiter(std::move(task_runners)) {
auto callback = [this](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {
Expand All @@ -35,6 +39,10 @@
[client_.get() invalidate];
}

void VsyncWaiterIOS::UpdateFrameRate(int64_t frequency) {
[client_.get() updateFrameRate:frequency];
}

void VsyncWaiterIOS::AwaitVSync() {
[client_.get() await];
}
Expand Down Expand Up @@ -74,6 +82,46 @@ - (instancetype)initWithTaskRunner:(fml::RefPtr<fml::TaskRunner>)task_runner
return self;
}

- (void)updateFrameRate:(int64_t)rate {
float targetPreferredFrameRate = 60.0;
const float minFrameRate = 10.0;
const float normalFrameRate = 60.0;
const float maxFrameRate = fmax([DisplayLinkManager displayRefreshRate], 60.0);

switch (rate) {
case flutter::kNormalRate:
targetPreferredFrameRate = normalFrameRate;
break;
case flutter::kMinRate:
targetPreferredFrameRate = minFrameRate;
break;
case flutter::kMaxRate:
targetPreferredFrameRate = maxFrameRate;
break;
default:
targetPreferredFrameRate = fmax(rate, minFrameRate);
break;
}

assert(targetPreferredFrameRate >= minFrameRate);

float currentPreferredFrame = 0.0;
if (@available(iOS 15.0, *)) {
currentPreferredFrame = display_link_.get().preferredFrameRateRange.preferred;
if (currentPreferredFrame == targetPreferredFrameRate) {
return;
}
display_link_.get().preferredFrameRateRange =
CAFrameRateRangeMake(minFrameRate, maxFrameRate, targetPreferredFrameRate);
} else if (@available(iOS 10.0, *)) {
currentPreferredFrame = display_link_.get().preferredFramesPerSecond;
if (currentPreferredFrame == targetPreferredFrameRate) {
return;
}
display_link_.get().preferredFramesPerSecond = targetPreferredFrameRate;
}
}

- (void)await {
display_link_.get().paused = NO;
}
Expand Down