7#include <CoreMedia/CoreMedia.h>
8#include <IOSurface/IOSurfaceObjC.h>
9#include <Metal/Metal.h>
10#include <UIKit/UIKit.h>
12#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
54- (void)onDisplayLink:(CADisplayLink*)link;
62@property(readonly, nonatomic) id<MTLTexture>
texture;
63@property(readonly, nonatomic) IOSurface* surface;
64@property(readwrite, nonatomic) CFTimeInterval presentedTime;
65@property(readwrite, atomic)
BOOL waitingForCompletion;
71- (instancetype)initWithTexture:(
id<MTLTexture>)texture surface:(IOSurface*)surface {
72 if (
self = [super init]) {
90 drawableId:(NSUInteger)drawableId;
98 drawableId:(NSUInteger)drawableId {
99 if (
self = [super init]) {
102 _drawableId = drawableId;
108 return self->_texture.texture;
111#pragma clang diagnostic push
112#pragma clang diagnostic ignored "-Wunguarded-availability-new"
113- (CAMetalLayer*)layer {
114 return (
id)
self->_layer;
116#pragma clang diagnostic pop
118- (NSUInteger)drawableID {
119 return self->_drawableId;
122- (CFTimeInterval)presentedTime {
127 [_layer presentTexture:self->_texture];
128 self->_presented = YES;
133 [_layer returnTexture:self->_texture];
137- (void)addPresentedHandler:(nonnull MTLDrawablePresentedHandler)block {
138 [FlutterLogger logWarning:@"FlutterMetalLayer drawable does not implement addPresentedHandler:"];
141- (void)presentAtTime:(CFTimeInterval)presentationTime {
142 [FlutterLogger logWarning:@"FlutterMetalLayer drawable does not implement presentAtTime:"];
145- (void)presentAfterMinimumDuration:(CFTimeInterval)duration {
147 logWarning:@"FlutterMetalLayer drawable does not implement presentAfterMinimumDuration:"];
150- (void)flutterPrepareForPresent:(nonnull
id<MTLCommandBuffer>)commandBuffer {
153 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
154 texture.waitingForCompletion = NO;
168 if (
self = [super init]) {
174- (void)onDisplayLink:(CADisplayLink*)link {
175 [_layer onDisplayLink:link];
182- (instancetype)init {
183 if (
self = [super init]) {
184 _preferredDevice = MTLCreateSystemDefaultDevice();
186 self.pixelFormat = MTLPixelFormatBGRA8Unorm;
187 _availableTextures = [[NSMutableSet alloc] init];
191 _displayLink = [CADisplayLink displayLinkWithTarget:proxy selector:@selector(onDisplayLink:)];
192 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
193 [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
194 [[NSNotificationCenter defaultCenter] addObserver:self
195 selector:@selector(didEnterBackground:)
196 name:UIApplicationDidEnterBackgroundNotification
203 [_displayLink invalidate];
204 [[NSNotificationCenter defaultCenter] removeObserver:self];
207- (void)setMaxRefreshRate:(
double)refreshRate forceMax:(
BOOL)forceMax {
215 double maxFrameRate = fmax(refreshRate, 60);
216 double minFrameRate = fmax(maxFrameRate / 2, 60);
217 if (@available(iOS 15.0, *)) {
219 CAFrameRateRangeMake(forceMax ? maxFrameRate : minFrameRate, maxFrameRate, maxFrameRate);
225- (void)onDisplayLink:(CADisplayLink*)link {
226 _didSetContentsDuringThisDisplayLinkPeriod = NO;
228 if (_displayLinkPauseCountdown == 3) {
230 if (_displayLinkForcedMaxRate) {
231 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
232 _displayLinkForcedMaxRate = NO;
235 ++_displayLinkPauseCountdown;
239- (
BOOL)isKindOfClass:(Class)aClass {
240#pragma clang diagnostic push
241#pragma clang diagnostic ignored "-Wunguarded-availability-new"
243 if ([aClass isEqual:[CAMetalLayer class]]) {
246#pragma clang diagnostic pop
247 return [
super isKindOfClass:aClass];
250- (void)setDrawableSize:(CGSize)drawableSize {
251 @
synchronized(
self) {
252 [_availableTextures removeAllObjects];
255 _drawableSize = drawableSize;
259- (void)didEnterBackground:(
id)notification {
260 @
synchronized(
self) {
261 [_availableTextures removeAllObjects];
262 _totalTextures = _front != nil ? 1 : 0;
267- (CGSize)drawableSize {
268 @
synchronized(
self) {
269 return _drawableSize;
273- (IOSurface*)createIOSurface {
274 unsigned pixelFormat;
275 unsigned bytesPerElement;
276 if (
self.pixelFormat == MTLPixelFormatRGBA16Float) {
277 pixelFormat = kCVPixelFormatType_64RGBAHalf;
279 }
else if (
self.pixelFormat == MTLPixelFormatBGRA8Unorm) {
280 pixelFormat = kCVPixelFormatType_32BGRA;
282 }
else if (
self.pixelFormat == MTLPixelFormatBGRA10_XR) {
283 pixelFormat = kCVPixelFormatType_40ARGBLEWideGamut;
286 NSString* errorMessage =
287 [NSString stringWithFormat:@"Unsupported pixel format: %lu", self.pixelFormat];
288 [FlutterLogger logError:errorMessage];
292 IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, _drawableSize.width * bytesPerElement);
294 IOSurfaceAlignProperty(kIOSurfaceAllocSize, _drawableSize.height * bytesPerRow);
295 NSDictionary* options = @{
296 (
id)kIOSurfaceWidth : @(_drawableSize.
width),
297 (
id)kIOSurfaceHeight : @(_drawableSize.
height),
298 (
id)kIOSurfacePixelFormat : @(pixelFormat),
299 (
id)kIOSurfaceBytesPerElement : @(bytesPerElement),
300 (
id)kIOSurfaceBytesPerRow : @(bytesPerRow),
301 (
id)kIOSurfaceAllocSize : @(totalBytes),
304 IOSurfaceRef res = IOSurfaceCreate((CFDictionaryRef)options);
306 NSString* errorMessage = [NSString
307 stringWithFormat:@"Failed to create IOSurface with options %@", options.debugDescription];
308 [FlutterLogger logError:errorMessage];
312 if (
self.colorspace != nil) {
313 CFStringRef
name = CGColorSpaceGetName(
self.colorspace);
314 IOSurfaceSetValue(res, kIOSurfaceColorSpace,
name);
316 IOSurfaceSetValue(res, kIOSurfaceColorSpace, kCGColorSpaceSRGB);
318 return (__bridge_transfer IOSurface*)res;
322 CFTimeInterval
start = CACurrentMediaTime();
328 CFTimeInterval elapsed = CACurrentMediaTime() -
start;
330 NSLog(
@"Waited %f seconds for a drawable, giving up.", elapsed);
337 @
synchronized(
self) {
338 if (_front != nil && _front.waitingForCompletion) {
341 if (_totalTextures < 3) {
343 IOSurface*
surface = [
self createIOSurface];
344 if (surface == nil) {
347 MTLTextureDescriptor* textureDescriptor =
348 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat
349 width:_drawableSize.width
350 height:_drawableSize.height
353 if (_framebufferOnly) {
354 textureDescriptor.usage = MTLTextureUsageRenderTarget;
356 textureDescriptor.usage =
357 MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
359 id<MTLTexture>
texture = [
self.device newTextureWithDescriptor:textureDescriptor
360 iosurface:(__bridge IOSurfaceRef)surface
364 return flutterTexture;
388 [_availableTextures removeObject:res];
395- (
id<CAMetalDrawable>)nextDrawable {
402 drawableId:_nextDrawableId++];
409 [
self setNeedsDisplay];
411 [CATransaction begin];
412 [CATransaction setDisableActions:YES];
414 [CATransaction commit];
416 _displayLinkPauseCountdown = 0;
417 if (!_didSetContentsDuringThisDisplayLinkPeriod) {
418 _didSetContentsDuringThisDisplayLinkPeriod = YES;
419 }
else if (!_displayLinkForcedMaxRate) {
420 _displayLinkForcedMaxRate = YES;
421 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:YES];
426 @
synchronized(
self) {
427 if (
texture.texture.width != _drawableSize.width ||
428 texture.texture.height != _drawableSize.height) {
432 [_availableTextures addObject:_front];
436 if ([NSThread isMainThread]) {
437 [
self presentOnMainThread:texture];
440 dispatch_async(dispatch_get_main_queue(), ^{
441 [
self presentOnMainThread:texture];
451 @
synchronized(
self) {
452 if (
texture.texture.width == _drawableSize.width &&
453 texture.texture.height == _drawableSize.height) {
454 [_availableTextures addObject:texture];
460 static BOOL enabled = YES;
461 static BOOL didCheckInfoPlist = NO;
462 if (!didCheckInfoPlist) {
463 didCheckInfoPlist = YES;
464 NSNumber* use_flutter_metal_layer =
465 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
466 if (use_flutter_metal_layer != nil && ![use_flutter_metal_layer boolValue]) {
id< FlutterTexture > _texture
BOOL maxRefreshRateEnabledOnIPhone
Whether the max refresh rate on iPhone ProMotion devices are enabled. This reflects the value of CADi...
double displayRefreshRate
The display refresh rate used for reporting purposes. The engine does not care about this for frame s...
FlutterTexture * _texture
__weak FlutterMetalLayer * _layer
BOOL waitingForCompletion
CFTimeInterval presentedTime
CADisplayLink * _displayLink