15
15
#include " flutter/fml/memory/task_runner_checker.h"
16
16
#include " flutter/fml/trace_event.h"
17
17
18
+ @interface VSyncClient ()
19
+ @property (nonatomic , assign , readonly ) double refreshRate;
20
+ @end
21
+
18
22
// When calculating refresh rate diffrence, anything within 0.1 fps is ignored.
19
23
const static double kRefreshRateDiffToIgnore = 0.1 ;
20
24
27
31
const fml::TimePoint target_time = recorder->GetVsyncTargetTime ();
28
32
FireCallback (start_time, target_time, true );
29
33
};
30
- client_ =
31
- fml::scoped_nsobject{[[VSyncClient alloc ] initWithTaskRunner: task_runners_.GetUITaskRunner ()
32
- callback: callback]};
33
- max_refresh_rate_ = [DisplayLinkManager displayRefreshRate ];
34
+ client_ = [[VSyncClient alloc ] initWithTaskRunner: task_runners_.GetUITaskRunner ()
35
+ callback: callback];
36
+ max_refresh_rate_ = DisplayLinkManager.displayRefreshRate ;
34
37
}
35
38
36
39
VsyncWaiterIOS::~VsyncWaiterIOS () {
37
40
// This way, we will get no more callbacks from the display link that holds a weak (non-nilling)
38
41
// reference to this C++ object.
39
- [client_. get () invalidate ];
42
+ [client_ invalidate ];
40
43
}
41
44
42
45
void VsyncWaiterIOS::AwaitVSync () {
43
- double new_max_refresh_rate = [ DisplayLinkManager displayRefreshRate ] ;
46
+ double new_max_refresh_rate = DisplayLinkManager. displayRefreshRate ;
44
47
if (fabs (new_max_refresh_rate - max_refresh_rate_) > kRefreshRateDiffToIgnore ) {
45
48
max_refresh_rate_ = new_max_refresh_rate;
46
- [client_. get () setMaxRefreshRate: max_refresh_rate_];
49
+ [client_ setMaxRefreshRate: max_refresh_rate_];
47
50
}
48
- [client_. get () await ];
51
+ [client_ await ];
49
52
}
50
53
51
54
// |VariableRefreshRateReporter|
52
55
double VsyncWaiterIOS::GetRefreshRate () const {
53
- return [client_.get () getRefreshRate ];
54
- }
55
-
56
- fml::scoped_nsobject<VSyncClient> VsyncWaiterIOS::GetVsyncClient () const {
57
- return client_;
56
+ return client_.refreshRate ;
58
57
}
59
58
60
59
} // namespace flutter
61
60
62
61
@implementation VSyncClient {
63
- flutter::VsyncWaiter::Callback callback_;
64
- fml::scoped_nsobject<CADisplayLink > display_link_;
65
- double current_refresh_rate_;
62
+ flutter::VsyncWaiter::Callback _callback;
63
+ CADisplayLink * _displayLink;
66
64
}
67
65
68
66
- (instancetype )initWithTaskRunner : (fml::RefPtr<fml::TaskRunner>)task_runner
69
67
callback : (flutter::VsyncWaiter::Callback)callback {
70
68
self = [super init ];
71
69
72
70
if (self) {
73
- current_refresh_rate_ = [ DisplayLinkManager displayRefreshRate ] ;
71
+ _refreshRate = DisplayLinkManager. displayRefreshRate ;
74
72
_allowPauseAfterVsync = YES ;
75
- callback_ = std::move (callback);
76
- display_link_ = fml::scoped_nsobject<CADisplayLink > {
77
- [[CADisplayLink displayLinkWithTarget: self selector: @selector (onDisplayLink: )] retain ]
78
- };
79
- display_link_.get ().paused = YES ;
80
-
81
- [self setMaxRefreshRate: [DisplayLinkManager displayRefreshRate ]];
82
-
83
- task_runner->PostTask ([client = [self retain ]]() {
84
- [client->display_link_.get () addToRunLoop: [NSRunLoop currentRunLoop ]
85
- forMode: NSRunLoopCommonModes ];
86
- [client release ];
73
+ _callback = std::move (callback);
74
+ _displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector (onDisplayLink: )];
75
+ _displayLink.paused = YES ;
76
+
77
+ [self setMaxRefreshRate: DisplayLinkManager.displayRefreshRate];
78
+
79
+ // Strongly retain the the captured link until it is added to the runloop.
80
+ CADisplayLink * localDisplayLink = _displayLink;
81
+ task_runner->PostTask ([localDisplayLink]() {
82
+ [localDisplayLink addToRunLoop: NSRunLoop .currentRunLoop forMode: NSRunLoopCommonModes ];
87
83
});
88
84
}
89
85
@@ -97,19 +93,19 @@ - (void)setMaxRefreshRate:(double)refreshRate {
97
93
double maxFrameRate = fmax (refreshRate, 60 );
98
94
double minFrameRate = fmax (maxFrameRate / 2 , 60 );
99
95
if (@available (iOS 15.0 , *)) {
100
- display_link_. get () .preferredFrameRateRange =
96
+ _displayLink .preferredFrameRateRange =
101
97
CAFrameRateRangeMake (minFrameRate, maxFrameRate, maxFrameRate);
102
98
} else {
103
- display_link_. get () .preferredFramesPerSecond = maxFrameRate;
99
+ _displayLink .preferredFramesPerSecond = maxFrameRate;
104
100
}
105
101
}
106
102
107
103
- (void )await {
108
- display_link_. get () .paused = NO ;
104
+ _displayLink .paused = NO ;
109
105
}
110
106
111
107
- (void )pause {
112
- display_link_. get () .paused = YES ;
108
+ _displayLink .paused = YES ;
113
109
}
114
110
115
111
- (void )onDisplayLink : (CADisplayLink *)link {
@@ -126,44 +122,33 @@ - (void)onDisplayLink:(CADisplayLink*)link {
126
122
std::unique_ptr<flutter::FrameTimingsRecorder> recorder =
127
123
std::make_unique<flutter::FrameTimingsRecorder>();
128
124
129
- current_refresh_rate_ = round (1 / (frame_target_time - frame_start_time).ToSecondsF ());
125
+ _refreshRate = round (1 / (frame_target_time - frame_start_time).ToSecondsF ());
130
126
131
127
recorder->RecordVsync (frame_start_time, frame_target_time);
132
128
if (_allowPauseAfterVsync) {
133
- display_link_. get () .paused = YES ;
129
+ link .paused = YES ;
134
130
}
135
- callback_ (std::move (recorder));
131
+ _callback (std::move (recorder));
136
132
}
137
133
138
134
- (void )invalidate {
139
- [display_link_.get () invalidate ];
140
- }
141
-
142
- - (void )dealloc {
143
- [self invalidate ];
144
-
145
- [super dealloc ];
146
- }
147
-
148
- - (double )getRefreshRate {
149
- return current_refresh_rate_;
135
+ [_displayLink invalidate ];
136
+ _displayLink = nil ; // Break retain cycle.
150
137
}
151
138
152
139
- (CADisplayLink *)getDisplayLink {
153
- return display_link_. get () ;
140
+ return _displayLink ;
154
141
}
155
142
156
143
@end
157
144
158
145
@implementation DisplayLinkManager
159
146
160
147
+ (double )displayRefreshRate {
161
- fml::scoped_nsobject<CADisplayLink > display_link = fml::scoped_nsobject<CADisplayLink > {
162
- [[CADisplayLink displayLinkWithTarget: [[[DisplayLinkManager alloc ] init ] autorelease ]
163
- selector: @selector (onDisplayLink: )] retain ]
164
- };
165
- display_link.get ().paused = YES ;
166
- auto preferredFPS = display_link.get ().preferredFramesPerSecond ;
148
+ CADisplayLink * displayLink = [CADisplayLink displayLinkWithTarget: [[[self class ] alloc ] init ]
149
+ selector: @selector (onDisplayLink: )];
150
+ displayLink.paused = YES ;
151
+ auto preferredFPS = displayLink.preferredFramesPerSecond ;
167
152
168
153
// From Docs:
169
154
// The default value for preferredFramesPerSecond is 0. When this value is 0, the preferred
@@ -174,15 +159,15 @@ + (double)displayRefreshRate {
174
159
return preferredFPS;
175
160
}
176
161
177
- return [ UIScreen mainScreen ] .maximumFramesPerSecond ;
162
+ return UIScreen. mainScreen .maximumFramesPerSecond ;
178
163
}
179
164
180
165
- (void )onDisplayLink : (CADisplayLink *)link {
181
166
// no-op.
182
167
}
183
168
184
169
+ (BOOL )maxRefreshRateEnabledOnIPhone {
185
- return [[[ NSBundle mainBundle ] objectForInfoDictionaryKey: @" CADisableMinimumFrameDurationOnPhone" ]
170
+ return [[NSBundle . mainBundle objectForInfoDictionaryKey: @" CADisableMinimumFrameDurationOnPhone" ]
186
171
boolValue ];
187
172
}
188
173
0 commit comments