Flutter Engine
The Flutter Engine
Instance Methods | Class Methods | Protected Attributes | Properties | List of all members
FlutterMetalLayer Class Reference

#import <FlutterMetalLayer.h>

Inheritance diagram for FlutterMetalLayer:

Instance Methods

(nullable id< CAMetalDrawable >) - nextDrawable
 
(instancetype) - init [implementation]
 
(void) - setMaxRefreshRate:forceMax: [implementation]
 
(void) - onDisplayLink: [implementation]
 
(BOOL- isKindOfClass: [implementation]
 
(void) - setDrawableSize: [implementation]
 
(void) - didEnterBackground: [implementation]
 
(IOSurface *) - createIOSurface [implementation]
 
(FlutterTexture *) - nextTexture [implementation]
 
(FlutterTexture *) - tryNextTexture [implementation]
 
(void) - presentOnMainThread: [implementation]
 
(void) - presentTexture: [implementation]
 
(void) - returnTexture: [implementation]
 

Class Methods

(BOOL+ enabled
 

Protected Attributes

id< MTLDevice > _preferredDevice
 
CGSize _drawableSize
 
NSUInteger _nextDrawableId
 
NSMutableSet< FlutterTexture * > * _availableTextures
 
NSUInteger _totalTextures
 
FlutterTexture_front
 
CADisplayLink * _displayLink
 
NSUInteger _displayLinkPauseCountdown
 
BOOL _didSetContentsDuringThisDisplayLinkPeriod
 
BOOL _displayLinkForcedMaxRate
 

Properties

id< MTLDevice > device
 
id< MTLDevice > preferredDevice
 
MTLPixelFormat pixelFormat
 
BOOL framebufferOnly
 
CGSize drawableSize
 
BOOL presentsWithTransaction
 
CGColorSpaceRef colorspace
 
BOOL wantsExtendedDynamicRangeContent
 

Detailed Description

Drop-in replacement (as far as Flutter is concerned) for CAMetalLayer that can present with transaction from a background thread.

Definition at line 12 of file FlutterMetalLayer.h.

Method Documentation

◆ createIOSurface

- (IOSurface *) createIOSurface
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

253 {
254 unsigned pixelFormat;
255 unsigned bytesPerElement;
256 if (self.pixelFormat == MTLPixelFormatRGBA16Float) {
257 pixelFormat = kCVPixelFormatType_64RGBAHalf;
258 bytesPerElement = 8;
259 } else if (self.pixelFormat == MTLPixelFormatBGRA8Unorm) {
260 pixelFormat = kCVPixelFormatType_32BGRA;
261 bytesPerElement = 4;
262 } else if (self.pixelFormat == MTLPixelFormatBGRA10_XR) {
263 pixelFormat = kCVPixelFormatType_40ARGBLEWideGamut;
264 bytesPerElement = 8;
265 } else {
266 FML_LOG(ERROR) << "Unsupported pixel format: " << self.pixelFormat;
267 return nil;
268 }
269 size_t bytesPerRow =
270 IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, _drawableSize.width * bytesPerElement);
271 size_t totalBytes =
272 IOSurfaceAlignProperty(kIOSurfaceAllocSize, _drawableSize.height * bytesPerRow);
273 NSDictionary* options = @{
274 (id)kIOSurfaceWidth : @(_drawableSize.width),
275 (id)kIOSurfaceHeight : @(_drawableSize.height),
276 (id)kIOSurfacePixelFormat : @(pixelFormat),
277 (id)kIOSurfaceBytesPerElement : @(bytesPerElement),
278 (id)kIOSurfaceBytesPerRow : @(bytesPerRow),
279 (id)kIOSurfaceAllocSize : @(totalBytes),
280 };
281
282 IOSurfaceRef res = IOSurfaceCreate((CFDictionaryRef)options);
283 if (res == nil) {
284 FML_LOG(ERROR) << "Failed to create IOSurface with options "
285 << options.debugDescription.UTF8String;
286 return nil;
287 }
288
289 if (self.colorspace != nil) {
290 CFStringRef name = CGColorSpaceGetName(self.colorspace);
291 IOSurfaceSetValue(res, CFSTR("IOSurfaceColorSpace"), name);
292 } else {
293 IOSurfaceSetValue(res, CFSTR("IOSurfaceColorSpace"), kCGColorSpaceSRGB);
294 }
295 return (__bridge_transfer IOSurface*)res;
296}
const char * options
#define FML_LOG(severity)
Definition: logging.h:82
CGColorSpaceRef colorspace
MTLPixelFormat pixelFormat
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
const uintptr_t id
#define ERROR(message)
Definition: elf_loader.cc:260

◆ didEnterBackground:

- (void) didEnterBackground: (id notification
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

243 :(id)notification {
244 [_availableTextures removeAllObjects];
245 _totalTextures = _front != nil ? 1 : 0;
246 _displayLink.paused = YES;
247}
CADisplayLink * _displayLink
FlutterTexture * _front

◆ enabled

+ (BOOL) enabled

Returns whether the Metal layer is enabled. This is controlled by FLTUseFlutterMetalLayer value in Info.plist.

Definition at line 92 of file FlutterMetalLayer.mm.

426 {
427 static BOOL enabled = NO;
428 static BOOL didCheckInfoPlist = NO;
429 if (!didCheckInfoPlist) {
430 didCheckInfoPlist = YES;
431 NSNumber* use_flutter_metal_layer =
432 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
433 if (use_flutter_metal_layer != nil && [use_flutter_metal_layer boolValue]) {
434 enabled = YES;
435 FML_LOG(WARNING) << "Using FlutterMetalLayer. This is an experimental feature.";
436 }
437 }
438 return enabled;
439}
int BOOL
Definition: windows_types.h:37

◆ init

- (instancetype) init
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

175 {
176 if (self = [super init]) {
177 _preferredDevice = MTLCreateSystemDefaultDevice();
178 self.device = self.preferredDevice;
179 self.pixelFormat = MTLPixelFormatBGRA8Unorm;
180 _availableTextures = [[NSMutableSet alloc] init];
181
182 _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
183 [self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
184 [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
185 [[NSNotificationCenter defaultCenter] addObserver:self
186 selector:@selector(didEnterBackground:)
187 name:UIApplicationDidEnterBackgroundNotification
188 object:nil];
189 }
190 return self;
191}
id< MTLDevice > _preferredDevice
NSMutableSet< FlutterTexture * > * _availableTextures

◆ isKindOfClass:

- (BOOL) isKindOfClass: (Class)  aClass
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

225 :(Class)aClass {
226#pragma clang diagnostic push
227#pragma clang diagnostic ignored "-Wunguarded-availability-new"
228 // Pretend that we're a CAMetalLayer so that the rest of Flutter plays along
229 if ([aClass isEqual:[CAMetalLayer class]]) {
230 return YES;
231 }
232#pragma clang diagnostic pop
233 return [super isKindOfClass:aClass];
234}

◆ nextDrawable

- (id< CAMetalDrawable >) nextDrawable

Definition at line 92 of file FlutterMetalLayer.mm.

372 {
373 FlutterTexture* texture = [self nextTexture];
374 if (texture == nil) {
375 return nil;
376 }
377 FlutterDrawable* drawable = [[FlutterDrawable alloc] initWithTexture:texture
378 layer:self
379 drawableId:_nextDrawableId++];
380 return drawable;
381}
FlTexture * texture

◆ nextTexture

- (FlutterTexture *) nextTexture
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

298 {
299 CFTimeInterval start = CACurrentMediaTime();
300 while (true) {
301 FlutterTexture* texture = [self tryNextTexture];
302 if (texture != nil) {
303 return texture;
304 }
305 CFTimeInterval elapsed = CACurrentMediaTime() - start;
306 if (elapsed > 1.0) {
307 NSLog(@"Waited %f seconds for a drawable, giving up.", elapsed);
308 return nil;
309 }
310 }
311}

◆ onDisplayLink:

- (void) onDisplayLink: (CADisplayLink*)  link
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

211 :(CADisplayLink*)link {
213 // Do not pause immediately, this seems to prevent 120hz while touching.
215 _displayLink.paused = YES;
217 [self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
219 }
220 } else {
222 }
223}
BOOL _didSetContentsDuringThisDisplayLinkPeriod
NSUInteger _displayLinkPauseCountdown
def link(from_root, to_root)
Definition: dart_pkg.py:44

◆ presentOnMainThread:

- (void) presentOnMainThread: (FlutterTexture*)  texture
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

384 // This is needed otherwise frame gets skipped on touch begin / end. Go figure.
385 // Might also be placebo
386 [self setNeedsDisplay];
387
388 [CATransaction begin];
389 [CATransaction setDisableActions:YES];
390 self.contents = texture.surface;
391 [CATransaction commit];
392 _displayLink.paused = NO;
396 } else if (!_displayLinkForcedMaxRate) {
398 [self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:YES];
399 }
400}

◆ presentTexture:

- (void) presentTexture: (FlutterTexture*)  texture
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

403 @synchronized(self) {
404 if (_front != nil) {
405 [_availableTextures addObject:_front];
406 }
407 _front = texture;
408 texture.presentedTime = CACurrentMediaTime();
409 if ([NSThread isMainThread]) {
410 [self presentOnMainThread:texture];
411 } else {
412 // Core animation layers can only be updated on main thread.
413 dispatch_async(dispatch_get_main_queue(), ^{
414 [self presentOnMainThread:texture];
415 });
416 }
417 }
418}

