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

Commit a134aa3

Browse files
committed
Play nice with thread merging
- return dropped drawable to pool - don't do dispatch async when presented from platform thread
1 parent ab764a4 commit a134aa3

File tree

1 file changed

+140
-88
lines changed

1 file changed

+140
-88
lines changed

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

Lines changed: 140 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ @interface DisplayLinkManager : NSObject
1313
+ (double)displayRefreshRate;
1414
@end
1515

16+
@class FlutterTexture;
1617
@class FlutterDrawable;
1718

1819
extern CFTimeInterval display_link_target;
@@ -23,10 +24,10 @@ @interface FlutterMetalLayer () {
2324

2425
NSUInteger _nextDrawableId;
2526

26-
NSMutableSet<FlutterDrawable*>* _availableDrawables;
27-
NSUInteger _totalDrawables;
27+
NSMutableSet<FlutterTexture*>* _availableTextures;
28+
NSUInteger _totalTextures;
2829

29-
FlutterDrawable* _front;
30+
FlutterTexture* _front;
3031

3132
// There must be a CADisplayLink scheduled *on main thread* otherwise
3233
// core animation only updates layers 60 times a second.
@@ -44,25 +45,103 @@ @interface FlutterMetalLayer () {
4445
BOOL _displayLinkForcedMaxRate;
4546
}
4647

47-
- (void)presentDrawable:(FlutterDrawable*)drawable;
48+
- (void)presentTexture:(FlutterTexture*)texture;
49+
- (void)returnTexture:(FlutterTexture*)texture;
4850

4951
@end
5052

51-
@interface FlutterDrawable : NSObject <CAMetalDrawable> {
53+
@interface FlutterTexture : NSObject {
5254
id<MTLTexture> _texture;
53-
__weak FlutterMetalLayer* _layer;
54-
NSUInteger _drawableId;
5555
IOSurface* _surface;
5656
CFTimeInterval _presentedTime;
5757
}
5858

59-
- (instancetype)initWithTexture:(id<MTLTexture>)texture
59+
@property(readonly, nonatomic) id<MTLTexture> texture;
60+
@property(readonly, nonatomic) IOSurface* surface;
61+
@property(readwrite, nonatomic) CFTimeInterval presentedTime;
62+
63+
@end
64+
65+
@implementation FlutterTexture
66+
67+
@synthesize texture = _texture;
68+
@synthesize surface = _surface;
69+
@synthesize presentedTime = _presentedTime;
70+
71+
- (instancetype)initWithTexture:(id<MTLTexture>)texture surface:(IOSurface*)surface {
72+
if (self = [super init]) {
73+
_texture = texture;
74+
_surface = surface;
75+
}
76+
return self;
77+
}
78+
79+
@end
80+
81+
@interface FlutterDrawable : NSObject <CAMetalDrawable> {
82+
FlutterTexture* _texture;
83+
__weak FlutterMetalLayer* _layer;
84+
NSUInteger _drawableId;
85+
BOOL _presented;
86+
}
87+
88+
- (instancetype)initWithTexture:(FlutterTexture*)texture
6089
layer:(FlutterMetalLayer*)layer
61-
drawableId:(NSUInteger)drawableId
62-
surface:(IOSurface*)surface;
90+
drawableId:(NSUInteger)drawableId;
6391

64-
@property(readonly) IOSurface* surface;
65-
@property(readwrite, nonatomic) CFTimeInterval presentedTime;
92+
@end
93+
94+
@implementation FlutterDrawable
95+
96+
- (instancetype)initWithTexture:(FlutterTexture*)texture
97+
layer:(FlutterMetalLayer*)layer
98+
drawableId:(NSUInteger)drawableId {
99+
if (self = [super init]) {
100+
_texture = texture;
101+
_layer = layer;
102+
_drawableId = drawableId;
103+
}
104+
return self;
105+
}
106+
107+
- (id<MTLTexture>)texture {
108+
return self->_texture.texture;
109+
}
110+
111+
#pragma clang diagnostic push
112+
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
113+
- (CAMetalLayer*)layer {
114+
return (id)self->_layer;
115+
}
116+
#pragma clang diagnostic pop
117+
118+
- (NSUInteger)drawableID {
119+
return self->_drawableId;
120+
}
121+
122+
- (CFTimeInterval)presentedTime {
123+
return 0;
124+
}
125+
126+
- (void)present {
127+
[_layer presentTexture:self->_texture];
128+
self->_presented = YES;
129+
}
130+
131+
- (void)dealloc {
132+
if (!_presented) {
133+
[_layer returnTexture:self->_texture];
134+
}
135+
}
136+
137+
- (void)addPresentedHandler:(nonnull MTLDrawablePresentedHandler)block {
138+
}
139+
140+
- (void)presentAtTime:(CFTimeInterval)presentationTime {
141+
}
142+
143+
- (void)presentAfterMinimumDuration:(CFTimeInterval)duration {
144+
}
66145

67146
@end
68147

@@ -79,7 +158,8 @@ - (instancetype)init {
79158
if (self = [super init]) {
80159
_preferredDevice = MTLCreateSystemDefaultDevice();
81160
self.device = self.preferredDevice;
82-
_availableDrawables = [[NSMutableSet alloc] init];
161+
self.pixelFormat = MTLPixelFormatBGRA8Unorm;
162+
_availableTextures = [[NSMutableSet alloc] init];
83163

84164
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
85165
[self setMaxRefreshRate:[DisplayLinkManager displayRefreshRate] forceMax:NO];
@@ -136,15 +216,15 @@ - (BOOL)isKindOfClass:(Class)aClass {
136216
}
137217

138218
- (void)setDrawableSize:(CGSize)drawableSize {
139-
[_availableDrawables removeAllObjects];
219+
[_availableTextures removeAllObjects];
140220
_front = nil;
141-
_totalDrawables = 0;
221+
_totalTextures = 0;
142222
_drawableSize = drawableSize;
143223
}
144224

145225
- (void)didEnterBackground:(id)notification {
146-
[_availableDrawables removeAllObjects];
147-
_totalDrawables = _front != nil ? 1 : 0;
226+
[_availableTextures removeAllObjects];
227+
_totalTextures = _front != nil ? 1 : 0;
148228
_displayLink.paused = YES;
149229
}
150230

@@ -185,15 +265,15 @@ - (IOSurface*)createIOSurface {
185265
return (__bridge_transfer IOSurface*)res;
186266
}
187267

188-
- (id<CAMetalDrawable>)nextDrawable {
268+
- (FlutterTexture*)nextTexture {
189269
@synchronized(self) {
190270
// IOSurface.isInUse to determine when compositor is done with the surface.
191271
// That a rather blunt instrument and we are probably waiting longer than
192272
// we really need to. With triple buffering at 120Hz that results in about
193273
// 2-3 milliseconds wait time at beginning of display link callback.
194274
// With four buffers this number gets close to zero.
195-
if (_totalDrawables < 3) {
196-
++_totalDrawables;
275+
if (_totalTextures < 3) {
276+
++_totalTextures;
197277
IOSurface* surface = [self createIOSurface];
198278
MTLTextureDescriptor* textureDescriptor =
199279
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat
@@ -205,20 +285,18 @@ - (IOSurface*)createIOSurface {
205285
id<MTLTexture> texture = [self.device newTextureWithDescriptor:textureDescriptor
206286
iosurface:(__bridge IOSurfaceRef)surface
207287
plane:0];
208-
FlutterDrawable* drawable = [[FlutterDrawable alloc] initWithTexture:texture
209-
layer:self
210-
drawableId:_nextDrawableId++
211-
surface:surface];
212-
return drawable;
288+
FlutterTexture* flutterTexture = [[FlutterTexture alloc] initWithTexture:texture
289+
surface:surface];
290+
return flutterTexture;
213291
} else {
214292
// Make sure raster thread doesn't have too many drawables in flight.
215-
if (_availableDrawables.count == 0) {
293+
if (_availableTextures.count == 0) {
216294
CFTimeInterval start = CACurrentMediaTime();
217-
while (_availableDrawables.count == 0 && CACurrentMediaTime() - start < 0.1) {
295+
while (_availableTextures.count == 0 && CACurrentMediaTime() - start < 0.1) {
218296
usleep(100);
219297
}
220298
CFTimeInterval elapsed = CACurrentMediaTime() - start;
221-
if (_availableDrawables.count == 0) {
299+
if (_availableTextures.count == 0) {
222300
NSLog(@"Waited %f seconds for a drawable, giving up.", elapsed);
223301
return nil;
224302
} else {
@@ -227,31 +305,42 @@ - (IOSurface*)createIOSurface {
227305
}
228306
// Return first drawable that is not in use or the one that was presented
229307
// the longest time ago.
230-
FlutterDrawable* res = nil;
231-
for (FlutterDrawable* drawable in _availableDrawables) {
232-
if (!drawable.surface.isInUse) {
233-
res = drawable;
308+
FlutterTexture* res = nil;
309+
for (FlutterTexture* texture in _availableTextures) {
310+
if (!texture.surface.isInUse) {
311+
res = texture;
234312
break;
235313
}
236-
if (res == nil || drawable.presentedTime < res.presentedTime) {
237-
res = drawable;
314+
if (res == nil || texture.presentedTime < res.presentedTime) {
315+
res = texture;
238316
}
239317
}
240-
[_availableDrawables removeObject:res];
318+
[_availableTextures removeObject:res];
241319
return res;
242320
}
243321
}
244322
}
245323

246-
- (void)presentOnMainThread:(FlutterDrawable*)drawable {
324+
- (id<CAMetalDrawable>)nextDrawable {
325+
FlutterTexture* texture = [self nextTexture];
326+
if (texture == nil) {
327+
return nil;
328+
}
329+
FlutterDrawable* drawable = [[FlutterDrawable alloc] initWithTexture:texture
330+
layer:self
331+
drawableId:_nextDrawableId++];
332+
return drawable;
333+
}
334+
335+
- (void)presentOnMainThread:(FlutterTexture*)texture {
247336
// This is needed otherwise frame gets skipped on touch begin / end. Go figure.
248337
// Might also be placebo
249338
[self setNeedsDisplay];
250339

251340
[CATransaction begin];
252341
[CATransaction setDisableActions:YES];
253-
self.contents = drawable.surface;
254-
drawable.presentedTime = CACurrentMediaTime();
342+
self.contents = texture.surface;
343+
texture.presentedTime = CACurrentMediaTime();
255344
[CATransaction commit];
256345
_displayLink.paused = NO;
257346
_displayLinkPauseCountdown = 0;
@@ -263,63 +352,26 @@ - (void)presentOnMainThread:(FlutterDrawable*)drawable {
263352
}
264353
}
265354

266-
- (void)presentDrawable:(FlutterDrawable*)drawable {
355+
- (void)presentTexture:(FlutterTexture*)texture {
267356
@synchronized(self) {
268357
if (_front != nil) {
269-
[_availableDrawables addObject:_front];
358+
[_availableTextures addObject:_front];
359+
}
360+
_front = texture;
361+
if ([NSThread isMainThread]) {
362+
[self presentOnMainThread:texture];
363+
} else {
364+
dispatch_async(dispatch_get_main_queue(), ^{
365+
[self presentOnMainThread:texture];
366+
});
270367
}
271-
_front = drawable;
272-
dispatch_async(dispatch_get_main_queue(), ^{
273-
[self presentOnMainThread:drawable];
274-
});
275368
}
276369
}
277370

278-
@end
279-
280-
@implementation FlutterDrawable
281-
282-
@synthesize presentedTime = _presentedTime;
283-
284-
- (instancetype)initWithTexture:(id<MTLTexture>)texture
285-
layer:(FlutterMetalLayer*)layer
286-
drawableId:(NSUInteger)drawableId
287-
surface:(IOSurface*)surface {
288-
if (self = [super init]) {
289-
_texture = texture;
290-
_layer = layer;
291-
_drawableId = drawableId;
292-
_surface = surface;
371+
- (void)returnTexture:(FlutterTexture*)texture {
372+
@synchronized(self) {
373+
[_availableTextures addObject:texture];
293374
}
294-
return self;
295-
}
296-
297-
- (id<MTLTexture>)texture {
298-
return self->_texture;
299-
}
300-
301-
#pragma clang diagnostic push
302-
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
303-
- (CAMetalLayer*)layer {
304-
return (id)self->_layer;
305-
}
306-
#pragma clang diagnostic pop
307-
308-
- (NSUInteger)drawableID {
309-
return self->_drawableId;
310-
}
311-
312-
- (void)present {
313-
[_layer presentDrawable:self];
314-
}
315-
316-
- (void)addPresentedHandler:(nonnull MTLDrawablePresentedHandler)block {
317-
}
318-
319-
- (void)presentAtTime:(CFTimeInterval)presentationTime {
320-
}
321-
322-
- (void)presentAfterMinimumDuration:(CFTimeInterval)duration {
323375
}
324376

325377
@end

0 commit comments

Comments
 (0)