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

Commit 083937b

Browse files
committed
Migrate vsync_waiter_ios to ARC
1 parent 1a13c7d commit 083937b

File tree

4 files changed

+50
-45
lines changed

4 files changed

+50
-45
lines changed

shell/platform/darwin/ios/BUILD.gn

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,20 @@ source_set("flutter_framework_source_arc") {
7676
"framework/Source/KeyCodeMap_Internal.h",
7777
"framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h",
7878
"framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.mm",
79+
"framework/Source/vsync_waiter_ios.h",
80+
"framework/Source/vsync_waiter_ios.mm",
7981
]
8082

8183
frameworks = [
8284
"UIKit.framework",
8385
"IOSurface.framework",
8486
]
8587

86-
deps += [ "//flutter/shell/platform/embedder:embedder_as_internal_library" ]
88+
deps += [
89+
"//flutter/common:common",
90+
"//flutter/shell/common",
91+
"//flutter/shell/platform/embedder:embedder_as_internal_library",
92+
]
8793
}
8894

8995
source_set("flutter_framework_source") {
@@ -145,8 +151,6 @@ source_set("flutter_framework_source") {
145151
"framework/Source/platform_message_response_darwin.mm",
146152
"framework/Source/profiler_metrics_ios.h",
147153
"framework/Source/profiler_metrics_ios.mm",
148-
"framework/Source/vsync_waiter_ios.h",
149-
"framework/Source/vsync_waiter_ios.mm",
150154
"ios_context.h",
151155
"ios_context.mm",
152156
"ios_context_metal_impeller.h",

shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,4 +127,20 @@ - (void)testAwaitAndPauseWillWorkCorrectly {
127127
XCTAssertTrue(link.isPaused);
128128
}
129129

130+
- (void)testReleasesLinkOnInvalidation {
131+
__weak CADisplayLink* weakLink;
132+
@autoreleasepool {
133+
auto thread_task_runner = CreateNewThread("VsyncWaiterIosTest");
134+
VSyncClient* vsyncClient = [[VSyncClient alloc]
135+
initWithTaskRunner:thread_task_runner
136+
callback:[](std::unique_ptr<flutter::FrameTimingsRecorder> recorder) {}];
137+
138+
weakLink = [vsyncClient getDisplayLink];
139+
XCTAssertNotNil(weakLink);
140+
[vsyncClient invalidate];
141+
}
142+
// VSyncClient has released the CADisplayLink.
143+
XCTAssertNil(weakLink);
144+
}
145+
130146
@end

shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
#include <QuartzCore/CADisplayLink.h>
99

1010
#include "flutter/fml/macros.h"
11-
#include "flutter/fml/memory/weak_ptr.h"
12-
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
1311
#include "flutter/shell/common/variable_refresh_rate_reporter.h"
1412
#include "flutter/shell/common/vsync_waiter.h"
1513

@@ -73,15 +71,12 @@ class VsyncWaiterIOS final : public VsyncWaiter, public VariableRefreshRateRepor
7371
// |VariableRefreshRateReporter|
7472
double GetRefreshRate() const override;
7573

76-
// Made public for testing.
77-
fml::scoped_nsobject<VSyncClient> GetVsyncClient() const;
78-
7974
// |VsyncWaiter|
8075
// Made public for testing.
8176
void AwaitVSync() override;
8277

8378
private:
84-
fml::scoped_nsobject<VSyncClient> client_;
79+
VSyncClient* client_;
8580
double max_refresh_rate_;
8681

8782
FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterIOS);

shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm

Lines changed: 26 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,41 +27,36 @@
2727
const fml::TimePoint target_time = recorder->GetVsyncTargetTime();
2828
FireCallback(start_time, target_time, true);
2929
};
30-
client_ =
31-
fml::scoped_nsobject{[[VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner()
32-
callback:callback]};
30+
client_ = [[VSyncClient alloc] initWithTaskRunner:task_runners_.GetUITaskRunner()
31+
callback:callback];
3332
max_refresh_rate_ = [DisplayLinkManager displayRefreshRate];
3433
}
3534

3635
VsyncWaiterIOS::~VsyncWaiterIOS() {
3736
// This way, we will get no more callbacks from the display link that holds a weak (non-nilling)
3837
// reference to this C++ object.
39-
[client_.get() invalidate];
38+
[client_ invalidate];
4039
}
4140