◆ returnTexture:

- (void) returnTexture: (FlutterTexture*)  texture
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

421 @synchronized(self) {
422 [_availableTextures addObject:texture];
423 }
424}

◆ setDrawableSize:

- (void) setDrawableSize: (CGSize)  drawableSize
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

236 :(CGSize)drawableSize {
237 [_availableTextures removeAllObjects];
238 _front = nil;
239 _totalTextures = 0;
241}

◆ setMaxRefreshRate:forceMax:

- (void) setMaxRefreshRate: (double)  refreshRate
forceMax: (BOOL forceMax 
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

193 :(double)refreshRate forceMax:(BOOL)forceMax {
194 // This is copied from vsync_waiter_ios.mm. The vsync waiter has display link scheduled on UI
195 // thread which does not trigger actual core animation frame. As a workaround FlutterMetalLayer
196 // has it's own displaylink scheduled on main thread, which is used to trigger core animation
197 // frame allowing for 120hz updates.
199 return;
200 }
201 double maxFrameRate = fmax(refreshRate, 60);
202 double minFrameRate = fmax(maxFrameRate / 2, 60);
203 if (@available(iOS 15.0, *)) {
204 _displayLink.preferredFrameRateRange =
205 CAFrameRateRangeMake(forceMax ? maxFrameRate : minFrameRate, maxFrameRate, maxFrameRate);
206 } else {
207 _displayLink.preferredFramesPerSecond = maxFrameRate;
208 }
209}

