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++];
407 if (
texture.texture.width != _drawableSize.width ||
408 texture.texture.height != _drawableSize.height) {
417 [
self setNeedsDisplay];
419 [CATransaction begin];
420 [CATransaction setDisableActions:YES];
422 [CATransaction commit];
424 _displayLinkPauseCountdown = 0;
425 if (!_didSetContentsDuringThisDisplayLinkPeriod) {
426 _didSetContentsDuringThisDisplayLinkPeriod = YES;
427 }
else if (!_displayLinkForcedMaxRate) {
428 _displayLinkForcedMaxRate = YES;
429 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:YES];
434 @
synchronized(
self) {
435 if (
texture.texture.width != _drawableSize.width ||
436 texture.texture.height != _drawableSize.height) {
440 [_availableTextures addObject:_front];
444 if ([NSThread isMainThread]) {
445 [
self presentOnMainThread:texture];
448 dispatch_async(dispatch_get_main_queue(), ^{
449 [
self presentOnMainThread:texture];
459 @
synchronized(
self) {
460 if (
texture.texture.width == _drawableSize.width &&
461 texture.texture.height == _drawableSize.height) {
462 [_availableTextures addObject:texture];
468 static BOOL enabled = YES;
469 static BOOL didCheckInfoPlist = NO;
470 if (!didCheckInfoPlist) {
471 didCheckInfoPlist = YES;
472 NSNumber* use_flutter_metal_layer =
473 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
474 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