4241
void VsyncWaiterIOS::AwaitVSync() {
4342
double new_max_refresh_rate = [DisplayLinkManager displayRefreshRate];
4443
if (fabs(new_max_refresh_rate - max_refresh_rate_) > kRefreshRateDiffToIgnore) {
4544
max_refresh_rate_ = new_max_refresh_rate;
46-
[client_.get() setMaxRefreshRate:max_refresh_rate_];
45+
[client_ setMaxRefreshRate:max_refresh_rate_];
4746
}
48-
[client_.get() await];
47+
[client_ await];
4948
}
5049

5150
// |VariableRefreshRateReporter|
5251
double VsyncWaiterIOS::GetRefreshRate() const {
53-
return [client_.get() getRefreshRate];
54-
}
55-
56-
fml::scoped_nsobject<VSyncClient> VsyncWaiterIOS::GetVsyncClient() const {
57-
return client_;
52+
return [client_ getRefreshRate];
5853
}
5954

6055
} // namespace flutter
6156

6257
@implementation VSyncClient {
6358
flutter::VsyncWaiter::Callback callback_;
64-
fml::scoped_nsobject<CADisplayLink> display_link_;
59+
CADisplayLink* _displayLink;
6560
double current_refresh_rate_;
6661
}
6762

@@ -73,17 +68,15 @@ - (instancetype)initWithTaskRunner:(fml::RefPtr<fml::TaskRunner>)task_runner
7368
current_refresh_rate_ = [DisplayLinkManager displayRefreshRate];
7469
_allowPauseAfterVsync = YES;
7570
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;
71+
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
72+
_displayLink.paused = YES;
8073

8174
[self setMaxRefreshRate:[DisplayLinkManager displayRefreshRate]];
8275

83-
task_runner->PostTask([client = [self retain]]() {
84-
[client->display_link_.get() addToRunLoop:[NSRunLoop currentRunLoop]
85-
forMode:NSRunLoopCommonModes];
86-
[client release];
76+
// Strongly retain the the captured link until it is added to the runloop.
77+
CADisplayLink* localDisplayLink = _displayLink;
78+
task_runner->PostTask([localDisplayLink]() {
79+
[localDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
8780
});
8881
}
8982

@@ -97,19 +90,19 @@ - (void)setMaxRefreshRate:(double)refreshRate {
9790
double maxFrameRate = fmax(refreshRate, 60);
9891
double minFrameRate = fmax(maxFrameRate / 2, 60);
9992
if (@available(iOS 15.0, *)) {
100-
display_link_.get().preferredFrameRateRange =
93+
_displayLink.preferredFrameRateRange =
10194
CAFrameRateRangeMake(minFrameRate, maxFrameRate, maxFrameRate);
10295
} else {
103-
display_link_.get().preferredFramesPerSecond = maxFrameRate;
96+
_displayLink.preferredFramesPerSecond = maxFrameRate;
10497
}
10598
}
10699

107100
- (void)await {
108-
display_link_.get().paused = NO;
101+
_displayLink.paused = NO;
109102
}
110103

111104
- (void)pause {
112-
display_link_.get().paused = YES;
105+
_displayLink.paused = YES;
113106
}
114107

115108
- (void)onDisplayLink:(CADisplayLink*)link {
@@ -130,40 +123,37 @@ - (void)onDisplayLink:(CADisplayLink*)link {
130123

131124
recorder->RecordVsync(frame_start_time, frame_target_time);
132125
if (_allowPauseAfterVsync) {
133-
display_link_.get().paused = YES;
126+
link.paused = YES;
134127
}
135128
callback_(std::move(recorder));
136129
}
137130

138131
- (void)invalidate {
139-
[display_link_.get() invalidate];
132+
[_displayLink invalidate];
133+
_displayLink = nil; // Break retain cycle.
140134
}
141135

142136
- (void)dealloc {
143-
[self invalidate];
144-
145-
[super dealloc];
137+
[_displayLink invalidate];
146138
}
147139

148140
- (double)getRefreshRate {
149141
return current_refresh_rate_;
150142
}
151143

152144
- (CADisplayLink*)getDisplayLink {
153-
return display_link_.get();
145+
return _displayLink;
154146
}
155147

156148
@end
157149

158150
@implementation DisplayLinkManager
159151

160152
+ (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;
153+
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:[[[self class] alloc] init]
154+
selector:@selector(onDisplayLink:)];
155+
displayLink.paused = YES;
156+
auto preferredFPS = displayLink.preferredFramesPerSecond;
167157

168158
// From Docs:
169159
// The default value for preferredFramesPerSecond is 0. When this value is 0, the preferred

0 commit comments

Comments
 (0)