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 : VsyncWaiter(task_runners) {
25 auto callback = [
this](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {
26 const fml::TimePoint start_time = recorder->GetVsyncStartTime();
27 const fml::TimePoint target_time = recorder->GetVsyncTargetTime();
28 FireCallback(start_time, target_time,
true);
31 fml::scoped_nsobject{[[
VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner()
39 [client_.get() invalidate];
44 if (fml::TaskRunnerChecker::RunsOnTheSameThread(
45 task_runners_.GetRasterTaskRunner()->GetTaskQueueId(),
46 task_runners_.GetPlatformTaskRunner()->GetTaskQueueId())) {
47 BOOL isRunningOnMac = NO;
48 if (@available(iOS 14.0, *)) {
49 isRunningOnMac = [NSProcessInfo processInfo].iOSAppOnMac;
51 if (!isRunningOnMac) {
55 new_max_refresh_rate = 80;
59 max_refresh_rate_ = new_max_refresh_rate;
60 [client_.get() setMaxRefreshRate:max_refresh_rate_];
62 [client_.get() await];
67 return [client_.get() getRefreshRate];
77 flutter::VsyncWaiter::Callback callback_;
82 - (instancetype)initWithTaskRunner:(fml::RefPtr<fml::TaskRunner>)task_runner
83 callback:(
flutter::VsyncWaiter::Callback)callback {
88 _allowPauseAfterVsync = YES;
89 callback_ = std::move(callback);
91 [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain]
97 task_runner->PostTask([client = [
self retain]]() {
98 [client->display_link_.get() addToRunLoop:[NSRunLoop currentRunLoop]
99 forMode:NSRunLoopCommonModes];
107 - (void)setMaxRefreshRate:(
double)refreshRate {
111 double maxFrameRate = fmax(refreshRate, 60);
112 double minFrameRate = fmax(maxFrameRate / 2, 60);
113 if (@available(iOS 15.0, *)) {
115 CAFrameRateRangeMake(minFrameRate, maxFrameRate, maxFrameRate);
129 - (void)onDisplayLink:(CADisplayLink*)link {
130 CFTimeInterval delay = CACurrentMediaTime() - link.timestamp;
131 fml::TimePoint frame_start_time = fml::TimePoint::Now() - fml::TimeDelta::FromSecondsF(delay);
133 CFTimeInterval duration = link.targetTimestamp - link.timestamp;
134 fml::TimePoint frame_target_time = frame_start_time + fml::TimeDelta::FromSecondsF(duration);
136 TRACE_EVENT2_INT(
"flutter",
"PlatformVsync",
"frame_start_time",
137 frame_start_time.ToEpochDelta().ToMicroseconds(),
"frame_target_time",
138 frame_target_time.ToEpochDelta().ToMicroseconds());
140 std::unique_ptr<flutter::FrameTimingsRecorder> recorder =
141 std::make_unique<flutter::FrameTimingsRecorder>();
145 recorder->RecordVsync(frame_start_time, frame_target_time);
146 if (_allowPauseAfterVsync) {
149 callback_(std::move(recorder));
153 [display_link_.get() invalidate];
162 - (double)getRefreshRate {
166 - (CADisplayLink*)getDisplayLink {
175 fml::scoped_nsobject<CADisplayLink> display_link = fml::scoped_nsobject<CADisplayLink> {
177 selector:@selector(onDisplayLink:)] retain]
179 display_link.get().paused = YES;
180 auto preferredFPS = display_link.get().preferredFramesPerSecond;
187 if (preferredFPS != 0) {
191 return [UIScreen mainScreen].maximumFramesPerSecond;
194 - (void)onDisplayLink:(CADisplayLink*)link {
199 return [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CADisableMinimumFrameDurationOnPhone"]