◆ tryNextTexture

- (FlutterTexture *) tryNextTexture
implementation

Definition at line 92 of file FlutterMetalLayer.mm.

313 {
314 @synchronized(self) {
315 if (_front != nil && _front.waitingForCompletion) {
316 return nil;
317 }
318 if (_totalTextures < 3) {
320 IOSurface* surface = [self createIOSurface];
321 if (surface == nil) {
322 return nil;
323 }
324 MTLTextureDescriptor* textureDescriptor =
325 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat
326 width:_drawableSize.width
327 height:_drawableSize.height
328 mipmapped:NO];
329
330 if (_framebufferOnly) {
331 textureDescriptor.usage = MTLTextureUsageRenderTarget;
332 } else {
333 textureDescriptor.usage =
334 MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
335 }
336 id<MTLTexture> texture = [self.device newTextureWithDescriptor:textureDescriptor
337 iosurface:(__bridge IOSurfaceRef)surface
338 plane:0];
339 FlutterTexture* flutterTexture = [[FlutterTexture alloc] initWithTexture:texture
340 surface:surface];
341 return flutterTexture;
342 } else {
343 // Prefer surface that is not in use and has been presented the longest
344 // time ago.
345 // When isInUse is false, the surface is definitely not used by the compositor.
346 // When isInUse is true, the surface may be used by the compositor.
347 // When both surfaces are in use, the one presented earlier will be returned.
348 // The assumption here is that the compositor is already aware of the
349 // newer texture and is unlikely to read from the older one, even though it
350 // has not decreased the use count yet (there seems to be certain latency).
351 FlutterTexture* res = nil;
353 if (res == nil) {
354 res = texture;
355 } else if (res.surface.isInUse && !texture.surface.isInUse) {
356 // prefer texture that is not in use.
357 res = texture;
358 } else if (res.surface.isInUse == texture.surface.isInUse &&
359 texture.presentedTime < res.presentedTime) {
360 // prefer texture with older presented time.
361 res = texture;
362 }
363 }
364 if (res != nil) {
365 [_availableTextures removeObject:res];
366 }
367 return res;
368 }
369 }
370}
VkSurfaceKHR surface
Definition: main.cc:49
IOSurface * surface
CFTimeInterval presentedTime

