Fix retain cycle in FlutterMetalLayer (flutter/engine#54119)

*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.*

*List which issues are fixed by this PR. You must list at least one issue.*

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Matej Knopp 2024-07-25 21:00:19 +02:00 committed by GitHub
parent b0dfbbd8ef
commit f0901b70fc
2 changed files with 49 additions and 1 deletions

View File

@ -50,6 +50,7 @@ extern CFTimeInterval display_link_target;
BOOL _displayLinkForcedMaxRate;
}
- (void)onDisplayLink:(CADisplayLink*)link;
- (void)presentTexture:(FlutterTexture*)texture;
- (void)returnTexture:(FlutterTexture*)texture;
@ -163,6 +164,26 @@ extern CFTimeInterval display_link_target;
@end
@interface FlutterMetalLayerDisplayLinkProxy : NSObject {
__weak FlutterMetalLayer* _layer;
}
@end
@implementation FlutterMetalLayerDisplayLinkProxy
- (instancetype)initWithLayer:(FlutterMetalLayer*)layer {
if (self = [super init]) {
_layer = layer;
}
return self;
}
- (void)onDisplayLink:(CADisplayLink*)link {
[_layer onDisplayLink:link];
}
@end
@implementation FlutterMetalLayer
@synthesize preferredDevice = _preferredDevice;
@ -179,7 +200,9 @@ extern CFTimeInterval display_link_target;
self.pixelFormat = MTLPixelFormatBGRA8Unorm;
_availableTextures = [[NSMutableSet alloc] init];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
FlutterMetalLayerDisplayLinkProxy* proxy =
[[FlutterMetalLayerDisplayLinkProxy alloc] initWithLayer:self];
_displayLink = [CADisplayLink displayLinkWithTarget:proxy selector:@selector(onDisplayLink:)];
[self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[[NSNotificationCenter defaultCenter] addObserver:self
@ -190,6 +213,11 @@ extern CFTimeInterval display_link_target;
return self;
}
- (void)dealloc {
[_displayLink invalidate];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setMaxRefreshRate:(double)refreshRate forceMax:(BOOL)forceMax {
// This is copied from vsync_waiter_ios.mm. The vsync waiter has display link scheduled on UI
// thread which does not trigger actual core animation frame. As a workaround FlutterMetalLayer

View File

@ -270,4 +270,24 @@
[self removeMetalLayer:layer];
}
- (void)testDealloc {
__weak FlutterMetalLayer* weakLayer;
@autoreleasepool {
FlutterMetalLayer* layer = [self addMetalLayer];
weakLayer = layer;
TestCompositor* compositor = [[TestCompositor alloc] initWithLayer:layer];
id<CAMetalDrawable> drawable = [layer nextDrawable];
BAIL_IF_NO_DRAWABLE(drawable);
[drawable present];
[compositor commitTransaction];
[self removeMetalLayer:layer];
// Deallocating the layer after removing is not synchronous.
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
}
XCTAssertNil(weakLayer);
}
@end