9#include <Foundation/Foundation.h>
10#include <UIKit/UIKit.h>
11#include <mach/mach_time.h>
24@property(nonatomic, assign, readonly)
double refreshRate;
34 auto callback = [this](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {
39 client_ = [[
VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner()
41 max_refresh_rate_ = DisplayLinkManager.displayRefreshRate;
51 double new_max_refresh_rate = DisplayLinkManager.displayRefreshRate;
53 max_refresh_rate_ = new_max_refresh_rate;
61 return client_.refreshRate;
71- (instancetype)initWithTaskRunner:(
fml::RefPtr<
fml::TaskRunner>)task_runner
72 callback:(
flutter::VsyncWaiter::Callback)callback {
75 if (
self = [super init]) {
77 _allowPauseAfterVsync = YES;
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;
120 CFTimeInterval duration = link.targetTimestamp - link.timestamp;
121 fml::TimePoint frame_target_time = frame_start_time +
fml::TimeDelta::FromSecondsF(duration);
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]
std::function< void(std::unique_ptr< FrameTimingsRecorder >)> Callback
void FireCallback(fml::TimePoint frame_start_time, fml::TimePoint frame_target_time, bool pause_secondary_tasks=true)
~VsyncWaiterIOS() override
double GetRefreshRate() const override
void AwaitVSync() override
VsyncWaiterIOS(const flutter::TaskRunners &task_runners)
constexpr int64_t ToMicroseconds() const
constexpr TimeDelta ToEpochDelta() const
FlutterDesktopBinaryReply callback
#define FML_DCHECK(condition)
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.
#define TRACE_EVENT2_INT(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
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...