Member Data Documentation

◆ _availableTextures

- (NSMutableSet<FlutterTexture*>*) _availableTextures
protected

Definition at line 32 of file FlutterMetalLayer.mm.

◆ _didSetContentsDuringThisDisplayLinkPeriod

- (BOOL) _didSetContentsDuringThisDisplayLinkPeriod
protected

Definition at line 47 of file FlutterMetalLayer.mm.

◆ _displayLink

- (CADisplayLink*) _displayLink
protected

Definition at line 39 of file FlutterMetalLayer.mm.

◆ _displayLinkForcedMaxRate

- (BOOL) _displayLinkForcedMaxRate
protected

Definition at line 50 of file FlutterMetalLayer.mm.

◆ _displayLinkPauseCountdown

- (NSUInteger) _displayLinkPauseCountdown
protected

Definition at line 40 of file FlutterMetalLayer.mm.

◆ _drawableSize

- (CGSize) _drawableSize
protected

Definition at line 28 of file FlutterMetalLayer.mm.

◆ _front

- (FlutterTexture*) _front
protected

Definition at line 35 of file FlutterMetalLayer.mm.

◆ _nextDrawableId

- (NSUInteger) _nextDrawableId
protected

Definition at line 30 of file FlutterMetalLayer.mm.

◆ _preferredDevice

- (id<MTLDevice>) _preferredDevice
protected

Definition at line 27 of file FlutterMetalLayer.mm.

◆ _totalTextures

- (NSUInteger) _totalTextures
protected

Definition at line 33 of file FlutterMetalLayer.mm.

Property Documentation

◆ colorspace

- (CGColorSpaceRef) colorspace
readwriteatomicassign

Definition at line 20 of file FlutterMetalLayer.h.

◆ device

- (id<MTLDevice>) device
readwriteatomicretain

Definition at line 14 of file FlutterMetalLayer.h.

◆ drawableSize

- (CGSize) drawableSize
readwriteatomic

Definition at line 18 of file FlutterMetalLayer.h.

◆ framebufferOnly

- (BOOL) framebufferOnly
readwriteatomic

Definition at line 17 of file FlutterMetalLayer.h.

◆ pixelFormat

- (MTLPixelFormat) pixelFormat
readwriteatomic

Definition at line 16 of file FlutterMetalLayer.h.

◆ preferredDevice

- (id<MTLDevice>) preferredDevice
readatomicassign

Definition at line 15 of file FlutterMetalLayer.h.

◆ presentsWithTransaction

- (BOOL) presentsWithTransaction
readwriteatomic

Definition at line 19 of file FlutterMetalLayer.h.

◆ wantsExtendedDynamicRangeContent

- (BOOL) wantsExtendedDynamicRangeContent
readwriteatomic

Definition at line 21 of file FlutterMetalLayer.h.


The documentation for this class was generated from the following files: