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]) {
107 - (id<MTLTexture>)texture {
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();
185 self.device =
self.preferredDevice;
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 [_availableTextures removeAllObjects];
257 - (void)didEnterBackground:(
id)notification {
258 [_availableTextures removeAllObjects];
259 _totalTextures = _front != nil ? 1 : 0;
264 return _drawableSize;
267 - (IOSurface*)createIOSurface {
269 unsigned bytesPerElement;
270 if (
self.
pixelFormat == MTLPixelFormatRGBA16Float) {
273 }
else if (
self.
pixelFormat == MTLPixelFormatBGRA8Unorm) {
276 }
else if (
self.
pixelFormat == MTLPixelFormatBGRA10_XR) {
277 pixelFormat = kCVPixelFormatType_40ARGBLEWideGamut;
280 NSString* errorMessage =
281 [NSString stringWithFormat:@"Unsupported pixel format: %lu", self.pixelFormat];
282 [FlutterLogger logError:errorMessage];
286 IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, _drawableSize.width * bytesPerElement);
288 IOSurfaceAlignProperty(kIOSurfaceAllocSize, _drawableSize.height * bytesPerRow);
289 NSDictionary* options = @{
290 (id)kIOSurfaceWidth : @(_drawableSize.width),
291 (id)kIOSurfaceHeight : @(_drawableSize.height),
293 (id)kIOSurfaceBytesPerElement : @(bytesPerElement),
294 (id)kIOSurfaceBytesPerRow : @(bytesPerRow),
295 (id)kIOSurfaceAllocSize : @(totalBytes),
298 IOSurfaceRef res = IOSurfaceCreate((CFDictionaryRef)options);
300 NSString* errorMessage = [NSString
301 stringWithFormat:@"Failed to create IOSurface with options %@", options.debugDescription];
302 [FlutterLogger logError:errorMessage];
307 CFStringRef name = CGColorSpaceGetName(
self.
colorspace);
308 IOSurfaceSetValue(res, kIOSurfaceColorSpace, name);
310 IOSurfaceSetValue(res, kIOSurfaceColorSpace, kCGColorSpaceSRGB);
312 return (__bridge_transfer IOSurface*)res;
316 CFTimeInterval start = CACurrentMediaTime();
319 if (texture != nil) {
322 CFTimeInterval elapsed = CACurrentMediaTime() - start;
324 NSLog(
@"Waited %f seconds for a drawable, giving up.", elapsed);
331 @
synchronized(
self) {
332 if (_front != nil && _front.waitingForCompletion) {
335 if (_totalTextures < 3) {
337 IOSurface* surface = [
self createIOSurface];
338 if (surface == nil) {
341 MTLTextureDescriptor* textureDescriptor =
342 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat
343 width:_drawableSize.width
344 height:_drawableSize.height
347 if (_framebufferOnly) {
348 textureDescriptor.usage = MTLTextureUsageRenderTarget;
350 textureDescriptor.usage =
351 MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
353 id<MTLTexture> texture = [
self.device newTextureWithDescriptor:textureDescriptor
354 iosurface:(__bridge IOSurfaceRef)surface
358 return flutterTexture;
382 [_availableTextures removeObject:res];
391 if (texture == nil) {
396 drawableId:_nextDrawableId++];
403 [
self setNeedsDisplay];
405 [CATransaction begin];
406 [CATransaction setDisableActions:YES];
407 self.contents = texture.
surface;
408 [CATransaction commit];
410 _displayLinkPauseCountdown = 0;
411 if (!_didSetContentsDuringThisDisplayLinkPeriod) {
412 _didSetContentsDuringThisDisplayLinkPeriod = YES;
413 }
else if (!_displayLinkForcedMaxRate) {
414 _displayLinkForcedMaxRate = YES;
415 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:YES];
420 @
synchronized(
self) {
422 [_availableTextures addObject:_front];
426 if ([NSThread isMainThread]) {
427 [
self presentOnMainThread:texture];
430 dispatch_async(dispatch_get_main_queue(), ^{
431 [
self presentOnMainThread:texture];
438 @
synchronized(
self) {
439 [_availableTextures addObject:texture];
445 static BOOL didCheckInfoPlist = NO;
446 if (!didCheckInfoPlist) {
447 didCheckInfoPlist = YES;
448 NSNumber* use_flutter_metal_layer =
449 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTUseFlutterMetalLayer"];
450 if (use_flutter_metal_layer != nil && ![use_flutter_metal_layer boolValue]) {
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