9 #include <Foundation/Foundation.h>
10 #include <UIKit/UIKit.h>
11 #include <mach/mach_time.h>
13 #include "flutter/common/task_runners.h"
14 #include "flutter/fml/logging.h"
15 #include "flutter/fml/memory/task_runner_checker.h"
16 #include "flutter/fml/trace_event.h"
24 @property(nonatomic, assign, readonly)
double refreshRate;
33 : VsyncWaiter(task_runners) {
34 auto callback = [this](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {
35 const fml::TimePoint start_time = recorder->GetVsyncStartTime();
36 const fml::TimePoint target_time = recorder->GetVsyncTargetTime();
37 FireCallback(start_time, target_time,
true);
39 client_ = [[
VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner()
53 max_refresh_rate_ = new_max_refresh_rate;
61 return client_.refreshRate;
67 flutter::VsyncWaiter::Callback _callback;
71 - (instancetype)initWithTaskRunner:(
fml::RefPtr<
fml::TaskRunner>)task_runner
72 callback:(
flutter::VsyncWaiter::Callback)callback {
73 FML_DCHECK(task_runner);
75 if (
self = [super init]) {
77 _allowPauseAfterVsync = YES;
78 _callback = std::move(callback);
79 _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
82 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate];
86 task_runner->PostTask([localDisplayLink]() {
87 [localDisplayLink addToRunLoop:NSRunLoop.currentRunLoop forMode:NSRunLoopCommonModes];
94 - (void)setMaxRefreshRate:(
double)refreshRate {
98 double maxFrameRate = fmax(refreshRate, 60);
99 double minFrameRate = fmax(maxFrameRate / 2, 60);
100 if (@available(iOS 15.0, *)) {
102 CAFrameRateRangeMake(minFrameRate, maxFrameRate, maxFrameRate);
116 - (void)onDisplayLink:(CADisplayLink*)link {
117 CFTimeInterval delay = CACurrentMediaTime() - link.timestamp;
118 fml::TimePoint frame_start_time = fml::TimePoint::Now() -
fml::TimeDelta::FromSecondsF(delay);
120 CFTimeInterval duration = link.targetTimestamp - link.timestamp;
121 fml::TimePoint frame_target_time = frame_start_time +
fml::TimeDelta::FromSecondsF(duration);
123 TRACE_EVENT2_INT(
"flutter",
"PlatformVsync",
"frame_start_time",
124 frame_start_time.ToEpochDelta().ToMicroseconds(),
"frame_target_time",
125 frame_target_time.ToEpochDelta().ToMicroseconds());
127 std::unique_ptr<flutter::FrameTimingsRecorder> recorder =
128 std::make_unique<flutter::FrameTimingsRecorder>();
130 _refreshRate = round(1 / (frame_target_time - frame_start_time).ToSecondsF());
132 recorder->RecordVsync(frame_start_time, frame_target_time);
133 if (_allowPauseAfterVsync) {
136 _callback(std::move(recorder));
140 [_displayLink invalidate];
144 - (CADisplayLink*)getDisplayLink {
153 CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:[[[
self class] alloc] init]
154 selector:@selector(onDisplayLink:)];
155 displayLink.paused = YES;
156 auto preferredFPS = displayLink.preferredFramesPerSecond;
163 if (preferredFPS != 0) {
167 return UIScreen.mainScreen.maximumFramesPerSecond;
170 - (void)onDisplayLink:(CADisplayLink*)link {
175 return [[NSBundle.mainBundle objectForInfoDictionaryKey:kCADisableMinimumFrameDurationOnPhoneKey]
~VsyncWaiterIOS() override
double GetRefreshRate() const override
void AwaitVSync() override
VsyncWaiterIOS(const flutter::TaskRunners &task_runners)
BOOL maxRefreshRateEnabledOnIPhone
Whether the max refresh rate on iPhone ProMotion devices are enabled. This reflects the value of CADi...
double displayRefreshRate
The display refresh rate used for reporting purposes. The engine does not care about this for frame s...
void setMaxRefreshRate:(double refreshRate)
void invalidate()
Call invalidate before releasing this object to remove from runloops.
CADisplayLink * _displayLink
static const double kRefreshRateDiffToIgnore
FLUTTER_ASSERT_ARC NSString *const kCADisableMinimumFrameDurationOnPhoneKey
Info.plist key enabling the full range of ProMotion refresh rates for CADisplayLink callbacks and CAA...