5#include <IOSurface/IOSurfaceObjC.h>
6#include <Metal/Metal.h>
7#include <UIKit/UIKit.h>
9#include "flutter/fml/logging.h"
10#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterMetalLayer.h"
60@property(readonly, nonatomic) id<MTLTexture>
texture;
61@property(readonly, nonatomic) IOSurface*
surface;
62@property(readwrite, nonatomic) CFTimeInterval presentedTime;
63@property(readwrite, atomic)
BOOL waitingForCompletion;
71@synthesize presentedTime = _presentedTime;
72@synthesize waitingForCompletion;
74- (instancetype)initWithTexture:(
id<MTLTexture>)texture surface:(IOSurface*)surface {
75 if (
self = [super init]) {
93 drawableId:(NSUInteger)drawableId;
101 drawableId:(NSUInteger)drawableId {
102 if (
self = [super init]) {
105 _drawableId = drawableId;
111 return self->_texture.texture;
114#pragma clang diagnostic push
115#pragma clang diagnostic ignored "-Wunguarded-availability-new"
116- (CAMetalLayer*)layer {
117 return (
id)
self->_layer;
119#pragma clang diagnostic pop
121- (NSUInteger)drawableID {
122 return self->_drawableId;
125- (CFTimeInterval)presentedTime {
130 [_layer presentTexture:self->_texture];
131 self->_presented = YES;
136 [_layer returnTexture:self->_texture];
140- (void)addPresentedHandler:(nonnull MTLDrawablePresentedHandler)block {
141 FML_LOG(WARNING) <<
"FlutterMetalLayer drawable does not implement addPresentedHandler:";
144- (void)presentAtTime:(CFTimeInterval)presentationTime {
145 FML_LOG(WARNING) <<
"FlutterMetalLayer drawable does not implement presentAtTime:";
148- (void)presentAfterMinimumDuration:(CFTimeInterval)duration {
149 FML_LOG(WARNING) <<
"FlutterMetalLayer drawable does not implement presentAfterMinimumDuration:";
152- (void)flutterPrepareForPresent:(nonnull
id<MTLCommandBuffer>)commandBuffer {
155 [commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
156 texture.waitingForCompletion = NO;
164@synthesize preferredDevice = _preferredDevice;
165@synthesize device = _device;
167@synthesize framebufferOnly = _framebufferOnly;
168@synthesize colorspace = _colorspace;
169@synthesize wantsExtendedDynamicRangeContent = _wantsExtendedDynamicRangeContent;
171- (instancetype)
init {
172 if (
self = [super init]) {
173 _preferredDevice = MTLCreateSystemDefaultDevice();
175 self.pixelFormat = MTLPixelFormatBGRA8Unorm;
176 _availableTextures = [[NSMutableSet alloc] init];
178 _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
179 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
180 [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
181 [[NSNotificationCenter defaultCenter] addObserver:self
182 selector:@selector(didEnterBackground:)
183 name:UIApplicationDidEnterBackgroundNotification
190 [[NSNotificationCenter defaultCenter] removeObserver:self];
193- (void)setMaxRefreshRate:(
double)refreshRate forceMax:(
BOOL)forceMax {
201 double maxFrameRate = fmax(refreshRate, 60);
202 double minFrameRate = fmax(maxFrameRate / 2, 60);
203 if (@available(iOS 15.0, *)) {
205 CAFrameRateRangeMake(forceMax ? maxFrameRate : minFrameRate, maxFrameRate, maxFrameRate);
211- (void)onDisplayLink:(CADisplayLink*)link {
212 _didSetContentsDuringThisDisplayLinkPeriod = NO;
214 if (_displayLinkPauseCountdown == 3) {
216 if (_displayLinkForcedMaxRate) {
217 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:NO];
218 _displayLinkForcedMaxRate = NO;
221 ++_displayLinkPauseCountdown;
225- (
BOOL)isKindOfClass:(Class)aClass {
226#pragma clang diagnostic push
227#pragma clang diagnostic ignored "-Wunguarded-availability-new"
229 if ([aClass isEqual:[CAMetalLayer class]]) {
232#pragma clang diagnostic pop
233 return [
super isKindOfClass:aClass];
236- (void)setDrawableSize:(CGSize)drawableSize {
237 [_availableTextures removeAllObjects];
240 _drawableSize = drawableSize;
243- (void)didEnterBackground:(
id)notification {
244 [_availableTextures removeAllObjects];
245 _totalTextures = _front != nil ? 1 : 0;
249- (CGSize)drawableSize {
250 return _drawableSize;
253- (IOSurface*)createIOSurface {
254 unsigned pixelFormat;
255 unsigned bytesPerElement;
256 if (
self.pixelFormat == MTLPixelFormatRGBA16Float) {
257 pixelFormat = kCVPixelFormatType_64RGBAHalf;
259 }
else if (
self.pixelFormat == MTLPixelFormatBGRA8Unorm) {
260 pixelFormat = kCVPixelFormatType_32BGRA;
262 }
else if (
self.pixelFormat == MTLPixelFormatBGRA10_XR) {
263 pixelFormat = kCVPixelFormatType_40ARGBLEWideGamut;
270 IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, _drawableSize.width * bytesPerElement);
272 IOSurfaceAlignProperty(kIOSurfaceAllocSize, _drawableSize.height * bytesPerRow);
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),
282 IOSurfaceRef res = IOSurfaceCreate((CFDictionaryRef)
options);
284 FML_LOG(
ERROR) <<
"Failed to create IOSurface with options "
285 <<
options.debugDescription.UTF8String;
289 if (
self.colorspace != nil) {
290 CFStringRef
name = CGColorSpaceGetName(
self.colorspace);
291 IOSurfaceSetValue(res, CFSTR(
"IOSurfaceColorSpace"),
name);
293 IOSurfaceSetValue(res, CFSTR(
"IOSurfaceColorSpace"), kCGColorSpaceSRGB);
295 return (__bridge_transfer IOSurface*)res;
299 CFTimeInterval
start = CACurrentMediaTime();
305 CFTimeInterval elapsed = CACurrentMediaTime() -
start;
307 NSLog(
@"Waited %f seconds for a drawable, giving up.", elapsed);
314 @
synchronized(
self) {
315 if (_front != nil && _front.waitingForCompletion) {
318 if (_totalTextures < 3) {
320 IOSurface*
surface = [
self createIOSurface];
324 MTLTextureDescriptor* textureDescriptor =
325 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:_pixelFormat
326 width:_drawableSize.width
327 height:_drawableSize.height
330 if (_framebufferOnly) {
331 textureDescriptor.usage = MTLTextureUsageRenderTarget;
333 textureDescriptor.usage =
334 MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite;
336 id<MTLTexture>
texture = [
self.device newTextureWithDescriptor:textureDescriptor
337 iosurface:(__bridge IOSurfaceRef)surface
341 return flutterTexture;
365 [_availableTextures removeObject:res];
372- (
id<CAMetalDrawable>)nextDrawable {
379 drawableId:_nextDrawableId++];
386 [
self setNeedsDisplay];
388 [CATransaction begin];
389 [CATransaction setDisableActions:YES];
391 [CATransaction commit];
393 _displayLinkPauseCountdown = 0;
394 if (!_didSetContentsDuringThisDisplayLinkPeriod) {
395 _didSetContentsDuringThisDisplayLinkPeriod = YES;
396 }
else if (!_displayLinkForcedMaxRate) {
397 _displayLinkForcedMaxRate = YES;
398 [
self setMaxRefreshRate:DisplayLinkManager.displayRefreshRate forceMax:YES];
403 @
synchronized(
self) {
405 [_availableTextures addObject:_front];
409 if ([NSThread isMainThread]) {
410 [
self presentOnMainThread:texture];
413 dispatch_async(dispatch_get_main_queue(), ^{
414 [
self presentOnMainThread:texture];
421 @
synchronized(
self) {
422 [_availableTextures addObject:texture];
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]) {
435 FML_LOG(WARNING) <<
"Using FlutterMetalLayer. This is an experimental feature.";
id< FlutterTexture > _texture
#define FML_LOG(severity)
BOOL maxRefreshRateEnabledOnIPhone
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
CFTimeInterval _presentedTime
id< MTLTexture > _texture
BOOL waitingForCompletion
CFTimeInterval presentedTime
CADisplayLink * _displayLink